SpringMVC支持跨域的几种姿势

后端需要支持跨域,一个是支持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下相关的参数如下

SpringMVC支持跨域的几种姿势-LMLPHP

即,修改返回的 content-type 为: application/javascript

返回的Container里面设置了jsonpFunction,为请求参数的value,至于是在什么时候封装的返回结果呢?这个有待后续补全

II. 支持cors跨域

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. 其他

声明

尽信书则不如,已上内容,纯属一家之言,因本人能力一般,见解不全,如有问题,欢迎批评指正

扫描关注,java分享

SpringMVC支持跨域的几种姿势-LMLPHP

05-17 21:36