##针对网络上各种”自定义过滤器Filter进行JWT登陆令牌验证并设置响应头实现跨域“时跨域失效问题详解
首先,使用Spring相关框架,Maven环境。
如若仅需要后台跨域设置,则使用下面方法1足以。若已存在已有的自定义过滤器,则方法1势必会和原有的过滤器引发冲突。详解如下:
解决跨域方法1(项目不包含任何自定义过滤器时可用):
在启动类中或@Configuration自定义配置类中使用@bean注册自定义配置 添加如下代码即可:
@Configuration
public class CorsConfig {
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*"); // 1
corsConfiguration.addAllowedHeader("*"); // 2
corsConfiguration.addAllowedMethod("*"); // 3
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig()); // 4
return new CorsFilter(source);
}
}
解决跨域方法2(项目中已有自定义过滤器时可用,此处以JWT登陆令牌验证的自定义过滤器为例):
JWT登陆令牌验证完整版参考链接(包含跨域设置):http://blog.csdn.net/qq_21144985/article/details/79363524
已存在自定义过滤器时,在过滤器中重写的doFilter()中设置响应头即可。网络上普遍的代码如下:
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws JsonProcessingException, IOException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, x-requested-with");
httpResponse.setHeader("Access-Control-Allow-Methods", "OPTIONS,GET,POST,DELETE,PUT");
chain.doFilter(request, response);
}
此方法笔者试了很多次均以失败告终。后改成如下代码遂得以解决。
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws JsonProcessingException, IOException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
if("OPTIONS".equals(httpRequest.getMethod())) {
httpResponse.setStatus(HttpStatus.SC_NO_CONTENT); //HttpStatus.SC_NO_CONTENT = 204
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, x-requested-with, Token");
httpResponse.setHeader("Access-Control-Allow-Methods", "OPTIONS,GET,POST,DELETE,PUT");
}
chain.doFilter(request, response);
}
此处需要引入apahe commons HttpClient的一个jar包:
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
为何上述第一种方式不能解决而第二种方式却可以解决呢?请参考如下文档:
https://segmentfault.com/a/1190000009971254
通过参考前辈的解决方案得知,浏览器发送请求时会将请求分为简单请求和预检请求。所有非get请求都是预检请求。
简单请求:浏览器进行一次请求获取服务端一次响应。
预检请求:首先先尝试发送一个method为options的请求进行预检。检测是否具有访问服务器资源的权限。返回200+的状态码即表示预检通过,前端在接收到此状态码之后将发送正式请求。
因此,结合实际测试我们得知,阻止我们正常响应的就是预检不通过。由预检不通过导致的跨域不通过。预检请求校验中就包含了跨域校验,所以预检通过时,即跨域成功。
此处需要提的一点是:前端浏览器设置跨域,本质是将预检请求改为简单请求。
所以在使用上面第一种响应头设置时,因为过滤器对webtoken的拦截,而预检请求均不会设置参数。而我们在进行登录令牌校验时,针对所有非放行接口的webtoken,当其值为空时均返回登录异常。所以每次都不会返回正常结果。于是浏览器得到的永远都是预检不通过。因此即使设置了响应头也无法跨域成功。因为预检不通过是不会发送正式请求的。
而即便进行了webtoken为空时放行,也会触发跨域校验导致跨域不通过。
明白了问题所在后,我们在webtoken校验之前首先判断请求是否为预检请求,然后将所有预检请求的状态码设置为正常响应。设置完成之后,浏览器每次使用预检请求时均可以得到正常结果,因此浏览器接下来就会发送正式请求。
但设置后依旧会发现无法成功,预检请求时获取webtoken值时报错。原因是因为,我们在设置响应头中的请求头时,没有对webtoken参数的key值进行配置。因此 我们修改这句话如下:
httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, x-requested-with, Token");
在Headers跨域设置时将前端给request中的header中设置的webtoken的Key值在此添加上,表示header中可以有一个叫‘Token’的参数。
设置完成之后就在登录校验完全限制的情况下跨域成功了。
总结起来就是我们在进行跨域登录令牌校验时按照如下步骤放行:
1、放行所有预检请求。
2、放行所有配置的需要放行的请求。
3、进行webtoken的存在性校验。
4、进行webtoken内容解析校验。
5、放行满足3和4的请求。
6、其余请求均为异常请求返回异常信息。
如有疑问烦请@笔者 Q:980420579 Q群:697819474