跨域请求
跨域请求就是指:当前发起请求的域与该请求指向的资源所在的域不一样。这里的域指的是这样的一个概念:我们认为如果协议 + 域名 + 端口号均相同,那么就是同域,否则则是跨域的
跨域请求是由于浏览器的同源策略导致的,浏览器的同源策略是不能没有的(同源策略是浏览器最核心最基础的安全策略)
- 同源策略禁止 Ajax 直接发起跨域HTTP请求(其实可以发送请求,结果被浏览器拦截,不展示),同时 Ajax 请求不能携带与本网站不同源的 Cookie;如果没有同源策略别的域名就可以拿到你浏览器上其他的Cookie信息,这样会导致很多重要的信息泄露,例如不法的网站可能会利用你的cookie去登录一些网站,盗用你的信息等。
- DOM 层面的同源策略限制了来自不同源的
Document
对象或 JS 脚本,对当前document
对象的读取或设置某些属性;没有同源策略,一些脚本就能获取到你的用户密码输入框的内容信息
JSONP
JSONP 是一种非官方的跨域数据交互协议;JSONP 本质上是利用
JSONP 的理念就是:与服务端约定好一个回调函数名,服务端接收到请求后,将返回一段Javascript
,在这段 Javascript
代码中调用了约定好的回调函数,并且将数据作为参数进行传递。当网页接收到这段 Javascript
代码后,就会执行这个回调函数,这时数据已经成功传输到客户端了
JSONP**只支持 get请求,只支持 get请求,只支持 get请求**
/**
* 如果使用了springmvc 3.2以上的版本 和5.0以下的版本可以配置一个jsonp 的 Advice 就可以了
* spring会自动做相应的处理
*
* 注意:springboot2.0以上舍弃了AbstractJsonpResponseBodyAdvice
*/
@ControllerAdvice
public class JsonPAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonPAdvice() {
// 这样如果请求中带 callback 参数,Spring 就知道这个是 jsonp 的请求了
super("callback");
}
}
/**
* JsonP 只支持get方法
* 需要配置dataType为 jsonp
*/
function getJsonP() {
$.ajax({
type:"get",
url:"http://localhost:8088/getJsonP/value.do",
dataType:"jsonp",
success:function (data) {
$("#userName").val(data.userName);
},
error:function () {
$("#userName").val("错误了");
}
});
}
CORS
跨源资源共享 Cross-Origin Resource Sharing(CORS) 是一个新的 W3C 标准,它新增的一组HTTP首部字段,允许服务端其声明哪些源站有权限访问哪些资源。换言之,它允许浏览器向声明了 CORS 的跨域服务器,发出 XMLHttpReuest 请求,从而克服 Ajax 只能同源使用的限制
CORS新增的HTTP头信息
-
Access-Control-Allow-Origin | *
响应首部中可以携带这个头部表示服务器允许哪些域可以访问该资源
origin 参数的值指定了允许访问该资源的外域 URI。对于不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符,表示允许来自所有域的请求
-
Access-Control-Allow-Methods […, ] | *
该首部字段用于预检请求的响应,指明实际请求所允许使用的HTTP方法
-
Access-Control-Allow-Headers […, ] | *
该首部字段用于预检请求的响应。指明了实际请求中允许携带的首部字段
-
Access-Control-Allow-Credentials true | false
表示是否允许发送Cookie
**注意:**如果需要在 Ajax 中设置和获取 Cookie,那么
Access-Control-Allow-Origin
首部字段不能设置为*
,必须设置为具体的 origin 源站
Configuration配置方式
/**
* 如果使用了springmvc4.2以上的版本,直接使用mvc相关配置即可
*
* springboot 2.0 以上的版本中 WebMvcConfigurerAdapter 已经过时了
*
* 可以实现 WebMvcConfigurer 来达到相同的目的
*/
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
//addMapping是指定支持跨域的路径
registry.addMapping("/**")
//如果前端不带cookie过来或者不限制域访问的话,那么设置 .allowedOrigins("*") 即可
//.allowedOrigins("*")
.allowedOrigins("http://localhost:8001", "http://localhost:8002")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowCredentials(true);
}
}
/**
* crossDomain: true,这里代表使用跨域请求
*
* xhrFields: {withCredentials: true}
* 这样配置就可以把 cookie 带过去了,不然我们连 session 都没法维护
* 当然,如果你没有这个需求,也就不需要配置这个了
*
* 注意:如果配置了true 需要带cookie信息过去的话,后端的Access-Control-Allow-Origin不能配置成 *
* 需要配置为具体的 origin 源站
*/
function getCors() {
$.ajax({
type:"POST",
url:"http://localhost:8088/getCors/value.do",
dataType:"json",
crossDomain: true,
xhrFields: {
withCredentials: false
},
success:function (data) {
$("#userName").val(data.userName);
},
error:function () {
$("#userName").val("错误了");
}
});
}
CrossOrigin注解方式
除了Configuration的配置方式外,springMVC4.2之后的版本都支持注解的方式,可以使用@CrossOrigin
注解来实现
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {
//默认的Access-Control-Allow-Origin 是 '*'
@Deprecated
String[] DEFAULT_ORIGINS = new String[]{"*"};
//默认的Access-Control-Allow-Headers 是 ‘*’
@Deprecated
String[] DEFAULT_ALLOWED_HEADERS = new String[]{"*"};
//默认的Access-Control-Allow-Credentials 是false;即不允许发送cookie
@Deprecated
boolean DEFAULT_ALLOW_CREDENTIALS = false;
//默认在1800秒内,不需要发出第二次预检请求
@Deprecated
long DEFAULT_MAX_AGE = 1800L;
@AliasFor("origins")
String[] value() default {};
@AliasFor("value")
String[] origins() default {};
String[] allowedHeaders() default {};
String[] exposedHeaders() default {};
RequestMethod[] methods() default {};
String allowCredentials() default "";
long maxAge() default -1L;
}
/**
* CrossOrigin 注解可以作用在整个Controller上或者作用在方法上
* 作用在类上是:表示支持拦截整个controller的接口
* 作用在方法上是:表示支持拦截指定的接口
*
* 配置中value和origins等价,均为配置Access-Control-Allow-Origin
*/
@CrossOrigin(origins = {"http://localhost:8001", "http://localhost:8002"},
methods = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE},
allowCredentials = "true")
@RestController
@RequestMapping("/getCors")
public class CorsController {
@RequestMapping(value = "/value.do")
public CorsUser getCors() {
return new CorsUser("我是Cors");
}
}
HttpServletResponse方式
@RestController
@RequestMapping("/getCors")
public class CorsController {
@RequestMapping(value = "/value.do")
public CorsUser getCors(HttpServletResponse response) {
response.addHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
return new CorsUser("我是Cors");
}
}
JsonP和CORS的比较
JSONP
只能实现 GET 请求,而CORS
支持所有类型的 HTTP 请求- 使用 CORS 可以使用普通的 XMLHttpRequest 发起请求和获取数据,比 JSONP 有更好的错误处理
- CORS 的兼容性比不上 JSONP,一些比较老的浏览器只支持 JSONP