Cors跨域(二):实现跨域Cookie共享的三要素(下)

简介: Cors跨域(二):实现跨域Cookie共享的三要素(下)

代码模拟跨域Cookie共享


前端页面:发送跨域请求,为了方便模拟这里发送跨域的简单请求即可(还不知道什么叫简单请求?戳这里


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Cookie交互机制(跨域)</title>
    <!--导入Jquery-->
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
</head>
<body>
<button id="btn">Cookie交互机制(跨域)</button>
<div id="content"></div>
<script>
    $("#btn").click(function () {
        $.get("http://localhost:8080/corscookie");
    });
</script>
</body>
</html>


前端页面托管在本地的63342端口上:http://localhost:63342/...


后端代码:后端接口托管在8080端口上:http://localhost:8080/...


这就是最简单的一个跨域场景,两个域具有相同的domain,因此才有共享Cookie的可能。


/**
 * 在此处添加备注信息
 *
 * @author YourBatman. <a href=mailto:yourbatman@aliyun.com>Send email to me</a>
 * @site https://yourbatman.cn
 * @date 2021/6/9 10:36
 * @since 0.0.1
 */
@Slf4j
@WebServlet(urlPatterns = "/corscookie")
public class CorsCookieServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String requestURI = req.getRequestURI();
        String method = req.getMethod();
        String originHeader = req.getHeader("Origin");
        log.info("收到请求:{},方法:{}, Origin头:{}", requestURI, method, originHeader);
        // 读取Cookie
        List<Cookie> myCookies = new ArrayList<>();
        if (req.getCookies() != null) {
            myCookies = Arrays.stream(req.getCookies()).filter(c -> c.getName().equals("name") || c.getName().equals("age")).collect(toList());
        }
        if (myCookies.isEmpty()) { // 种植Cookie
            Cookie cookie = new Cookie("name", "YourBatman");
            // cookie.setDomain("baidu.com");
            cookie.setMaxAge(3600);
            resp.addCookie(cookie);
            cookie = new Cookie("age", "18");
            cookie.setMaxAge(3600);
            resp.addCookie(cookie);
        } else {
            myCookies.stream().forEach(c -> {
                log.info("name:{} value:{} domain:{} path:{} maxAge:{} secure:{}", c.getName(), c.getValue(), c.getDomain(), c.getPath(), c.getMaxAge(), c.getVersion(), c.getSecure());
            });
        }
        setCrosHeader(resp);
        resp.getWriter().write("hello cookie...");
    }
    private void setCrosHeader(HttpServletResponse resp) {
        resp.setHeader("Access-Control-Allow-Origin", "http://localhost:63342");
    }
}


点击按钮,发送请求:


image.png


image.png



注意看,服务端代码虽然resp.addCookie(cookie);添加了Cookie,但是Response响应里并没有Set-Cookie这个头哦。查看浏览器发现木有Cookie:


image.png


也许你会说,当然没有啦,因为Response里没有Set-Cookie头嘛,但我们代码里明明已经addCookie了呀。


这半截理论当然没问题,现在我在服务端程序里补充一个响应头:

 private void setCrosHeader(HttpServletResponse resp) {
     resp.setHeader("Access-Control-Allow-Origin", "http://localhost:63342");
     resp.setHeader("Access-Control-Allow-Credentials", "true");
 }


重启服务端应用),再次发送请求,响应如下:


image.png


可以看到响应中已经有Set-Cookie响应头了,再次查看Cookie是否已被浏览器保存,同样的比比脸还干净:



image.png


浏览器没有存储Cookie。What?难道翻车了?No,下面教你如何解释以及怎么破?


跨域Cookie共享的关键点


这里要讨论的是跨域中Cookie的存储问题:默认情况下,浏览器是不会去为你保存下跨域请求响应的Cookie的。具体现象是:跨域请求的Response响应了即使有Set-Cookie响应头(且有值),浏览器收到后也是不会保存此cookie的。


要实现Cookie的跨域共享,有3个关键点:


  1. 服务端负责在响应中将Set-Cookie发出来(由Access-Control-Allow-Credentials响应头决定)
  2. 浏览器端只要响应里有Set-Cookie头,就将此Cookie存储(由异步对象的withCredentials属性决定)
  3. 浏览器端发现只要有Cookie,即使是跨域请求也将其带着(由异步对象的withCredentials属性决定)


为了满足这三个关键点,在实施层面就有三要素来指导我们开发来解决此类问题。


跨域Cookie共享的三要素


首先确保服务端能正确的在响应中有Set-Cookie响应头,这由Access-Control-Allow-Credentials: true来保证。因此服务端只需要做多加这一步即可:


resp.setHeader("Access-Control-Allow-Credentials", "true");


Access-Control-Allow-Credentials该头是可选的,是个bool值,它若为true就有两个作用:


  • 在跨域请求的响应中允许Set-Cookie响应头
  • 浏览器收到响应后,浏览器根据此头判断是否让自己的withCredentials属性生效


所以就来到了第二个要素:XMLHttpRequest对象的withCredentials属性。该属性是一个Boolean类型,它指示了是否该使用类似cookies,authorization headers(头部授权)或者TLS客户端证书这一类资格证书来创建一个跨站点访问控制(cross-site Access-Control)请求。


var xhr = new XMLHttpRequest();
...
xhr.withCredentials = true;


Jquery的Ajax写法与此不同,但底层原理一样


官方的语言理解起来总是那么晦涩,翻译成人话:当异步对象设置了withCredentials=true时,浏览器会保留下响应的Cookie等信息,并且下次发送请求时将其携带。因此要指示浏览器存储Cookie并且每次跨域请求都携带,仅需加上此参数即可:


$.ajax({
    url: "http://localhost:8080/corscookie",
    type: "GET",
    xhrFields: {
        withCredentials: true
    },
    crossDomain: true
});


以上两个要素完成后,影响“结果”的还有最后一个要素。这个要素比较隐晦,也是很多同学/文章忽略的点。


服务端的Access-Control-Allow-Origin这个响应头的值不能是通配符*,而只能是具体的值。否则出现报错:



image.png



换句话讲:浏览器端跨域请求对象一旦开启withCredentials=true属性,服务端跨域Origin将不能再用*通配符,否则CORS error!


三要素都满足后(Access-Control-Allow-Credentials:true;Access-Control-Allow-Origin:http://localhost:63342;withCredentials=true),再次点击发送请求,结果如下:


image.png


image.png


image.png


完美。


总结


上篇文章对Cors进行了全面介绍,本文以跨域Cookie共享为场景,很好的对跨域知识点进行了补充,并且也补足了Cors里一个重要的响应头Access-Control-Allow-Credentials的解释,相信通过本文同学你能加深对Web中Cookie的了解,以及跨域情况下Cookie信息如何共享。


本系列下篇将着眼于跨域请求解决方案的阐述,欢迎关注。


本文思考题

本文已被https://yourbatman.cn收录。所属专栏:点拨-Cors跨域,后台回复“专栏列表”即可查看详情。


看完了不一定懂,看懂了不一定会。来,3个思考题帮你复盘:


  1. Access-Control-Allow-Origin值设置为通配符*是万金油吗?
  2. 如何通过Cookie技术实现SSO单点登录?
  3. 实现跨域Cookie共享的三要素是什么?
相关文章
|
2月前
|
JSON 安全 前端开发
浅析CORS跨域漏洞与JSONP劫持
浅析CORS跨域漏洞与JSONP劫持
89 3
|
13天前
|
开发框架 中间件 Java
如何处理跨域资源共享(CORS)的 OPTIONS 请求?
处理 CORS 的 OPTIONS 请求的关键是正确设置响应头,以告知浏览器是否允许跨域请求以及允许的具体条件。根据所使用的服务器端技术和框架,可以选择相应的方法来实现对 OPTIONS 请求的处理,从而确保跨域资源共享的正常进行。
|
13天前
|
JavaScript 前端开发 API
跨域资源共享(CORS)的工作原理是什么?
跨域资源共享(CORS)通过浏览器和服务器之间的这种交互机制,在保证安全性的前提下,实现了跨域资源的访问,使得不同源的网页能够合法地获取和共享服务器端的资源,为现代Web应用的开发提供了更大的灵活性和扩展性。
|
27天前
|
JSON 前端开发 安全
CORS 是什么?它是如何解决跨域问题的?
【10月更文挑战第20天】CORS 是一种通过服务器端配置和浏览器端协商来解决跨域问题的机制。它为跨域资源共享提供了一种规范和有效的方法,使得前端开发人员能够更加方便地进行跨域数据交互。
|
1月前
|
缓存 前端开发 应用服务中间件
CORS跨域+Nginx配置、Apache配置
CORS跨域+Nginx配置、Apache配置
145 7
|
1月前
axios允许跨域cookie
axios允许跨域cookie
32 3
|
2月前
|
安全
CORS 跨域资源共享的实现原理
CORS 跨域资源共享的实现原理
|
16天前
|
存储 安全 搜索推荐
理解Session和Cookie:Java Web开发中的用户状态管理
理解Session和Cookie:Java Web开发中的用户状态管理
40 4
|
19天前
|
存储 缓存 网络协议
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点,GET、POST的区别,Cookie与Session
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点、状态码、报文格式,GET、POST的区别,DNS的解析过程、数字证书、Cookie与Session,对称加密和非对称加密
|
1月前
|
缓存 Java Spring
servlet和SpringBoot两种方式分别获取Cookie和Session方式比较(带源码) —— 图文并茂 两种方式获取Header
文章比较了在Servlet和Spring Boot中获取Cookie、Session和Header的方法,并提供了相应的代码实例,展示了两种方式在实际应用中的异同。
159 3
servlet和SpringBoot两种方式分别获取Cookie和Session方式比较(带源码) —— 图文并茂 两种方式获取Header
下一篇
无影云桌面