vue发送请求时总多出一个options请求,该请求拥有独立的sessionid,干扰后台过滤器的登录验证。
浏览器在跨域请求时会自动预发送一个 options 请求确认站点是否允许跨域访问。这个功能是浏览器内置的,js无权干预。这个问题干扰后端在过滤器里验证session中是否有当前登录人信息的逻辑。先来看下浏览器自动发送options的问题,如下图。
因为这个请求采用了独立的sessionid,所以后端采用通过将登录信息存入Session的方式便会出现问题,过滤器拦截到这个请求后,根据该sessionid无法获取到登录人信息,从而当作未登录处理,系统退出。所以,如果一定要以传统session方式存储登录人信息的话,必须在过滤器中放行 options 请求。
本人只是为了教学需要整合了下前后端分离框架和传统的session模型。关于登录信息保持,真实的系统如果采用有状态的方案往往采用redis+token,而采用jwt的无状态方案更是大势所趋。所以本文的问题并太不具备普遍性,仅供学习参考。
以下是关键代码。
前端发起请求的拦截器代码:
// response拦截器
service.interceptors.response.use(
response => {
// return response.data
console.info(response);
// code为非200是抛错 可结合自己业务进行修改
if(response.data.restCode != 200){
Message({
message: '登录状态已过期,请重新登录',
type: 'warning'
})
//清空当前登录信息
sessionStorage.removeItem("loginInfo");
//退出
router.push('/login')
}else{
return response.data
}
}
后端过滤器代码:
public class AuthFilter implements Filter {
private static final Set<String> ALLOWED_PATHS = Collections.unmodifiableSet(new HashSet<>(
Arrays.asList("/admin/login")));
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse resp = (HttpServletResponse) servletResponse;
String path = req.getRequestURI().substring(req.getContextPath().length()).replaceAll("[/]+$", "");
System.out.println("path:" + path);
String sessionId = req.getSession().getId();
System.out.println("sessionId:" + sessionId + " req.method:" + req.getMethod());
//filterChain.doFilter(servletRequest, servletResponse);
//放行options请求, 因为浏览器的跨域验证,通过options请求发送
if(req.getMethod().equals("OPTIONS")){
filterChain.doFilter(servletRequest, servletResponse);
return;
}
boolean allowedPath = ALLOWED_PATHS.contains(path);
if (allowedPath) {
System.out.println("这里是不需要处理的url进入的方法");
filterChain.doFilter(servletRequest, servletResponse);
}
else {
System.out.println("这里是需要处理的url进入的方法");
Object currentUser = req.getSession().getAttribute("currentUser");
if(currentUser != null){
filterChain.doFilter(servletRequest, servletResponse);
}else{
resp.setCharacterEncoding("UTF-8");
resp.addHeader("Access-Control-Allow-Origin", "http://localhost:8080");
resp.addHeader("Access-Control-Allow-Credentials","true");
resp.setContentType("application/json; charset=utf-8");
PrintWriter out = resp.getWriter();
JSONObject res = new JSONObject();
res.put("code", -101);
res.put("restInfo", "登录已过期,请重写登录");
out.append(res.toString());
out = resp.getWriter();
}
}
}
}