跨域&Cookie&Session
一、Cookie设置域名domain与跨域的问题说明
1、Cookie无法设置除当前域名或者其父域名之外的其他domain,这是因为浏览器出于对cookie的保护造成的(同源策略),也就是cookie无法跨域设置。
2、对于子域名有如下规则,当前域名只能设置当前域名以及当前域名的父(上级)域名,不能设置当前域名的子(下级)域名,例如在www.wo.cao.baidu.com域名下能设置 cao.baidu.com、baidu.com,不能设置da.jia.wo.cao.baidu.com的Cookie。
当给网站设置cookie时,大家有没有发现在网站的其他域名下也接收到了这些cookie。这些没用的cookie看似不占多少流量,但如果对一个日PV千万的站点来说,那浪费的资源就不是一点点了。因此在设置cookie时,对它的作用域要尽可能的设置准确。
举例:现在有如下3个域名,一个顶级域名、一个二级域名和一个三级域名
① zydya.com
②blog.zyday.com
③one.blog.zyday.com
- 首先在①zyday.com域名下设置cookie,做四次测试,分别设置domain参数为空、'zyday.com'、'blog.zyday.com'与'one.blog.zyday.com'。
√表示该域名下能取到cookie,×表示不能取到cookie
domain参数 | zydya.com | blog.zyday.com | one.blog.zyday.com |
---|---|---|---|
setcookie('name',1,time()+1) | √ | √ | √ |
setcookie('name',1,time()+1,'/','zyday.com') | √ | √ | √ |
setcookie('name',1,time() +1,'/','blog.zyday.com') | × | × | × |
setcookie('name',1,time() +1,'/','one.blog.zyday.com') | × | × | × |
结论:当domain设置为空,domain默认为当前域名,并且该域名下的子域名都可以接收到cookie。
但是domain参数设置其子域名时,所有域名就接收不到了,包括那个子域名。
- 然后在②blog.zyday.com域名下设置cookie,测试条件同上
domain参数 | zydya.com | blog.zyday.com | one.blog.zyday.com |
---|---|---|---|
setcookie('name',1,time() +1) | × | √ | √ |
setcookie('name',1,time()+1,'/','zyday.com') | √ | √ | √ |
setcookie('name',1,time()+1,'/','blog.zyday.com') | × | √ | √ |
setcookie('name',1,time()+1,'/',one.blog.zyday.com') | × | × | × |
注意看第二行,domain参数是zyday.com,是blog.zyday.com的父域名,那么zyday.com下所有子域名(包括zyday.com、blog.zyday.com、one.blog.zyday.com)都能接收到cookie。
结论:当domain为自身域名时,那么其父域名受影响,其本身与其子域名可以接收到cookie。
而设置其子域名或其他域名时,所有域名都将接收不到cookie。
- 最后在③one.blog.zyday.com域名下设置cookie
domain参数 | zydya.com | blog.zyday.com | one.blog.zyday.com |
---|---|---|---|
setcookie('name',1,time() +1) | × | × | √ |
setcookie('name',1,time()+1,'/','zyday.com') | √ | √ | √ |
setcookie('name',1,time()+1,'/','blog.zyday.com') | × | √ | √ |
setcookie('name',1,time()+1,'/',one.blog.zyday.com') | × | × | √ |
domain的设置,有两点要注意:
-
在setcookie中省略domain参数,那么domain默认为当前域名。
-
domain参数可以设置父域名以及自身,但不能设置其它域名,包括子域名,否则cookie不起作用。
Cookie的作用域:cookie的作用域是domain本身以及domain下的所有子域名。
二、SpringBoot项目解决跨域方案
- 非同源限制
浏览器为了安全性,限制了一些请求无法访问的非同源URL
- 无法读取非同源网页的Cookie、LocalStorage和IndexedDB
- 无法接触非同源网页的DOM
- 无法向非同源地址发送AJAX请求
- 如何解决跨域问题
服务端使用纯代码的方式逻辑解决跨域问题。
五种解决方式:
1. 返回新的CorsFilter(全局)
2. 重写WebMvcConfigurer(全局)
3. 使用注解@CrossOrigin(局部)
4. 手动设置响应头(HttpServletResponse)(局部)
注意:
CorsFilter、WebMvcConfigurer、@CrossOrigin需要SpringMVC4.2以上的版本才支持,对应SpringBoot1.3以上版本才支持。
如果使用了局部跨域会覆盖全局跨域规则,因此可以通过@CrossOrigin进行细粒度更高的跨域资源控制。
事实上无论哪种方式,最终目的都是修改响应头,向响应头中添加浏览器所要求的数据,进而实现跨域
- 配置CorsFilter(全局跨域)
import lombok.extern.slf4j.Slf4j;
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;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 跨域前后端配置类
**/
@Configuration
@Slf4j
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
// 1.添加CORS配置信息
CorsConfiguration config = new CorsConfiguration();
// 放行哪些原始域
config.addAllowedOrigin("*");
// 是否发送Cookie信息
config.setAllowCredentials(true);
// 放行哪些原始域(请求方式)
config.addAllowedMethod("*");
// 放行哪些原始域(头部信息)
config.addAllowedHeader("*");
// 暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
// config.addExposedHeader("*");
config.addExposedHeader("Content-Type");
config.addExposedHeader( "X-Requested-With");
config.addExposedHeader("accept");
config.addExposedHeader("Origin");
config.addExposedHeader("Access-Control-Request-Method");
config.addExposedHeader("Access-Control-Request-Headers");
// 2.添加映射路径
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
configSource.registerCorsConfiguration("/**", config);
// 3.返回新的CorsFilter.
return new CorsFilter(configSource);
}
}
如果使用的是高版本SpringBoot2.4.4则需要改动一下,否则后台报错
java.lang.IllegalArgumentException: When allowCredentials is true, allowedOrigins cannot contain the special value since that cannot be set on the "Access-Control-Allow-Origin" response header. To allow credentials to a set of origins, list them explicitly or consider using "allowedOriginPatterns" instead.
at org.springframework.web.cors.CorsConfiguration.validateAllowCredentials(CorsConfiguration.java:453) ~[spring-web-5.3.6.jar:5.3.6]
解决:把config.addAllowedOrigin("*");替换成config.addAllowedOriginPattern("*");
- 重写WebMvcConfigurer(全局跨域)
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 跨域前后端配置类
**/
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
//添加映射路径
registry.addMapping("/**")
//是否发送Cookie
.allowCredentials(true)
//设置放行哪些原始域,SpringBoot2.4.4下低版本使用.allowedOrigins("*")
.allowedOriginPatterns("*")
//放行哪些请求方式
.allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"})
//.allowedMethods("*") //或者放行全部
//放行哪些原始请求头部信息
.allowedHeaders("*")
//暴露哪些原始请求头部信息
.exposedHeaders("*");
}
}
- 使用注解@CrossOrigin(局部跨域)
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {
//这origins和value是一样的
//允许来源域名的列表,例如 'www.jd.com',匹配的域名是跨域预请求 Response 头中的'Access-Control-Aloow_origin'
//字段值。不设置确切值时默认支持所有域名跨域访问。
@AliasFor("origins")
String[] value() default {};
@AliasFor("value")
String[] origins() default {};
//高版本下Spring2.4.4使用originPatterns 而不是value 和 origins
String[] originPatterns() default {};
//跨域请求中允许的请求头中的字段类型, 该值对应跨域预请求 Response 头中的 'Access-Control-Allow-Headers' 字段值。
//不设置确切值默认支持所有的header字段(Cache-Controller、Content-Language、Content-Type、
//Expires、Last-Modified、Pragma)跨域访问
String[] allowedHeaders() default {};
//跨域请求请求头中允许携带的除Cache-Controller、Content-Language、Content-Type、Expires、Last-Modified、
//Pragma这六个基本字段之外的其他字段信息,对应的是跨域请求 Response 头中的 'Access-control-Expose-Headers'字段值
String[] exposedHeaders() default {};
//跨域HTTP请求中支持的HTTP请求类型(GET、POST...),不指定确切值时默认与 Controller 方法中的 methods 字段保持一致。
RequestMethod[] methods() default {};
//该值对应的是是跨域请求 Response 头中的 'Access-Control-Allow-Credentials' 字段值。
//浏览器是否将本域名下的 cookie 信息携带至跨域服务器中。默认携带至跨域服务器中,但要实现 cookie
//共享还需要前端在 AJAX 请求中打开 withCredentials 属性。
String allowCredentials() default "";
//该值对应的是是跨域请求 Response 头中的 'Access-Control-Max-Age' 字段值,表示预检请求响应的缓存持续的最大时间,
//目的是减少浏览器预检请求/响应交互的数量。默认值1800s。设置了该值后,浏览器将在设置值的时间段内对该跨域请求不再发起预请求
long maxAge() default -1;
}
- 在控制器(类上)使用@CrossOrigin注解,表示该类的所有方法允许跨域
@Controller
@RequestMapping("/shop")
@CrossOrigin(originPatterns = "*", methods = {RequestMethod.GET, RequestMethod.POST})
public class ShopController {
@GetMapping("/")
@ResponseBody
public Map<String, Object> findAll() {
//返回数据
return DataSchool.getStudents();
}
}
- 也可以设置更小的粒度,在方法上设置跨域
@Controller
@RequestMapping("/shop")
public class ShopController {
@GetMapping("/")
@ResponseBody
//更小的解决跨域 设置只能某些地址访问
@CrossOrigin(originPatterns = "http://localhost:8080")
public Map<String, Object> findAll() {
//返回数据
return DataSchool.getStudents();
}
}
三、JSONP跨域
【腾讯文档】JSONP跨域解决https://docs.qq.com/doc/DVEVTemdrcVVjSFlE
跨域问题是浏览器为了防御CSRF攻击(Cross-site request forgery跨站请求伪造)发生,避免恶意攻击而带来的风险而采取的同源策略限制。当一个页面中使用XMLHTTPRequest(XHR请求,也既AJAX请求)对象发送HTTP请求时,必须保证当前页面和请求的对象是同源的,即协议、域名和端口号要完全一致,否则浏览器就会阻止此跨域请求返回的数据。
举例:
http://www.a.com 与 https://www.a.com 是不同源的,它们协议不同(跨域)
http://www.a.com 与 http://www.b.com 是不同源的,它们域名不同(跨域)
http://www.a.com:80 与 http://www.a.com:8080 是不同源的,它们端口号不同(跨域)
http://www.a.com/test1.js 与 http://www.a.com/test2.js 是同源的(不跨越)
1、基本介绍
1.1 什么是JSONP
Jsonp(JSON with Padding) 是 json 的一种"使用模式",可以让网页从别的域名(网站)那获取资料,即跨域读取数据。为什么我们从不同的域(网站)访问数据需要一个特殊的技术(JSONP )呢?这是因为同源策略。
1.2 什么是跨域
是指一个域(网站)下的文档或脚本试图去请求另一个域(网站)下的资源
1.3 什么是同源策略
同源策略/SOP(Same origin policy)是一种约定,由 Netscape 公司 1995 年引入浏览器,它是浏览器最核心也最基本的安全功能,现在所有支持 JavaScript 的浏览器都会使用这个策略。如果缺少了同源策略,浏览器很容易受到 XSS、CSFR 等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个 ip 地址,也非同源
2、跨域问题产生
- 服务端代码
- 客户端(浏览器)代码
从上面的案例可以看出我们服务端的协议和ip都是一样的,但是唯独端口不一样,这就导致跨域问题;一旦协议、IP、端口有任何一个不一样都会造成跨域
3、服务端解决跨域
其实服务端解决跨域特别简单,但是极不推荐,只要在服务端请求的资源里面添加一个响应头,并设置任何访问都可以被受理并发送
此时执行客户端代码就会发现不会报错跨域问题错误了,在前端的Ajax调用之后加一个打印会发现可以把数据获取到,但并不推荐这么写
4、其它方式解决跨域
之前的的Ajax不能实现跨域访问资源,那怎么办呢?HTML标签中的img、link、script这3个标签是可以实现跨域的,如在项目中的img使用网络图片,或者使用link引用网络上的样式,还有script,可以看出以前写CDN网络引用Jquery、Vue等
一般选择使用script来解决跨域,因为它可以解析如函数等一些方法,后面详细介绍
4.1 利用Script标签解决跨域
浏览器只对XHR(XMLHttpRequest)请求有同源请求限制,而对script标签src属性、link标签ref属性和img标签src属性没有这种限制,利用这个“漏洞”就可以很好的解决跨域请求。JSONP就是利用了script标签无同源限制的特点来实现的,当向第三方站点请求时,就可以将此请求放在<script>标签的src属性里,这就如同请求一个普通的JS脚本,可以自由的向不同的站点请求
这种方式创建一个script标签确实是创建并在响应体获取到数据,但是怎么获取数据并为当前页面使用呢?
对Script请求跨域携带参数:
script默认会解析js语法的,在服务端发送之前把数据整理成JS函数方式,发送回来正好在JS里面调用此函数不就可以获取到数据了吗
- 服务器代码
- 客户端代码
JSONP 的优点是:它不像 XMLHttpRequest 对象实现的 Ajax 请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都 可以运行,不需要 XMLHttpRequest 或ActiveX 的支持;并且在请求完毕后可以通过调用 callback 的方式回传结果。
JSONP 的缺点则是:它只支持 GET 请求而不支持 POST 等其它类型的 HTTP 请求;它只支持跨域 HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。
5、Jquery完成跨域问题
四、跨域session同步问题
跨域来源:(前端站点和后端API布署到不同的站点)
解决方案
一.服务端设置
1.配置允许跨域请求
public class BaseAction {
/**
* 支持跨域请求
*/
protected void crossComain(HttpServletRequest request,HttpServletResponse response){
String[] allowDomains = {"http://www域名1","http://www域名2"};
Set allowOrigins = new HashSet(Arrays.asList(allowDomains));
String originHeads = request.getHeader("Origin");
if(allowOrigins.contains(originHeads)){
//配置跨域访问的最简单的方式是用通配符 * ,(就是不安全,所有的请求都能跨域)
response.setHeader("Access-Control-Allow-Origin", originHeads);
response.setHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS,DELETE,HEAD,PUT,PATCH");
response.setHeader("Access-Control-Max-Age", "36000");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept,Authorization,authorization");
response.setHeader("Access-Control-Allow-Credentials","true");//保持跨域 Ajax 时的 Cookie
}
}
}
2.接口调用跨域方法
/**
* 测试
*/
@Controller
public class Test extends BaseAction{
@RequestMapping("Test")
public void test(HttpServletRequest request,HttpServletResponse response){
//调用允许跨域方法,此接口支持跨域请求
crossComain(request, response);
System.out.println("支持跨域请求");
}
}
二. 客户端配置
1.因为在默认情况下,跨源请求不提供凭据(cookie、HTTP认证及客户端SSL证明等)。通过将withCredentials属性设置为true,可以指定某个请求应该发送凭据。如果服务器接收带凭据的请求,会用下面的HTTP头部来响应。
/**
* ajax post 跨域 request
* @param {Object} url 请求地址
* @param {Object} data 请求参数
* @param {Object} success 成功回调
* @param {Object} async 是否同步 false:同步 ture:异步
*/
function ajax_cross(url,data,success,async){
$.ajax({
type:"post",
url:url,
async:async,
data:data,
dataType:"json",
xhrFields:{
withCredentials:true
},
crossDomain:true,
success:success
});
}
2.调用分装好的ajax方法:如下
$(function(){
Test();
});
//测试
function Test(){
//跨域请求
ajax_cross("/Test",{"data":"postData"},function success(result){
console.log(result);
},false);
}
node服务中跨域解决方法
https://blog.csdn.net/qq_43437874/article/details/120565996