springboot解决谷歌80及以上版本的SameSite设置cookie失效

前言

谷歌80新加了一个SameSite属性,防止跨域。但是就是由于这个新加的属性,我无法把cookie传到前端,搜罗了全网找到了两种解决方法,在此记录一下。
如果console出现下面这个,那cookie估计就没设置上:
在这里插入图片描述

A cookie associated with a cross-site resource at http://stu.hrbkyd.com/ was set without the SameSite attribute. It has been blocked, as Chrome now only delivers cookies with cross-site requests if they are set with SameSite=None and Secure. You can review cookies in developer tools under Application>Storage>Cookies and see more details at https://www.chromestatus.com/feature/5088147346030592 and https://www.chromestatus.com/feature/5633521622188032.

先看一下能正常设置cookie的request和response

addCookie的response:
在这里插入图片描述
getCookie的request:
在这里插入图片描述
可以看出添加cookie的response会有 Set-Cookie 字段,getCookie的request会有 Cookie 字段,有这两个字段才能添加成功。
下面来看springboot下的操作。

方法一

@GetMapping("/addCookie3")
    @ResponseBody
    public String addCookie3(@RequestParam("name") String name,@RequestParam("value") String value,HttpServletRequest request,HttpServletResponse response) {
        System.out.println(name+" "+value);
        Cookie cookie = new Cookie(name,value);
        cookie.setDomain(domain);//域名,xxx.com
        cookie.setHttpOnly(false);
        cookie.setPath(request.getContextPath());
        cookie.setMaxAge(60*60*24);
        response.addCookie(cookie);
        String s = name+"="+value+";";
        response.setHeader("Set-Cookie",s + "Path=*; SameSite=None; Secure");
        return "success";
    }
    @GetMapping("/getCookie")
    @ResponseBody
    public String getCookie(@RequestParam("name") String name,HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if (cookies == null) {
            return "no cookies";
        }
        for (Cookie cookie : cookies) {
            System.out.println(cookie.getName()+" "+cookie.getValue());
        }
        for (Cookie cookie : cookies) {
            if (cookie.getName().equals(name)) {
                return cookie.getValue();
            }
        }
        return "no such cookie";
    }
效果图:

说明:测试用页面的两个set按钮分别是添加cookie,一个为添加demoData=demoData,另一个为demoData2=damoData2,两个get是没别获取demoData和demoData2.

添加demoData:

在这里插入图片描述
可以看出response的Set-Cookie字段存在值。这里我的SameSite设置为了None,这样就必须再设置一个属性Secure,并且用https连接,这里是迫不得已,还可以设置为SameSite=Lax,这样就不用设置Secure,但是这样我无法get到值,所以采用None搭配Secure。

得到demoData:

在这里插入图片描述
本次我们发送的request也存在之前设置的cookie,并且也取到了值。
如下:
在这里插入图片描述
现在我们添加了一个cookie是成功了,这时我就在想,上面的代码是拼接的Set-Cookie字段,那么设置下一个cookie时要不要把之前的cookie也获取到,然后再拼接上新cookie呢?
比如说在设置一个demoData2=demoData2,那么这次用不用写成
response.setHeader("Set-Cookie","demoData=demoData;demoData2=demoData" + "Path=*; SameSite=None; Secure");
其实是不用的。这里在测试一下就可以,我们点击第二个set按钮,看看发生什么。

添加demoData2

在这里插入图片描述
可以看出,request发送时候带有demoData=demoData,response我们并没有像上面那么拼接,只拼接了demoData2,但是response自动带上了demoData。

得到demoData2

在这里插入图片描述
全部结果如下
在这里插入图片描述
成功的取到了demoData2.
下面来看方法二

方法二

get方法是一样的,这里只贴出来addCookie

@GetMapping("/addCookie2")
    @ResponseBody
    public String addCookie2(@RequestParam("name") String name,@RequestParam("value") String value,HttpServletRequest request,HttpServletResponse response) {
        System.out.println(name+" "+value);
        //new
        HttpCookie cookie = CookieUtils.generateSetCookie(request, name, value,Duration.ofHours(24 * 7));//七天过期
        response.setHeader(HttpHeaders.SET_COOKIE, cookie.toString());
        return "success";
    }

下面是CookieUtils

public class CookieUtils {
    private final static String domain = "xxx.com";
    public static HttpCookie generateSetCookie(HttpServletRequest request, String name, String value,Duration duration){
        ResponseCookie cookie = ResponseCookie.from(name, value) // key & value
                .secure(true)		// 在https下传输,配合none使用
                .domain(domain)// 域名
                .path("*")			// path
                .maxAge(duration)	// 过期时间
                .sameSite("None")	
                .build()
                ;
        return cookie;
    }
}

具体情况和细节与方法一是一样的。

deleteCookie

有了添加的方法,删除就不难了,只需要把有效时间变为0就行。下面采用方法2来删除cookie。

@GetMapping("/deleteCookie")
    @ResponseBody
    public String deleteCookie(String names, HttpServletRequest request,HttpServletResponse response) {
        System.out.println("names:"+names);
        Cookie[] cookies = request.getCookies();
        if (cookies == null) {
            return "no cookies";
        }
        for (int i = 0; i < cookies.length; i++) {
            if (names.equals(cookies[i].getValue())) {
                ResponseCookie cookie = (ResponseCookie)CookieUtils.generateSetCookie(request,names,null,Duration.ZERO);//有效时间为0
                response.setHeader(HttpHeaders.SET_COOKIE, cookie.toString());
                break;
            }
        }
        return "success";
    }

设置session

cookie整明白了,session就好设置了,因为session是通过Set-Cookie的JSESSIONID设置的,只需要获取到JSESSIONID就行。

@GetMapping("/setSession")
    @ResponseBody
    public Result setSession(String str,HttpSession session,HttpServletRequest request,HttpServletResponse response) {
        System.out.println(str);
        session.setAttribute("str",str);
        String id = session.getId();
        HttpCookie cookie = CookieUtils.generateSetCookie(request, "JSESSIONID", id, Duration.ofHours(3));
        response.setHeader(HttpHeaders.SET_COOKIE, cookie.toString());
        Result result = new Result();
        result.setCode(HttpStatus.OK.value());
        return result;
    }

最后附上测试用前端代码

注意如果设置SameSite=None,必须设置Secure,并且用https传输。如果是SameSite=Lax,则没有前面要求。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.20.0-0/axios.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
</head>
<body>
<button id="btn1">按钮1(set)</button>
<button id="btn2">按钮2(get)</button>
<button id="btn3">按钮3(set)</button>
<button id="btn6">按钮6(get)</button>
<button id="btn4">按钮4(delete1)</button>
<button id="btn5">按钮4(delete2)</button>
<script>


    let config = {headers: {'Content-Type': 'application/x-www-form-urlencoded'}};
    let domain = "localhost"
    $(function () {
        axios.defaults.withCredentials = true;
        $("#btn1").click(function () {
            axios.get('https://xxx.com/test/addCookie?name=demoData&value=demoData').then(res => {
                console.log(res.data);
            });
        });

        $("#btn2").click(function () {
            axios.get('https://xxx.com/test/getCookie?name=demoData').then(res => {
                console.log(res.data);
            });
        })

        $("#btn3").click(function () {
            axios.get('https://xxx.com/test/addCookie?name=demoData2&value=demoData2').then(res => {
                console.log(res.data);
            });
        });

        $("#btn4").click(function () {
            axios.get('https://xxx.com/test/deleteCookie?names=demoData').then(res => {
                console.log(res.data);
            });
        });

        $("#btn5").click(function () {
            axios.get('https://xxx.com/test/deleteCookie?names=demoData2').then(res => {
                console.log(res.data);
            });
        });

        $("#btn6").click(function () {
            axios.get('https://xxx.com/test/getCookie?name=demoData2').then(res => {
                console.log(res.data);
            });
        })
    })

</script>
</body>
</html>

后记

测试时候发现,/user下面的session会保存成功,但是到了/orderForm时,不会自动加上session,会在生成一个session,查看cookie就能发现有两个JSESSION,但是路径不一样。我的处理方式是在cookieUtil里面设置路径的path设置为"/"。

public static HttpCookie generateSetCookie(HttpServletRequest request, String name, String value,Duration duration) {
        ResponseCookie cookie = ResponseCookie.from(name, value) // key & value
                .secure(true)		// 在https下传输,配合none使用
                .domain(domain)// 域名
                .path("/")			// path
                .maxAge(duration)	// 过期时间
                .sameSite("None")	// 大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外Lax或者none
                .build()
                ;
        return cookie;
    }
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值