奇怪的;jsessionid
背景
由于最近在整理session&cookie的一些原理https://my.oschina.net/qixiaobo025/blog?search=session
我们可以看到在cookie没有开启的情况下 servlet会通过传输sessionid来完成用户的标识~
我们来考虑这个问题,服务端如何来判断客户端是否开启cookie了呢???
问题
对于cookie是否禁用 在客户端的判断比较容易 但是在服务端来说基本不太现实。
我们可以判断cookie被启用 但是比较难以判断cookie是被禁用的。毕竟当客户端没有传入cookie
也有可能是客户端第一次访问而已。那么如何判断是否是cookie被禁用呢?
一个简单能思考到的方案如下:
客户端访问指定url
服务端设置cookie 设置重定向
客户端重定向到服务端
服务端检查客户端有没有携带cookie 携带cookie则认为cookie启用否则认为cookie被禁用
考虑下列场景
请求url上增加了;jsessionid的【注意不是QueryString而是url进行了重写】
可是carOwner项目都是移动端客户 莫非移动端客户都禁止了cookie???
分析
当cookie被禁用了之后 那么为了记住用户的身份 必然需要在客户端向服务端进行请求的时候传递一些必要的信息
在cookie启用的场景下需要传递sessionId那么在cookie禁用的场景下呢?自然是通过传输sessionId
servlet在做一些response的时候帮我们做了一些事情 当我们调用response
response.encodeRedirectURL(targetUrl)
/** * Encode the session identifier associated with this response * into the specified redirect URL, if necessary. * * @param url URL to be encoded */@Overridepublic String encodeRedirectURL(String url) { if (isEncodeable(toAbsolute(url))) { return (toEncoded(url, request.getSessionInternal().getIdInternal())); } else { return (url); } }
从这段注释可以看出 当使用上述api时会自动“判断cookie是否被禁用”
似乎很神奇 按照我们之前的描述 想要判断cookie是否被禁用至少要进行一次重定向 那servlet是怎么判断的呢???
/** * Return <code>true</code> if the specified URL should be encoded with * a session identifier. This will be true if all of the following * conditions are met: * <ul> * <li>The request we are responding to asked for a valid session * <li>The requested session ID was not received via a cookie * <li>The specified URL points back to somewhere within the web * application that is responding to this request * </ul> * * @param location Absolute URL to be validated */protected boolean isEncodeable(final String location) { if (location == null) { return (false); } // Is this an intra-document reference? if (location.startsWith("#")) { return (false); } // Are we in a valid session that is not using cookies? final Request hreq = request; final Session session = hreq.getSessionInternal(false); if (session == null) { return (false); } if (hreq.isRequestedSessionIdFromCookie()) { return (false); } // Is URL encoding permitted if (!hreq.getServletContext().getEffectiveSessionTrackingModes(). contains(SessionTrackingMode.URL)) { return false; } if (SecurityUtil.isPackageProtectionEnabled()) { return ( AccessController.doPrivileged(new PrivilegedAction<Boolean>() { @Override public Boolean run(){ return Boolean.valueOf(doIsEncodeable(hreq, session, location)); } })).booleanValue(); } else { return doIsEncodeable(hreq, session, location); } } private boolean doIsEncodeable(Request hreq, Session session, String location) { // Is this a valid absolute URL? URL url = null; try { url = new URL(location); } catch (MalformedURLException e) { return (false); } // Does this URL match down to (and including) the context path? if (!hreq.getScheme().equalsIgnoreCase(url.getProtocol())) { return (false); } if (!hreq.getServerName().equalsIgnoreCase(url.getHost())) { return (false); } int serverPort = hreq.getServerPort(); if (serverPort == -1) { if ("https".equals(hreq.getScheme())) { serverPort = 443; } else { serverPort = 80; } } int urlPort = url.getPort(); if (urlPort == -1) { if ("https".equals(url.getProtocol())) { urlPort = 443; } else { urlPort = 80; } } if (serverPort != urlPort) { return (false); } String contextPath = getContext().getPath(); if (contextPath != null) { String file = url.getFile(); if (!file.startsWith(contextPath)) { return (false); } String tok = ";" + SessionConfig.getSessionUriParamName(request.getContext()) + "=" + session.getIdInternal(); if( file.indexOf(tok, contextPath.length()) >= 0 ) { return (false); } } // This URL belongs to our web application, so it is encodeable return (true); }
从这段代码来看当且仅当 当前请求中并没有从cookie中获取到session 【另外就是允许在url中跟踪用户===》目前tomcat均是默认开启】
并且当前重定向url必须和当前port contextPath相同的场景下将会在url上添加上sessionId【需要额外处理url中包含了sessionId的场景】
从这种情况下来看其实并没有判断服务端cookie是否被禁用【当然cookie被禁用了之后也能走到这段逻辑,自然也是OK的】
复盘
判断我们车主端使用跳转来指定页面 通常需要微信用户授权后跳转到首页
/** * 微信授权回调接口 */@RequestMapping(value = "/wxRedirect")public String wxRedirect(HttpServletRequest request, String code, String state) throws Exception { String[] params = state.split("\\|"); if (!TextUtils.isEmpty(code) && params.length == 2) { PappStation pappStation = wxStationService.getPappStationbyWxAppId(params[0]); if (pappStation != null) { OauthAPI oauthAPI = new OauthAPI(wxConfigService.getConfig(pappStation)); OauthGetTokenResponse response = oauthAPI.getToken(code); if (response.getErrcode() == null || ResultType.SUCCESS.getCode().toString().equals(response.getErrcode())) { SessionUtil.saveToSession(request, SessionUtil.KEY_APP, pappStation); GetUserInfoResponse userinfo = oauthAPI.getUserInfo(response.getAccessToken(), response.getOpenid()); //保存或更新用户信息 wxUserService.createOrUpdateUser(createUserObject(userinfo, pappStation.getWxAppId())); Puser puser = wxUserService.getUserInfoByOpenId(response.getOpenid()); SessionUtil.saveToSession(request, SessionUtil.KEY_USER, puser); //重定向到用户实际访问的页面 return "redirect:" + mConstant.getClientUrl() + "?appid=" + params[0] + "&openid=" + response.getOpenid() + "#" + params[1]; } else { J2Cache.getChannel().evict("apiConfig", pappStation.getWxAppId()); logger.warn("clear cache for {}", pappStation.getWxAppId()); } } } return "redirect:" + mConstant.getClientErrorPage(); }
其实springmvc在使用redirect的时候其实走到了RedirectView
/** * Convert model to request parameters and redirect to the given URL. * @see #appendQueryProperties * @see #sendRedirect */@Overrideprotected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws IOException { String targetUrl = createTargetUrl(model, request); targetUrl = updateTargetUrl(targetUrl, model, request, response); FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request); if (!CollectionUtils.isEmpty(flashMap)) { UriComponents uriComponents = UriComponentsBuilder.fromUriString(targetUrl).build(); flashMap.setTargetRequestPath(uriComponents.getPath()); flashMap.addTargetRequestParams(uriComponents.getQueryParams()); FlashMapManager flashMapManager = RequestContextUtils.getFlashMapManager(request); if (flashMapManager == null) { throw new IllegalStateException("FlashMapManager not found despite output FlashMap having been set"); } flashMapManager.saveOutputFlashMap(flashMap, request, response); } sendRedirect(request, response, targetUrl, this.http10Compatible); } /** * Send a redirect back to the HTTP client * @param request current HTTP request (allows for reacting to request method) * @param response current HTTP response (for sending response headers) * @param targetUrl the target URL to redirect to * @param http10Compatible whether to stay compatible with HTTP 1.0 clients * @throws IOException if thrown by response methods */protected void sendRedirect(HttpServletRequest request, HttpServletResponse response, String targetUrl, boolean http10Compatible) throws IOException { String encodedRedirectURL = response.encodeRedirectURL(targetUrl); if (http10Compatible) { if (this.statusCode != null) { response.setStatus(this.statusCode.value()); response.setHeader("Location", encodedRedirectURL); } else { // Send status code 302 by default. response.sendRedirect(encodedRedirectURL); } } else { HttpStatus statusCode = getHttp11StatusCode(request, response, targetUrl); response.setStatus(statusCode.value()); response.setHeader("Location", encodedRedirectURL); } }
很明显这边调用了
response.encodeRedirectURL(targetUrl)
而根据我们上面描述就是 当该请求并没有cookie中获取到session中时 一旦调用encodeRedirectURL 那么在默认场景下将会使得url被重写
当然除了该接口encodeRedirectUrl之外还有
/** * Encode the session identifier associated with this response * into the specified URL, if necessary. * * @param url URL to be encoded */@Overridepublic String encodeURL(String url) { String absolute; try { absolute = toAbsolute(url); } catch (IllegalArgumentException iae) { // Relative URL return url; } if (isEncodeable(absolute)) { // W3c spec clearly said if (url.equalsIgnoreCase("")) { url = absolute; } else if (url.equals(absolute) && !hasPath(url)) { url += '/'; } return (toEncoded(url, request.getSessionInternal().getIdInternal())); } else { return (url); } }
作者:Mr_Qi
来源:https://my.oschina.net/qixiaobo025/blog/1794325
共同學習,寫下你的評論
評論加載中...
作者其他優質文章