前言
在做项目时,在登录验证码生成环节,后台生成验证码图像返回给前端,并将验证码置于session
,用户填入验证码后传入后台并验证。
但在实验时发现,由于前后端分离项目存在跨域问题,session
不再相同,通过输出 sessionId
也可以看出,不同请求到达服务端时sessionId
是不同的,故需要考虑如何解决Session
不一致的问题。
正文
CORS
首先简单讲下CORS。
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
其中关于CORS的请求头参数主要有:
- Access-Control-Allow-Origin
该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。
- Access-Control-Allow-Credentials
该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下为fasle,即Cookie不包括在CORS请求之中。(为了确保session的正常使用,需要将此布尔值设为true)
- Access-Control-Expose-Headers
该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。
综上,为了sesion的使用,需要开启Credentials
前端开启Credentials
由于前端请求使用的是Vue中的axios,故在axios请求上统一设置Credentials,具体如下:
axios.defaults.withCredentials = true
当然,如果使用的是ajax,也可如下设置:
$.ajax({
....
xhrFields: {
withCredentials: true
},
crossDomain: true,
...
以上,前端设置完成。
服务端开启Credentials
相应地,对于客户端的参数,服务器端也需要进行设置。
对应客户端的 xhrFields.withCredentials: true 参数,服务器端通过在响应 header 中设置 Access-Control-Allow-Credentials = true 来运行客户端携带证书式访问。
具体如下:
首先添加一个全局拦截器:
package com.hpsyche.hpsycheauthserver.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author Hpsyche
*/
@Component
public class ProcessInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
httpServletResponse.setHeader("Access-Control-Allow-Origin", httpServletRequest.getHeader("origin"));
httpServletResponse.setHeader("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With");
httpServletResponse.setHeader("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
httpServletResponse.setHeader("X-Powered-By","Jetty");
//重点!!!
httpServletResponse.setHeader("Access-Control-Allow-Credentials","true");
String method= httpServletRequest.getMethod();
if (method.equals("OPTIONS")){
httpServletResponse.setStatus(200);
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
声明拦截器后,同时需要配置拦截路径
package com.hpsyche.hpsycheauthserver.config;
import com.hpsyche.hpsycheauthserver.interceptor.GlobalInterceptor;
import com.hpsyche.hpsycheauthserver.interceptor.ProcessInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author Hpsyche
*/
@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new ProcessInterceptor()).addPathPatterns("/**");
}
}
以上,再次进行业务处理,发现同一个会话进行时,即使是多次请求,也可确保了sessionId的一致,从而顺利解决了session的跨域问题。
总结
发现问题,是解决问题的第一步!
由于本人水平有限,如大佬们发现我方案中的漏洞,或者有更好的解决方案,望指出!