一、简介
在前后分离开发模式中突然遇到请求后台接口时前端控制台抛出资源请求头上没有"Access-Control-Allow-Origin"不允许访问,基本可以判定为跨域问题
1 什么是跨域
跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。
同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现
总结:访问客户端与服务端协议、域名、端口时,二者有任意一个不一样就是跨域,例:访问前端localhost:8081/index.html,客户端访问后台为localhost:8080/index/info,就跨域
2. 常见解决方案
- jsonp跨域
https://www.runoob.com/json/json-jsonp.html - CORS 跨域资源共享 http://www.ruanyifeng.com/blog/2016/04/cors.html
下面为java实现cors跨域方案
二、CORS跨域
1.spring mvc实现cors
(1)web.xml
<filter>
<filter-name>corsFilter</filter-name>
<filter-class>org.besteam.filter.CorsFilter</filter-class>
<init-param>
<param-name>allowOrigin</param-name>
<param-value>*</param-value>
</init-param>
<init-param>
<param-name>allowMethods</param-name>
<param-value>CONNECT, HEAD, OPTIONS, POST, GET, PUT, DELETE, TRACE</param-value>
</init-param>
<init-param>
<param-name>allowCredentials</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>allowHeaders</param-name>
<param-value>
Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires,
Content-Type, X-E4M-With, Accept, Language
</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>corsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.filter类
package org.besteam.filter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
* CORS 跨域过滤器,统一处理所有请求,解决 AJAX 跨域访问问题。
* <p>
* 本 Filter 将从 web.xml 中读取相关 Filter 初始化参数,并将在处理 HTTP 请求时将这些参数写入对应的 CORS 响应头中。
* <p> CORS是一个W3C标准,全称是“跨域资源共享”(Cross-origin resource sharing)。<br/>
* 它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。<br/>
* CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。
* 对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,
* 有时还会多出一次附加的请求,但用户不会有感觉。<br/>
* 因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
* <p>另外,通过设置参数,实现了 Ajax 有状态访问,保证 Ajax 请求每次的 sessionId 都一样。
* <br>
* <br>
* 创建时间:2017年10月3日 上午11:47:23 <br>
* 修改人: <br>
* 修改时间: <br>
* 修改备注: <br>
*
* @version V1.0
*/
public class CorsFilter implements Filter {
/**
* 允许访问的客户端域名,例如:http://web.xxx.com,http://www.baidu.com,若为
* *,则表示从任意域都能访问,即不做任何限制。
* <p>
* 需要注意的是,CORS 规范中定义 Access-Control-Allow-Origin 只允许两种取值,要么为
* *,要么为具体的域名,也就是说, 不支持同时配置多个域名。为了解决跨多个域的问题,需要在代码中做一些处理(见下文)。
*/
private String allowOrigin;
/**
* 允许访问的方法名,多个方法名用逗号分割,例如:GET,POST,PUT,DELETE,OPTIONS。
*/
private String allowMethods;
/**
* 是否允许请求带有验证信息,若要获取客户端域下的 cookie 时,需要将其设置为 true。
*/
private String allowCredentials;
/**
* 允许服务端访问的客户端请求头,多个请求头用逗号分割,例如:Content-Type。
*/
private String allowHeaders;
/**
* 允许客户端访问的服务端响应头,多个响应头用逗号分割。
*/
private String exposeHeaders;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
allowOrigin = filterConfig.getInitParameter("allowOrigin");
allowMethods = filterConfig.getInitParameter("allowMethods");
allowCredentials = filterConfig.getInitParameter("allowCredentials");
allowHeaders = filterConfig.getInitParameter("allowHeaders");
exposeHeaders = filterConfig.getInitParameter("exposeHeaders");
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (StringUtils.isNotEmpty(allowOrigin)) {
// 初始化参数可能是 * 或 多个域名用逗号拼接的字符串
List<String> allowOriginList = Arrays.asList(allowOrigin.split(","));
if (CollectionUtils.isNotEmpty(allowOriginList)) {
// 从当前请求中获取 Origin 请求头,就知道是从哪个域中发出的请求
String currentOrigin = request.getHeader("Origin");
// 若初始化参数为 * ,或者该请求在以上允许的域名集合中,则将其放入 Access-Control-Allow-Origin
// 响应头,这样跨多个域的问题就轻松解决了
if ("*".equals(allowOriginList.get(0)) || allowOriginList.contains(currentOrigin)) {
response.addHeader("Access-Control-Allow-Origin", currentOrigin);
}
}
}
if (StringUtils.isNotEmpty(allowMethods)) {
response.addHeader("Access-Control-Allow-Methods", allowMethods);
}
if (StringUtils.isNotEmpty(allowCredentials)) {
response.addHeader("Access-Control-Allow-Credentials", allowCredentials);
}
if (StringUtils.isNotEmpty(allowHeaders)) {
response.addHeader("Access-Control-Allow-Headers", allowHeaders);
}
if (StringUtils.isNotEmpty(exposeHeaders)) {
response.addHeader("Access-Control-Expose-Headers", exposeHeaders);
}
response.addHeader("Access-Control-Max-Age", "0");
response.addHeader("XDomainRequestAllowed","1");
if (request.getMethod().equals("OPTIONS")) {
response.setStatus(HttpServletResponse.SC_OK);
return;
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
2.springboot实现cors
配置config即可
package cn.stylefeng.guns.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
* @ClassName CorsConfig
* @Description TODO
* @Author ginwu
* @Date 2019/9/14 16:03
* @Version 1.0
*/
@Configuration
public class CorsConfig {
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*"); //允许任何域名
corsConfiguration.addAllowedHeader("*"); //允许任何头
corsConfiguration.addAllowedMethod("*"); //允许任何方法
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig()); //注册
return new CorsFilter(source);
}
}