SpringMVC支持跨域的几种姿势
跨域好像是一个前端的问题,通常是a域名下向b域名的服务发起请求,然后处于浏览器的安全原则,被拦截了,而这种场景,在实际的项目中并不少见,那么作为后端可以怎么去支持跨域的case呢?
后端需要支持跨域,一个是支持jsonp请求;还有一个就是设置responseHeader中crossOrigin等相关参数
<!-- more -->
I. Jsonp的支持
jsonp的请求表现方式就是url里面会多一个参数 callback,一般如下
callback=jQuery21105810685605043302_1516257942328
jsonp的返回与一般调用方式的返回也会有点区别,会在外面包装一层,如
jQuery21105810685605043302_1516257942328(...);
springmvc中,jsonp的支持却是比较简单了,不需要对现有的接口进行任何处理,只需要像下面这么玩即可
@ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpAdvice() {
super("callback");
}
}
分析说明
首先是利用了注解 @ControllerAdvice
, 这个注解在后面说到的统一异常处理时,也会用到,从命名也可以看出,就是为Controller添加一个切面,简单来讲,就是在直接返回数据前,对返回的结果包装一把;从实现也可以看出,主要的逻辑就在 AbstractJsonpResponseBodyAdvice
里面,所以有必要看一下这个东西是怎么支持的了
核心的代码逻辑就是
@Override
protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,
MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
for (String name : this.jsonpQueryParamNames) {
String value = servletRequest.getParameter(name);
if (value != null) {
if (!isValidJsonpQueryParam(value)) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring invalid jsonp parameter value: " + value);
}
continue;
}
// 下面三行是主要的逻辑
MediaType contentTypeToUse = getContentType(contentType, request, response);
response.getHeaders().setContentType(contentTypeToUse);
bodyContainer.setJsonpFunction(value);
break;
}
}
}
直接看可能看不太明白究竟做了什么,写了个测试,debug下相关的参数如下
即,修改返回的 content-type 为: application/javascript
返回的Container里面设置了jsonpFunction,为请求参数的value,至于是在什么时候封装的返回结果呢?这个有待后续补全
II. 支持cors跨域
Cross-Origin Resource Sharing(CORS)跨来源资源共享是一份浏览器技术的规范,提供了 Web 服务从不同域传来沙盒脚本的方法,以避开浏览器的同源策略,是 JSONP 模式的现代版。与 JSONP 不同,CORS 除了 GET 要求方法以外也支持其他的 HTTP 要求
1. 背景
CORS背后的基本思想是使用自定义的HTTP头部允许浏览器和服务器相互了解对方,从而决定请求或响应成功与否
所以问题就来了,安全如何保证?
一般而言,为了避免夸站点攻击(csrf),常见的手段无非:
- 身份校验(比如要求用户登录)
- token验证
- ip白名单
- 来源referer校验
- 频率限制
2. 实现方式
要支持csrf,也比较简单了,无非就是设置下responseHeader了, 一般需要设置以下几项
- Access-Control-Allow-Origin: *; // 允许的来源
- Access-Control-Allow-Methods: GET, POST, PUT, DELETE
- Access-Control-Allow-Credentials: true
- Access-Control-Allow-Headers: Content-Type
- Access-Control-Max-Age: 1800 //30 min
所以实现起来的方式就比较多了,一个是新增一个filter,主动设置下返回头,当然spring mvc提供了更友好的方式了
常见的几种手段如下:
a. xml配置方式
<mvc:cors>
<mvc:mapping path="/ajax/*"
allowed-origins="*"
max-age="3600" />
</mvc:cors>
b. 注解方式
在controller方法上,添加下面这个注解即可
@CrossOrigin(origins = "*")
@RequestMapping(value = {"xx"},
method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.OPTIONS})
public ResponseWrapper<WxBaseResponse> create(HttpServletRequest httpServletRequest) {
}
c. 直接修改返回的responseHeader
response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin"));
response.setHeader("Access-Control-Allow-Methods", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
III. 小结
上面介绍了两种方式,支持起来都比较简单
- jsonp: 通过ControllerAdvice拦截Controller,然后继承AbstractJsonpResponseBodyAdvice即可
- cors: 通过xml配置或者直接使用 @CrossOrigin注解
IV. 其他
声明
尽信书则不如,已上内容,纯属一家之言,因本人能力一般,见解不全,如有问题,欢迎批评指正