1.背景
起初我程序是配置了跨域设置的 如下
@Component
public class WebConfig implements WebMvcConfigurer {
private static final Logger logger = LogManager.getLogger(WebConfig.class);
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
// 设置允许跨域请求的域名
.allowedOriginPatterns("*")
// 设置允许的方法
.allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
// 是否允许证书(cookies)
.allowCredentials(true)
// 跨域允许时间
.maxAge(3600);
}
}
本来正常请求以及OPTIONS请求都是没问题的postman 和 前端都可以正常收发数据
后面根据业务需求添加过滤器 如下(省略部分业务代码)
@WebFilter(urlPatterns = "/*" , filterName = "TokenFilter")
public class TokenFilter implements Filter {
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String options = "OPTIONS";
// OPTIONS 请求略过
if (options.equals(request.getMethod())) {
filterChain.doFilter(request, response);
return;
}
TokenBean tokenBean = tokenCommon.getTokenBeanByAccessToken(accessToken);
boolean tokenFlag = tokenCommon.checkToken(tokenBean);
if (!tokenFlag) {
logger.error("Token不存在或者过期:" + request.getMethod());
response.setStatus(401);
errorMessage(response, "请求非法");
return;
}
}
}
// 此处为 过滤器不通过直接返回页面数据
public static void errorMessage(HttpServletResponse res, String message) throws IOException {
res.setContentType("application/json;charset=UTF-8");
PrintWriter out = res.getWriter();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValue(out, RestResultFactory.errorMessage(message));
}
这个时候问题就出现了 明明设置的跨域 也对OPTIONS请求做了处理 但是一旦在过滤器中直接返回 也就是调用 上面的 errorMessage 方法 就会出现跨越错误 而只要在过滤器中正确通过 依然能够正常通行 这说明本身跨域是起作用的 那么问题就在在于过滤器直接返回是有问题的
首先返回的方法本身没有问题 已经测试过多次 而上面的 WebConfig 配置是在程序启动就加载进去了 问题就出在 response 返回未做跨域处理
而我网上 收到的大多是 这种处理
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods","POST, GET, PUT, OPTIONS, DELETE, PATCH");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers","token,Origin, X-Requested-With, Content-Type, Accept");
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
以及这种
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, X-Requested-With, token");
response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS, POST, PUT, DELETE");
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Max-Age", "3600");
但是这两种放在doFilter 中都不行 说明过滤器中response返回这两种前端都不认啊
没办法 继续搜索 发现了这种
String allowOrigin = request.getHeader("Origin");
String allowHeader = request.getHeader("Access-Control-Request-Headers");
System.out.println(allowOrigin);
System.out.println(allowHeader);
response.setHeader("Access-Control-Allow-Origin", allowOrigin);
//设置允许带cookie的请求
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "OPTIONS, POST, PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Allow-Headers", allowHeader);
看起来这种跟上面的区别不大 唯独两个 参数是获取Request再装载到Response中 这就很奇怪为啥这么做 通过打印输出得出如下结果
第一次 OPTIONS 请求输出如下
http://localhost:8081
access_token,content-type,x-requested-with
第二次 Post 请求 输出如下
http://localhost:8081
null
那么再对比上面两种就会发现
Access-Control-Allow-Headers 获取的值 跟 固定写死一个:token,Origin, X-Requested-With, Content-Type, Accept 或 Authorization, Content-Type, X-Requested-With, token 不一样
我输出中有个 access_token 这个是重点 这个就是我放在head中的token 名称 可不是固定写死的 token
现在加入进去后 从过滤器直接返回就可以正常显示了 并且能收到401的状态码
总结 假如你跟我也是head带入自定义的Token 建议不要写死 通过Request中获取后再返回
还有一个有趣的就是 在过滤器加入这个后 最开始的 WebConfig 去掉也可以实现跨域 加上也不影响 大家可以自己测试下