近日,在与前端对接完了【登录页面】与【首页】的相关逻辑之后,开始对接【个人中心】页面相关的逻辑,排查了一个很棘手的问题,涉及前后端的跨域,故作次记录✍
一、异常描述
首先我们来看看究竟触发了什么异常
- 很明显可以看到为
NullPointerException
空指针异常
- 并且我们又看到当前所访问的session为
null
接下去我们来看看已经插入的测试数据
- 关于
user_info
的用户表 :这里有两个用户,我们主要关注的是这里的uid
- 然后是关于
article_info
的文章表 :我们看到的是uid
为【13】的记录有两条,说明这个用户发了两篇文章
此时我们要来测试观察的就是 能否在用户的个人中心看到这个用户所发的所有文章
- 通过前端向后端发送请求我们可以看到,后端发送回来一个
401
的状态码,学习过相关 HTTP状态码 的通过就可以很清楚地知道,其表示:由于缺乏目标资源要求的身份验证凭证,发送的请求未得到满足
💬 那么从初步看来我们就可以知道是由于【身份验证】这一块出现了问题
二、异常排查
那么接下去我们就可以去展开进一步的排查,对于【身份验证】的问题我可以初步想到是与
Cookie/Session
有关
- 此时就回想起我们在学习 JavaWeb 的时候谈到了 理解Cookie和Session之间的区别,在其中我们讲到了当我们在第一次登录一个系统的时候,后端就会为这个用户创建会话并分配SessionId,在第一次返回的时候便会给到响应。
- 接着当我们再次登录的时候就需要 HTTP的请求报文中协带有这个Cookie字段并存在合法SessionId
1、优先转包观察
那现在我们正处于的是已经登录之后的页面,如果此时当前所登录的用户再向后端发起任何请求的话就一定会携带有这个
Cookie
字段,要想查看的话我们就需要去【抓包】
- 这是当前用户在登录完成之后,后端所分配给它的一个
SessionId
,发还回去之后下一次次用户还想再发起其他请求的时候便需要携带这个Cookie
字段,并且其中的SessionId
还要匹配才行
- 那么接下去,若当前用户去获取其所有文章列表的时候,就需要带上这个
Cookie
字段,但是就以下抓包的结果来看,并没有这个字段的出现,所以我们可以初步断定的是 若当前用户在登录完成之后再次向后端发起请求的时候,不会再次携带所给的Cookie
2、调试观察
在转包完成之后还是不够的,我们再通过调试来观察一下,到底是哪里出了问题💻
- 以下是后端中
/article/get_list_by_page
这个接口中的实现逻辑
java
/**
* 实现文章列表的分页功能
*/
@RequestMapping("get_list_by_page")
public AjaxResult GetListByPage(Integer pageSize, Integer pageIndex, HttpServletRequest request){
// 1.参数异常处理
if (pageSize == null || pageSize <= 0)
pageSize = 4; // 大小默认为4
if (pageIndex == null || pageIndex < 1)
pageIndex = 1; // 默认在首页
// 2.通过传递的参数计算 offset
int offset = (pageIndex - 1) * pageSize;
// 3.传递参数,获取相应的文章列表
UserInfo userInfo = SessionUtils.getUser(request);
List<ArticleInfoVO> list = articleService.GetListByPage(pageSize, offset, userInfo);
// 4.处理文章简介
ArticleUtils.SetContentToDescribe(list);
return AjaxResult.success(list);
}
- 通过
getUser()
来获取当前用户的信息的时候,我们可以发现当前的session
是空的
三、可能性测试
💬 所以此刻有些同学就会提出这样的问题:是否是在登录成功后进入首页,然后再进入【个人中心】的时候Cookie
丢失了呢?
这个问题问得很好,于是我便让前端修改了一下,让用户在登录成功之后直接跳转到【个人中心】,以测试
Cookie
是否会丢失
1、测试页面跳转丢失Cookie可能性
- 但是在登录成功之后直接跳转到当前页面时,我们实行同样的操作之后呢。。。
- 还是没有看见这个请求报文中有
Cookie
字段
2、测试发送请求类型
学习过HTTP请求的同学一定清楚,除了『状态码』之外,还有很重要的一点就是『发送的请求类型』,最常见的就是
POST
请求和GET
请求
- 那么此时我们就需要对照这两个请求来看看到底是否为【发送请求类型】的问题?
- 通过抓包我们可以看待,即使是
GET
请求我们也看不到Cookie
字段。POST
请求即为上面所测试的结果,此处不再展示
💬 那经过观察我们可以发现请求报文不携带Cookie
字段和所发送的请求类型并无关系
3、跨域问题诱因测试
经过上面的种种排查和测试,都没有发现问题,于是我回到这个网页中,继续进行了一波仔细的观察,又注意到了这两个点,即
localhost:5173
和localhost:8080
- 因为我们这个项目是前后端分离的,所以在前端给后端发起请求的时候会涉及到跨域问题,
- 所以还专门针对跨域问题给到了解决的措施,这样才能打通前后端
java
configuration.addAllowedOrigin("http://localhost:5173"); // 允许谁跨域
最后,根据这个蛛丝马迹,找到了相关的解决问问题
- 出于 浏览器的安全机制,在跨域的环境中,当前端向后端发起请求后处于安全考虑浏览器是不会携带
Cookie
的,那我们在前端的代码中就需要加上以下两句:
js
import axios from 'axios';
axios.defaults.withCredentials = true;
- 此时我们再去调试观察就可以发现此时的
session
已经不为 null
- 接下去再去转包观察的时候,便发现此时的发送报文就携带上
Cookie
字段了
- 既然携带有
Cookie
的话便可以查询到当前用户的文章列表了✔
4、Chrome浏览器版本问题
这里再补充一点,在网上很多人都提到是 Chrome浏览器版本问题 ,所以呢这里也标注一下这个解决方案,文章来源 链接
🔰 91版本及以上的Chrome浏览器
【Windows】:打开Chrome快捷方式的属性,在目标后添加--disable-features=SameSiteByDefaultCookies
,点击确定,关闭所有Chrome窗口包括Chrome浏览器后再重启浏览器运行项目即可解决。
【Mac】:Mac系统下可以通过命令行携带参数打开浏览器的方式来解决,前提须关闭所有浏览器窗口并退出浏览器后再进行操作
🔰 94及以上版本的Chrome浏览器
Chromium项目官网 提到在94版本通过命令行禁用设置SameSite
默认值的方式会被移除,到时方案1和方案2的方式都将无法使用,后续可通过nginx
等代理工具或软件将跨域请求转为非跨域请求来解决改问题
The flags #same-site-by-default-cookies and #cookies-without-same-site-must-be-secure have been removed from chrome://flags as of Chrome 91, as the behavior is now enabled by default. In Chrome 94, the command-line flag --disable-features=SameSiteByDefaultCookies,CookiesWithoutSameSiteMustBeSecure will be removed.
四、总结与反思
最后我们来总结并回顾一下这个异常:book:
- 于我的排查过程而言,这个异常很好地帮助我清楚地了解了前后端的交互流程,并且锻炼了我对于各类异常:比如Java中的各种异常 、前端页面发起请求后的HTTP状态码异常 、各种的跨域问题等等,对于前后端分离的项目中最常见的便是【跨域问题】了,以及各类接口不匹配的问题
- 归根结底,BUG是排不完的,无论是你在做项目还是在做其他的事情,取决的不是这个BUG的难易程度,而是你有没有一颗的积极探索的心去一步步这个问题,继而从中获得
些许的排查经验
以上就是本文要介绍的所有内容,感觉您的阅读:rose: