由于与权限拦截器冲突导致的Cors跨域设置失效问题
自定义拦截器实现跨域处理,
这里就要先了解CORS的跨域原理,下面这段摘自https://www.jianshu.com/p/f5a88983f42d。
cross-origin resource sharing, 跨域资源共享.
因为出于安全的考虑, 浏览器不允许Ajax调用当前源之外的资源. 即浏览器的同源策略.
CORS需要浏览器和服务器同时支持。目前,所有主流浏览器都支持该功能,IE浏览器不能低于IE10。在浏览器端, 整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,用户对这些都不会有感觉。因此,实现CORS通信的关键是服务器。
2. 跨域请求分类
浏览器将跨域请求分为两大类: 简单请求和非简单请求.
同时满足以下条件的请求都为简单请求:
凡是同时满足以上两个条件的, 就是简单请求.
3. 跨域请求处理方式
3.1 简单请求
对于简单请求, 浏览器直接发出CORS请求, 即浏览器自动在请求header中加上Origin
字段, 告诉服务器这个请求来自哪个源(请求协议+域名+端口). 服务器收到请求后, 会对比这个字段, 如果字段值不在服务器的许可范围内, 服务器会返回一个正常的HTTP响应, 但是其响应头中不会包含Access-Control-Allow-Origin
字段, 浏览器发现后, 就会抛出一个异常提示响应头中没有这个字段. 如果这个源在服务器的许可范围内, 服务器的响应头会加上以下字段:
Access-Control-Allow-Origin:http://ip:port
Origin
的值.Access-Control-Allow-Credentials:true
Access-Control-Expose-Headers:
Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma
这六个字段.3.2 非简单请求
对于非简单请求, 浏览器的CORS
请求分为两步, 首先是执行预检(preflight)
请求, 询问服务器是否允许当前源访问, 如果允许, 才会执行实际请求, 预检请求可以缓存(缓存时间由服务器定义), 在缓存有效期内再执行CORS请求时无需进行预检请求.
3.2.1 预检请求
OPTIONs
, 表示这个请求是用来询问的。Origin
的值.如果预检请求正常返回, 接下来执行实际请求. 在预检请求缓存有效期内, 再执行跨域请求时无需进行预检请求.
总之get的ajax请求,可以实现 简单跨域请求,不需要预检请求,浏览器会自动添加一些头,后端再返回Access-Control-Allow-Origin头就可以了。但是当post的ajax请求传送json数据时,是非简单请求。这里需要先进行一次预检请求。
这里要注意,因为要传送json数据,后端的预检请求的响应头要返回,
response.setHeader("Access-Control-Allow-Headers", "content-type");
对应前端的请求头Access-Control-Allow-Headers:content-type。代表允许接受的头信息。之后再进行实际请求。
常见Content-Type有三种:
1.content-type:application/x-www-form-urlencoded 默认格式,当没有在信息头指定Content-Type的时候,默认使用这种格式传参
key1=value1
key2=value2
2. content-type:application/json 参数为json格式
{
"key1":"value1",
"key2":"value2"
}
3.content-type:multipart/form-data 上传文件用这种格式
预检请求:
实现跨域请求的拦截器。(这里简便处理,实际应自行添加Oringin的名单)
package com.yaoct.blog.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;
@Component
public class CrossOriginInteceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String origin = request.getHeader("Origin");
System.out.println(origin);
//当成非跨域请求
if(origin==null||origin.equals("")){
return true;
}
//非简单请求需要预请求,有json有cookie都是非简单请求
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", "content-type");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
参考:https://segmentfault.com/a/1190000010348077
=====================2020.6.18=====================
使用另一种方法,使用CorsFilter过滤器。
参考https://segmentfault.com/a/1190000018018849
private CorsConfiguration corsConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
* 请求常用的三种配置,*代表允许所有,当时你也可以自定义属性(比如header只能带什么,只能是post方式等等)
*/
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.setAllowCredentials(true);
corsConfiguration.setMaxAge(3600L);
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfig());
return new CorsFilter(source);
}