一、什么情况下需要跨域
跨域问题源于浏览器的同源策略,所谓同源(即指在同一个域)就是两个页面具有相同的协议(http/https),主机(host)和端口号(port)。
同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
那么跨域,说的就是:当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域
工作中我们经常需要获取其他服务的资源信息,这种时候就需要考虑跨域的问题。
二、跨域常见的两种方式
1. JSONP
- JSONP是“JSON with padding”的简写,我将其翻译为“被包裹的JSON”。JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,兼容性好(兼容低版本IE)
原理
首先,我们应该清楚的认识到,浏览器的“同源策略”只是阻止了通过AJAX技术跨域获取资源,而并没有禁止跨域获取资源这件事本身,正因如此,我们可以通过<link>标签,<img>标签以及<script>标签中的href属性或src属性获取异域的CSS,JS资源和图片(虽然我们其实并不能读取这些资源的内容)。
核心思想:网页通过添加一个<script>元素,向服务器请求 JSON 数据,服务器收到请求后,将数据放在一个指定名字的回调函数的参数位置传回来。
JSONP的本质是绕过AJAX获取资源的机制,使用原始的src属性获取异域资源;
- 原生实现
<script src="http://test.com/data.php?callback=dosomething"></script>
// 向服务器test.com发出请求,该请求的查询字符串有一个callback参数,用来指定回调函数的名字
// 处理服务器返回回调函数的数据
<script type="text/javascript">
function dosomething(res){
// 处理获得的数据
console.log(res.data)
}
</script>
- jQuery ajax:
$.ajax({
url: 'http://www.test.com:8080/login',
type: 'get',
dataType: 'jsonp', // 请求方式为jsonp
jsonpCallback: "handleCallback", // 自定义回调函数名
data: {}
});
- Vue.js
this.$http.jsonp('http://www.domain2.com:8080/login', {
params: {},
jsonp: 'handleCallback'
}).then((res) => {
console.log(res);
})
JSONP的缺点
- 无法发送POST请求,也就是说JSONP技术只能用于请求异域资源,无法上传数据或修改异域数据;
- 无法监测JSONP请求是否失败;
- 可能存在安全隐患:别忘了,JSONP之所以能成功获取异域服务器资源,靠的是服务器动态生成了回调函数,并在页面中执行,那么如果服务器在原有的回调函数下再添加些别的恶意JavaScript代码会怎样?当然也会被执行!所以在使用JSONP技术时,一定要确保请求资源的服务器是值得信赖的;
2. 官方推荐:CORS
- CORS 是跨域资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法。
Spring Boot 通过 CORS 实现跨域
1、全局配置
可以通过实现WebMvcConfigurer接口然后重写addCorsMappings方法解决跨域问题。
// 请求跨域
@Configuration
public class CorsConfig implements WebMvcConfigurer {
static final String ORIGINS[] = new String[] { "GET", "POST", "PUT", "DELETE" };
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 所有的当前站点的请求地址,都支持跨域访问。
.allowedOrigins("*") // 所有的外部域都可跨域访问。 如果是localhost则很难配置,因为在跨域请求的时候,外部域的解析可能是localhost、127.0.0.1、主机名
.allowCredentials(true) // 是否支持跨域用户凭证
.allowedMethods(ORIGINS) // 当前站点支持的跨域请求类型是什么
.maxAge(3600); // 超时时长设置为1小时。 时间单位是秒。
}
}
2.使用 @CrossOrigin 注解
Controller层在需要跨域的类或者方法上加上该注解即可。
@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
@CrossOrigin(origins = "*",maxAge = 3600)
public class UserController {
final UserService userService;
@GetMapping("/getOne/{id}")
public User getOne(@PathVariable("id") Integer id) {
return userService.getById(id);
}
}
3、自定义跨域过滤器
1).编写过滤器
// 跨域过滤器
@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("Success");
return;
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
2).注册过滤器
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(urlBasedCorsConfigurationSource);
}
}