项目背景:
最近在做一个后台用Spring boot、Shiro、Mybatis plus 、Oauth2,前台用layui的项目.前端在调后台接口的时候需要在浏览器中的headers头中添加token和userId的值(根据业务不同可能传值不一样),后台有一个过滤器,获取到headers中的token和userId,并去验证token是否在有效期内,在进行其他操作,在这个过程中,shiro的过滤器中一直接收不到token和userId的值,并且前端会报跨域问题
前端错误信息
后端拦截器一直接收不到token和userId信息
但这个时候尝试用postman(postman不会出现跨域问题)发送试试,结果可以收到值,WTF???,开始考虑的原因可能是token和userId没有放入headers中,后来通过仔细看了下没问题啊(下图)
为什么不发送值呢。仔细又去看了CROS的介绍。原来CROS复杂请求时会先发送一个OPTIONS请求,来测试服务器是否支持本次请求,这个请求时不带数据的,请求成功后才会发送真实的请求。所以前面那个只发送key的问题是要确认服务器支不支持接收这个headers。所以每次获取不到数据的请求都是OPTIONS请求?。所以我们要做的就是把所有的OPTIONS请求统统放行。
做法是在 StatelessAuthcFilter(自定义的过滤器) 中继承AccessControlFilter(Shiro自带的过滤器) 并重写其中的 preHandler 方法。 代码如下:
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpRequest = WebUtils.toHttp(request);
HttpServletResponse httpResponse = WebUtils.toHttp(response);
if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpResponse.setHeader("Access-control-Allow-Origin", httpRequest.getHeader("Origin"));
httpResponse.setHeader("Access-Control-Allow-Methods", httpRequest.getMethod());
// httpResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH, DELETE, PUT");
httpResponse.setHeader("Access-Control-Allow-Headers", httpRequest.getHeader("Access-Control-Request-Headers"));
httpResponse.setStatus(HttpStatus.OK.value());
return false;
}
return super.preHandle(request, response);
}
这个时候还没有完全解决问题,上面步骤完成后,第一次OPTIONS请求成功,第二次真实请求也发送成功(状态为200),但是response里面没有数据,此时需要再次添加一个CrosFilter类,如下
package com.welsee.shiro;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class CorsFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpServletRequest httpRequest = (HttpServletRequest) request;
httpResponse.setHeader("Access-Control-Allow-Origin", httpRequest.getHeader("Origin"));
httpResponse.setHeader("Access-Control-Allow-Methods", httpRequest.getMethod());
httpResponse.setHeader("Access-Control-Max-Age", "3600");
httpResponse.setHeader("Access-Control-Allow-Headers", httpRequest.getHeader("Access-Control-Request-Headers"));
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
再次调试,可以了