Spring应用中处理cors跨域

cors

#1

spring应用中处理cors跨域

关于跨域和CORS相关概念性的问题,这里不多赘述

可以参考阮一峰大神的博客 http://www.ruanyifeng.com/blog/2016/04/cors.html

Spring提供的注解支持

CrossOrigin 可以标识在类或者方法上

import java.util.HashMap;
import java.util.Map;

import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/")
public class CorsController {
	
	@GetMapping
	@CrossOrigin(
		origins = {"http://localhost"}, // 允许哪些域名来跨域来请求当前资源,可以使用通配符 *
		allowedHeaders = {"foo"},		// 允许客户端请求携带的请求头
		exposedHeaders = {"foo"},		// 允许客户端访问的响应头
		methods = {RequestMethod.GET},	// 允许客户端跨域请求的请求方式
		allowCredentials = "true",		// 允许客户端请求提交cookie
		maxAge = 1600					// 预检测缓存时间
	)
	public Object cors() {
		Map<String,Object> map = new HashMap<>();
		map.put("success", Boolean.TRUE);
		return map;
	}
}

SpringBoot以编码的方式处理跨域

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
	
	@Override
	public void addCorsMappings(CorsRegistry registry) {
		registry.addMapping("/**")					// 需要跨域的uri
			.allowedOrigins("http://localhost")
			.allowedHeaders("foo")
			.exposedHeaders("foo")
			.allowedMethods("GET")
			.allowCredentials(true)
			.maxAge(1600);
	}
}

Spring以xml配置的方式处理跨域

<mvc:cors>
	<mvc:mapping path="/api/**"
		allowed-origins="http://domain1.com, http://domain2.com"
		allowed-methods="GET, PUT"
		allowed-headers="header1, header2, header3"
		exposed-headers="header1, header2" 
		allow-credentials="false"
		max-age="123" />

	<mvc:mapping path="/resources/**"
		allowed-origins="http://domain1.com" />
</mvc:cors>

原生的Servlet可以自定义一个 CorsFilter 来处理跨域

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpHeaders;


@WebFilter(urlPatterns = {"/api/*"},filterName = "corsFilter")
public class CorsFilter implements Filter {
	
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {
		
		HttpServletResponse httpServletResponse = (HttpServletResponse) response;
	
		httpServletResponse.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
		httpServletResponse.addHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "foo");
		httpServletResponse.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS,"foo");
		httpServletResponse.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET");
		httpServletResponse.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
		httpServletResponse.addHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "3600");
		
		chain.doFilter(request, response);
	}

	@Override
	public void destroy() {
		
	}
}

客户端需要设置,允许携带Cookie

默认的情况下,客户的跨域请求不会携带凭证,需要自己手动编码的方式去设置

XMLHttpRequest

let req = new XMLHttpRequest();
req.withCredentials = true;		// 在跨域的情况下,允许携带凭证
req.open('GET','http://localhost:8080');
req.send(null);
req.onreadystatechange = function(){
	if(req.readyState == 4 && req.status == 200){
		let response = req.responseText;
		console.log(response);
	}
}

fetch

fetch('http://localhost:8080',{
	credentials:'include',	// 在跨域的情况下,允许携带凭证
	method:'GET',
}).then(response => {
	if(response.ok){
		response.json().then((json) => {
			console.log(json);
		});
	}
});

千万要注意

如果需要客户端提交Cookie, Access-Control-Allow-Origin 就不能设为星号,必须指定明确的、与请求网页一致的域名(Origin)。否则浏览器会给出异常,跨域失败

String origin = httpServletRequest.getHeader(HttpHeaders.ORIGIN);
httpServletResponse.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, origin);