跨域&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的设置,有两点要注意:

  1. 在setcookie中省略domain参数,那么domain默认为当前域名。

  2. domain参数可以设置父域名以及自身,但不能设置其它域名,包括子域名,否则cookie不起作用。

  Cookie的作用域:cookie的作用域是domain本身以及domain下的所有子域名。

参考文章



二、SpringBoot项目解决跨域方案

  • 非同源限制

  浏览器为了安全性,限制了一些请求无法访问的非同源URL

  1. 无法读取非同源网页的Cookie、LocalStorage和IndexedDB
  2. 无法接触非同源网页的DOM
  3. 无法向非同源地址发送AJAX请求
  • 如何解决跨域问题

  服务端使用纯代码的方式逻辑解决跨域问题。

五种解决方式:
1. 返回新的CorsFilter(全局)
2. 重写WebMvcConfigurer(全局)
3. 使用注解@CrossOrigin(局部)
4. 手动设置响应头(HttpServletResponse)(局部)
注意:
CorsFilter、WebMvcConfigurer、@CrossOrigin需要SpringMVC4.2以上的版本才支持,对应SpringBoot1.3以上版本才支持。
如果使用了局部跨域会覆盖全局跨域规则,因此可以通过@CrossOrigin进行细粒度更高的跨域资源控制。
事实上无论哪种方式,最终目的都是修改响应头,向响应头中添加浏览器所要求的数据,进而实现跨域
  1. 配置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("*");

  1. 重写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("*");
  }
}
  1. 使用注解@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

img

  跨域问题是浏览器为了防御CSRF攻击(Cross-site request forgery跨站请求伪造)发生,避免恶意攻击而带来的风险而采取的同源策略限制。当一个页面中使用XMLHTTPRequest(XHR请求,也既AJAX请求)对象发送HTTP请求时,必须保证当前页面和请求的对象是同源的,即协议、域名和端口号要完全一致,否则浏览器就会阻止此跨域请求返回的数据。

举例:

http://www.a.comhttps://www.a.com 是不同源的,它们协议不同(跨域)

http://www.a.comhttp://www.b.com 是不同源的,它们域名不同(跨域)

http://www.a.com:80http://www.a.com:8080 是不同源的,它们端口号不同(跨域)

http://www.a.com/test1.jshttp://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、跨域问题产生

  • 服务端代码

img

  • 客户端(浏览器)代码

img

img

从上面的案例可以看出我们服务端的协议和ip都是一样的,但是唯独端口不一样,这就导致跨域问题;一旦协议、IP、端口有任何一个不一样都会造成跨域

3、服务端解决跨域

其实服务端解决跨域特别简单,但是极不推荐,只要在服务端请求的资源里面添加一个响应头,并设置任何访问都可以被受理并发送
img

此时执行客户端代码就会发现不会报错跨域问题错误了,在前端的Ajax调用之后加一个打印会发现可以把数据获取到,但并不推荐这么写

img

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脚本,可以自由的向不同的站点请求

img

这种方式创建一个script标签确实是创建并在响应体获取到数据,但是怎么获取数据并为当前页面使用呢?

对Script请求跨域携带参数:

script默认会解析js语法的,在服务端发送之前把数据整理成JS函数方式,发送回来正好在JS里面调用此函数不就可以获取到数据了吗

  • 服务器代码

img

  • 客户端代码

img

JSONP 的优点是:它不像 XMLHttpRequest 对象实现的 Ajax 请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都 可以运行,不需要 XMLHttpRequest 或ActiveX 的支持;并且在请求完毕后可以通过调用 callback 的方式回传结果。

JSONP 的缺点则是:它只支持 GET 请求而不支持 POST 等其它类型的 HTTP 请求;它只支持跨域 HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。

5、Jquery完成跨域问题

img



四、跨域session同步问题

  跨域来源:(前端站点和后端API布署到不同的站点)

img

解决方案

一.服务端设置

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

参考文章

解决跨域session 同步问题
跨域问题(纯前端解决,纯后端解决,webservice解决)

posted @ 2022-02-03 00:41  黄河大道东  阅读(728)  评论(0编辑  收藏  举报