博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

[麦芽项目 day three] 三、ajax请求不携带session cookie信息

郁闷的一天,昨天还以为简单引入spring session就万事大吉了,然而事情并没有那么简单。原来<img/>标签内的http请求是会自动带上浏览器中的cookies,从而达到验证用户的目的,但是!!!ajax请求却死活带不上cookie。。。😭

网上一查一大片[跨域ajax请求cookie问题],设置这样,然后配套在服务器端设置response巴拉巴拉之类的,并没有什么软用(微笑脸)

xhrFields: {
withCredentials: true
},
crossDomain: true

终于在漫天的类似文章博客中,找到一个重点:前端js代码没有权限拿到httponly=true的cookie!开心,似乎找到了问题的根源!!
因为spring session默认创建的 session cookie 就是httponly 的!!就开始分析spring session data redis 的具体实现原理:
1、其实就是一个filter,然后帮你做了操作redis的动作。
filter的名字是SessionRepositoryFilter,order顺序是:@Order(-2147483598)
{为什么关注这个,是因为当时写了一个自定义的filter,希望可以将自定义filter执行顺序放在这个前面,从而达到修饰request,response的目的,但是失败了。。。。😢}
 1  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
 2         request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
 3         SessionRepositoryFilter<S>.SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryFilter.SessionRepositoryRequestWrapper(request, response, this.servletContext);
 4         SessionRepositoryFilter<S>.SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryFilter.SessionRepositoryResponseWrapper(wrappedRequest, response);
 5         HttpServletRequest strategyRequest = this.httpSessionStrategy.wrapRequest(wrappedRequest, wrappedResponse);
 6         HttpServletResponse strategyResponse = this.httpSessionStrategy.wrapResponse(wrappedRequest, wrappedResponse);
 7 
 8         try {
 9             filterChain.doFilter(strategyRequest, strategyResponse);
10         } finally {
11             wrappedRequest.commitSession();
12         }
13 
14     }
doFilter
wrappedRequest.commitSession();这个就是将创建好的session保存到远程redis中。
 1    private void commitSession() {
 2             SessionRepositoryFilter<S>.SessionRepositoryRequestWrapper.HttpSessionWrapper wrappedSession = this.getCurrentSession();
 3             if (wrappedSession == null) {
 4                 if (this.isInvalidateClientSession()) {
 5                     SessionRepositoryFilter.this.httpSessionStrategy.onInvalidateSession(this, this.response);
 6                 }
 7             } else {
 8                 S session = wrappedSession.getSession();
 9                 SessionRepositoryFilter.this.sessionRepository.save(session);
10                 if (!this.isRequestedSessionIdValid() || !session.getId().equals(this.getRequestedSessionId())) {
11                     SessionRepositoryFilter.this.httpSessionStrategy.onNewSession(session, this, this.response);
12                 }
13             }
14 
15         }
commitSession

源码就是好看,名字很规范,基本看名字就知道什么意思。

如果sessionid没有写进cookie,那么就会调用CookieHttpSessionStrategy.onNewSession方法:

   public void onNewSession(Session session, HttpServletRequest request, HttpServletResponse response) {
        Set<String> sessionIdsWritten = this.getSessionIdsWritten(request);
        if (!sessionIdsWritten.contains(session.getId())) {
            sessionIdsWritten.add(session.getId());
            Map<String, String> sessionIds = this.getSessionIds(request);
            String sessionAlias = this.getCurrentSessionAlias(request);
            sessionIds.put(sessionAlias, session.getId());
            String cookieValue = this.createSessionCookieValue(sessionIds);
            this.cookieSerializer.writeCookieValue(new CookieValue(request, response, cookieValue));
        }
    }
onNewSession

继续跟进,最终会调用DefaultCookieSerializer.writeCookieValue,终于到了精髓部分:

 1 public void writeCookieValue(CookieValue cookieValue) {
 2         HttpServletRequest request = cookieValue.getRequest();
 3         HttpServletResponse response = cookieValue.getResponse();
 4         String requestedCookieValue = cookieValue.getCookieValue();
 5         String actualCookieValue = this.jvmRoute == null ? requestedCookieValue : requestedCookieValue + this.jvmRoute;
 6         Cookie sessionCookie = new Cookie(this.cookieName, this.useBase64Encoding ? this.base64Encode(actualCookieValue) : actualCookieValue);
 7         sessionCookie.setSecure(this.isSecureCookie(request));
 8         sessionCookie.setPath(this.getCookiePath(request));
 9         String domainName = this.getDomainName(request);
10         if (domainName != null) {
11             sessionCookie.setDomain(domainName);
12         }
13 
14         if (this.useHttpOnlyCookie) {
15             sessionCookie.setHttpOnly(true);
16         }
17 
18         if ("".equals(requestedCookieValue)) {
19             sessionCookie.setMaxAge(0);
20         } else if (this.rememberMeRequestAttribute != null && request.getAttribute(this.rememberMeRequestAttribute) != null) {
21             sessionCookie.setMaxAge(2147483647);
22         } else {
23             sessionCookie.setMaxAge(this.cookieMaxAge);
24         }
25 
26         response.addCookie(sessionCookie);
27     }
writeCookieValue

查看了这个类,就明白了,我只要操作其中的useHttpOnlyCookie这个属性,改为false,就可以创建不是httponly的session啦,😄

private boolean useHttpOnlyCookie = this.isServlet3();
private boolean isServlet3() {
try {
ServletRequest.class.getMethod("startAsync");
return true;
} catch (NoSuchMethodException var2) {
return false;
}
}
懂了,原来Servlet3往上全是httponly的,因为httponly可以防止js攻击。。。坑爹
但我还是不放弃:
自己注入!!!
1 @Bean
2     public DefaultCookieSerializer defaultCookieSerializer(){
3         DefaultCookieSerializer defaultCookieSerializer = new DefaultCookieSerializer();
4         defaultCookieSerializer.setUseHttpOnlyCookie(false);
5         defaultCookieSerializer.setDomainName("localhost:8081");
6         defaultCookieSerializer().setCookiePath("/");
7         return defaultCookieSerializer;
8     }
注入自己需要配置的session

大功告成!虽然最后还是没有解决ajax跨域请求的问题,但还是学到了,明天继续解决。。。

搬砖ing。。。

 

后记:终于解决了:

 

beforeSend:function(xhr){
xhr.withCredentials=true;
},
😢。。。
还遇到一个坑:图片img的src不变,想让浏览器重新加载怎么办,在图片地址src不变的情况下让浏览器重新加载图片,实际上在src不变时,浏览器直接就去读取缓存。
我们只需要在src后面加上参数就行。。
//改变验证码-micro service
function changeImageMS(){
$("#verifyImage").empty();
var textHtml="<img src='"+ctxaccount+"/account/getValidatePicture?t="+imageNumber+"' id='Image' style='cursor:pointer;' alt='看不清,换一张'/>";
$("#verifyImage").append(textHtml);
imageNumber++;
}}
这样一个带验证码微服务注册的就搞定啦

 

 

  

posted @ 2018-10-11 21:30  忘川vs  阅读(1024)  评论(0编辑  收藏  举报