在项目开发过程中不可避免的就是跨域请求,以下下是跨域请求处理的几种方法总结
1. 直接采用SpringBoot的注解@CrossOrigin
Controller层在需要跨域的类或者方法上加上该注解即可。
2. 处理跨域请求的Configuration继承WebMvcConfigurerAdapter或者实现WebMvcConfigurer接口
例如:CrossOriginConfig.java
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* AJAX请求跨域
* @author Mr.W
* @time 2018-08-13
*/
@Configuration
public class CorsConfig extends WebMvcConfigurerAdapter {
static final String ORIGINS[] = new String[] { "GET", "POST", "PUT", "DELETE" };
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowCredentials(true).allowedMethods(ORIGINS)
.maxAge(3600);
}
}
3. 采用过滤器的方式Filter
@Component
public class CORSFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
res.addHeader("Access-Control-Allow-Credentials", "true");
res.addHeader("Access-Control-Allow-Origin", "*");
res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
res.addHeader("Access-Control-Allow-Headers", "Content-Type,X-CAF-Authorization-Token,sessionToken,X-TOKEN");
if (((HttpServletRequest) request).getMethod().equals("OPTIONS")) {
response.getWriter().println("ok");
return;
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
4. 引入maven依赖:
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>RELEASE</version>
</dependency>
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
@Configuration
public class RouteConfiguration {
//这里为支持的请求头,如果有自定义的header字段请自己添加(不知道为什么不能使用*)
private static final String ALLOWED_HEADERS = "x-requested-with, authorization, Content-Type, Authorization, credential, X-XSRF-TOKEN,token,username,client";
private static final String ALLOWED_METHODS = "*";
private static final String ALLOWED_ORIGIN = "*";
private static final String ALLOWED_Expose = "*";
private static final String MAX_AGE = "18000L";
@Bean
public WebFilter corsFilter() {
return (ServerWebExchange ctx, WebFilterChain chain) -> {
ServerHttpRequest request = ctx.getRequest();
if (CorsUtils.isCorsRequest(request)) {
ServerHttpResponse response = ctx.getResponse();
HttpHeaders headers = response.getHeaders();
headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN);
headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS);
headers.add("Access-Control-Max-Age", MAX_AGE);
headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS);
headers.add("Access-Control-Expose-Headers", ALLOWED_Expose);
headers.add("Access-Control-Allow-Credentials", "true");
if (request.getMethod() == HttpMethod.OPTIONS) {
response.setStatusCode(HttpStatus.OK);
return Mono.empty();
}
}
return chain.filter(ctx);
};
}
}
5. 前后分离的跨域问题其他解决方案
Nginx服务器反向代理
通过反向代理服务器监听同端口,同域名的访问,不同路径映射到不同的地址,比如,在nginx服务器中,监听同一个域名和端口,不同路径转发到客户端和服务器,把不同端口和域名的限制通过反向代理,来解决跨域的问题。
server {
listen 80;
server_name abc.com;
#charset koi8-r;
#access_log logs/host.access.log main;
location /client { #访问客户端路径
proxy_pass http://localhost:81;
proxy_redirect default;
}
location /apis { #访问服务器路径
rewrite ^/apis/(.*)$ /$1 break;
proxy_pass http://localhost:82;
}
}
直接在Nginx中进行配置
location / {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers X-Requested-With;
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
if ($request_method = 'OPTIONS') {
return 204;
}
}
6 还有一种使用 jsonp 的调用方式,不过好像太古老了
JSON的核心是:动态添加script标签来调用服务器提供的js脚本。JQuery ajax本质上是通过script标签实现跨域请求,而非真正的ajax请求。整个过程可简单描述为:客户端跨域请求数据responseData,然后使用客户端js函数handleResponse(responseData)对数据进行处理。具体实现思路为:客户端封装一个脚本请求如
<script src="https://www.jsonp_request.com/request_method?callback=handleResponse¶m1=valu1...">
其中handleResponse为本地JS函数;然后,服务器端获取callback的回调函数名即handleResponse后,将处理结果responseData,包装成javascript函数调用的形式,即字符串“handleResponse({responseData的json数据})”传回客户端;最后,客户端使用eval(返回字符串);执行返回结果。
JQuery封装JSONP请求:对于经常使用jQuery的开发者来说,肯定注意到jQuery封装的$.ajax中有一个dataType属性,如果将该属性设置成dataType:“jsonp”,就能实现JSONP跨域了。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jQuery实现JSONP</title>
</head>
<body>
<div id="mydiv">
<button id="btn">点击</button>
</div>
</body>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
<script type="text/javascript">
$(function(){
$("#btn").click(function(){
$.ajax({
async : true,
url : "https://api.douban.com/v2/book/search",
type : "GET", //如果是跨域请求或将crossDomain设置为true,则type只能是GET
dataType : "jsonp", // 返回的数据类型,设置为JSONP方式
jsonp : 'callback', //指定一个查询参数名称来覆盖默认的 jsonp 回调参数名 callback
jsonpCallback: 'handleResponse', //设置回调函数名,默认jquery自定义
data : {
q : "javascript",
count : 1
},
success: function(response, status, xhr){ //返回数据,即response.data
console.log('状态为:' + status + ',状态是:' + xhr.statusText);
console.log(response);
}
});
});
});
</script>
其实,在JQuery的JSONP实现中,首先将你在success中定义的函数封装成jsonpCallback定义的函数,然后再封装一个脚本请求如
<script src="https://api.douban.com/v2/book/search?callback=handleResponse&q=javascript&count=1">
。
JSONP请求,缺点是仅支持GET请求,必须完全信任提供服务的第三方,优点是支持老式浏览器,兼容性较好。CORS比JSONP更强大,支持所有类型的HTTP请求。