过滤器丢失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 Method为Options,另一条请求的Reuest Method为Post。
当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 的值是为空
2.再比较下方成功获取cookie 数据的 请求
可以看到先后顺序,当服务器响应成功之后会正式发起一次 post / get 请求
同时 我们在 log 日志中可以也看到 , 当options 进入过滤器后,获取cookies 数据时是为空,而当 发出post 请求时候,才成功获取到 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");
}
问题解决,感谢大家的支持!
推荐阅读