推荐阅读:
跨域访问是指请求一个与自身资源不同源(不同的域名、协议或端口)的资源。
CORS
浏览器出于安全考虑设置了同源策略,限制从脚本内发起跨域请求。但是实际应用中,往往常会发生跨域访问。因为,W3C提供了一个标准的跨域解决方案:跨域资源共享(Cross-Origin Resource Sharing),以支持安全的跨域请求和数据传输。
浏览器将CORS请求分为以下两类:
- 简单请求
- 预检请求:防止资源被本来没有权限的请求修改的保护机制。浏览器会在发送实际请求之前使用
OPTIONS
方法发送一个预检请求,从而获知服务器端是否允许该跨域请求。服务端确认允许后,才会发起实际的HTTP请求。
简单请求
满足以下条件的,属于简单请求:
- 请求方法是下面三种方法之一:
- HEAD
- GET
- POST
- HTTP头信息不超过以下几种字段:
- Cache-Control
- Content-Language
- Content-Type
- 只限于三种:text/plain,multipart/form-data,application/x-www-form-urlencoded
- Expires
- Last-Modified
- Pragma
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
预检请求
非简单请求为预检请求,会在正式通信之前触发一个OPTIONS
请求进行预检。
预检请求会在请求头中附带一些正式请求的信息给服务端,主要有:
- origin:请求源信息;
- Access-Control-Request-Method:接下来的请求类型,如POST,GET等;
- Access-Control-Request-Headers:接下来的请求中包含的用户显式设置的Header列表。
服务端接收请求后,会根据上述附带的信息判断是否允许跨域,通过响应头返回对应的信息:
- Access-Control-Allow-Origin:允许跨域的 Origin 列表;
- Access-Control-Allow-Methods:允许跨域的方法列表;
- Access-Control-Allow-Headers:允许跨域的 Header 列表;
- Access-Control-Expose-Headers:允许暴露的 Header 列表;
- Access-Control-Max-Age:最大的浏览器缓存时间,单位:秒;
- Access-Control-Allow-Credentials:是否允许发送 Cookie。
浏览器会根据返回的CORS信息判断是否发送真实的请求。以上行为都是浏览器自动完成的,服务端只需配置特定的CORS规则。
如何解决跨域访问问题?
最直接的方式:
设置为相同的域名、端口
添加跨域头
让服务器告诉浏览器:允许跨域(返回Access-Control-Allow-Origin响应头)
网关支持(nginx)
# 跨域配置
location ^~ /api/ {
proxy_pass http://127.0.0.1:8080/api/;
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers '*';
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
}
将之前的反向代理删掉:
将配置复制进去并保存:
修改后端项目
- 在控制器类上添加
@CrossOrigin
注解
@RestController
@RequestMapping("/user")
@CrossOrigin(origins = {"http://192.168.31.78:3000"}, allowCredentials = "true", maxAge = 3600)
public class UserController {
- 配置全局请求拦截器
@Configuration
public class WebMvcConfg implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
//设置允许跨域的路径
registry.addMapping("/**")
//设置允许跨域请求的域名
//当**Credentials为true时,**Origin不能为星号,需为具体的ip地址【如果接口不带cookie,ip无需设成具体ip】
.allowedOrigins("http://localhost:9527", "http://127.0.0.1:9527", "http://127.0.0.1:8082", "http://127.0.0.1:8083")
//是否允许证书 不再默认开启
.allowCredentials(true)
//设置允许的方法
.allowedMethods("*")
//跨域允许时间
.maxAge(3600);
}
}
- 定义新的
CorsFilter
@Configuration
public class CorsConfig {
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.setMaxAge(3600L);
corsConfiguration.setAllowCredentials(true);
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig());
return new CorsFilter(source);
}
}
推荐阅读:SpringBoot设置Cors跨域的四种方式
配置好了后访问测试:
IE浏览器成功登录进入主页;
Chrome浏览器遇到问题了,跨站登录失效:
发现出问题了!登录成功后浏览器没有保存Cookie(IE可以正常访问)
黄色三角警告信息:
This Set-Cookie was blocked because it had the “Secure” attribute but was not received over a secure connection.
查阅相关资料,大概就是因为安全问题。浏览器迭代升级,secure表示Cookie的安全传输要求,当设置secure参数时,浏览器只会在使用HTTPS加密连接时将Cookie发送到服务器;samesite设置为none的意思是允许跨站点发送 Cookie,但是Chrome要求必须使用HTTPS传输Cookie,那就得考虑SSL证书了。简单了解了一下相关内容,网络安全还得多多学习!(不同浏览器、不同版本,相关默认参数有区别)
解决跨站Cookie SameSite限制(跨站登录失效)
跨站:eTLD+1(有效顶级域名+二级域名)不同
推荐阅读:
解决方式?
- 前后端地址不跨站
HTTPS
+SameSite=None
后端application.yml
server:
servlet:
session:
cookie:
same-site: none
secure: true
这样配置后使用火狐可以访问,但是Chrome和IE不行,再加上SSL证书应该能实现所有用户访问。目前没有配置SSL证书
推荐阅读: