郁闷的一天,昨天还以为简单引入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++;
}}
这样一个带验证码微服务注册的就搞定啦