欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

过滤器丢失Cookie的解决方案

程序员文章站 2022-05-10 13:08:48
...

过滤器丢失Cookie的解决方案

今天碰到一个问题迟迟未能解决
本意在过滤器中拦截每一个请求,通过获取到的Cookie 信息进行强校验,但获取结果一直未空

目录


  • 首先我们要知道什么是Cookie?它是怎么产生的

首先我们要了解什么是Cookie,什么时候产生
官网是这么介绍的,一个HTTP cookie的(网络Cookie,浏览器cookie)是一小片数据的一个服务器发送到用户的网络浏览器。浏览器可以存储它并将其与下一个请求一起发送回同一服务器。通常,它用于判断两个请求是否来自同一个浏览器 - 例如,保持用户登录。它记住无状态 HTTP协议的有状态信息。

简单来说,cookie就是访问网站时生成的缓存,用于记录用户的信息,就比如你吃过的好吃的,你还想在吃。
这里不细说,详细cookie了解 可参考官网。

因为本次重点问题在于Cookie,
下列掺杂关于部分setCookie 以及获取Cookie 的代码。

代码块

当前set Cookie 的代码如下

//在当前项目中 创建Cookie 以及录入Cookie的键及value   
//当前token 为 缓存在redis 中用户数据的 key
private String setAccountVoCookie(AccountVo accountVo, HttpServletResponse response, String cookieKey) {
        if (accountVo!= null) {
            String token = UUID.randomUUID().toString().replace("-","");
            //创建一个Cookie,cookie的名字为 agency_login
            Cookie cookie = new Cookie(cookieKey, token);
            cookie.setHttpOnly(true);
            cookie.setPath("/");
            cookie.setMaxAge(-1);
            cookie.setSecure(false);
            response.addCookie(cookie);
            //将cookie 对象添加到response 对象中,这样服务器在输出response对象中的内容时
            //就会把cookie也也输出到客户端游览器中 所以在下方中我们通过request取cookie
            return token;
        }
        return "";
    }

当前getCookie 的代码如下

//通过cacheId 获取 Cookie里的值 (当前token)
//我们通过token 获取reids 中用户的 预存的信息
public static String getCacheId(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        String cacheId = null;
        logger.debug("===>cookies: {}", cookies);
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                logger.info("cookie, {}={}", cookie.getName(), cookie.getValue());
                if (StringUtils.equals("login", cookie.getName())) {
                    cacheId = cookie.getValue();
                }
            }
        }
        return cacheId;
    }

问题分析

在当前项目中,因为要校验每次请求的当前用户的角色。因此,我们需要拿到在登录时,存入response的 Cookie,
并且通过 Cookie的值 获取缓存 与 Redis 中的用户数据进行身份校验。
而当在过滤器中获取当前 request 请求的Cookie 数据时,却始终为空 , 起初认为过滤器在请求之间便已进行拦截,因此无法获取Cookie值。

这是因为 Cookies 要在请求提交过后才会生效,它无法同时读写。
因为我们 cookie 对象是存入response对象中,服务器在输出response对象中的内容时,
才会将cookie值输出到客户端游览器中,这样我们即时在request中取cookie时,cookie自然是为空。

最后找到创建Cookie 时,发现 它是在第一次登陆时便已经将cookie写入response中,而每次当前操作Request 并不属于当前一次客户端请求,因此并不是 即存即写问题。

最后在游览器控制台找到原因,当每次发出请求时
前端通过POST方式访问后端的REST接口时,发现两条请求记录,一条请求的Request MethodOptions,另一条请求的Reuest MethodPost。 

当options 进入过滤器后,因为它是伪请求,不携带任何数据,因此  在获取cookies的过程中,始终为空。

Options 分析:

简单来说, Options 预请求 就是用于检查请求请求通道是否安全
它类属与复杂请求 。
度娘一把,果然我所踩到的坑前辈们都已经踩烂了。 

W3C规范:跨域请求中,分为简单请求和复杂请求;所谓的简单请求,需要满足如下几个条件: 
- GET,HEAD,POST请求中的一种; 
- 除了浏览器在请求头上增加的信息(如Connection,User-Agent等),开发者仅可以增加Accept,Accept-Language,Content-Type等; 
- Content-Type的取值必须是以下三个:application/x-www-form-urlencoded,multipart/form-data,text/plain。

在发出复杂请求的之前,就会出现一次OPTIONS请求。 
OPTIONS请求可以被称作一次嗅探请求,通过这个方法,客户端可以在采取具体的资源请求之前,决定对资源采取何种必要措施。 

因为我们在后端接口 接受数据定义的格式都是使用application/json做数据交互的
请求内容为json的时候,是复杂请求,因此提前进行了一次OPTIONS请求。 

我们通过下方 图例做对比

1.获取Cookie 的为空 的当前请求,我们通过log 日志打印它的当前请求方法

过滤器丢失Cookie的解决方案
过滤器丢失Cookie的解决方案

这里可以在日志中明确看到,我们获取cookie 的值是为空

2.再比较下方成功获取cookie 数据的 请求

过滤器丢失Cookie的解决方案

可以看到先后顺序,当服务器响应成功之后会正式发起一次 post / get 请求

过滤器丢失Cookie的解决方案

同时  我们在 log 日志中可以也看到 , 当options 进入过滤器后,获取cookies 数据时是为空,而当 发出post 请求时候,才成功获取到 cookie 中的数据。

过滤器丢失Cookie的解决方案

也就是说问题出在于,当请求进入 过滤器后被 拦截返回。
因为我们在过滤器拦截器之间,都是正常进行请求,
因此最简单的解决办法即是放行OPTIONS请求。 
通过下列方法放行 options 请求

或者在get request 的请求方法之后,进行强校验,如果为 options 直接 false 放行,我这里最后采取第一种,代码如下:
public void updateHeader (HttpServletResponse response, HttpServletRequest request) throws UnsupportedEncodingException {
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json;charset=UTF-8");
        //允许跨域
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin")); 
        //允许携带带Cookie
        response.setHeader("Access-Control-Allow-Credentials", "true");
        //允许使用的请求方式
        response.setHeader("Access-Control-Allow-Methods", "POST");
        //将请求中头部出现下列关键字的类型都予以支持
        response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
    }
问题解决,感谢大家的支持!
相关标签: 总结