ajax登录成功跳转页面
用fetch发一个登录请求,登录拿到`{ code: 200,成功 token: 'xxx'}`,然后执行`window.location.href = '/dashboard'`。跳转页面白屏了一下,页面地址栏变了,登录内容没出来。成功打开Network面板,跳转那个dashboard请求返回的页面是登录页的HTML。问题不在前端跳转,登录在服务端根本没认出来你已经登录了。成功
这是跳转Ajax登录最典型的坑。很多人以为前端拿到成功响应、页面执行跳转就完事了,登录但HTTP是成功无状态的。你第一次POST /login,跳转服务端验证通过,返回token或者Set-Cookie。浏览器确实存了Cookie,但如果你跳转用的是前端路由(比如React Router的history.push),页面不会重新发起文档请求,服务端不知道你已登录。等你手动刷新或者直接访问受保护路由,服务端读Cookie——如果Cookie的Path、Domain、SameSite没配对,照样401。
我见过一个项目,登录接口返回JSON里带token,前端把token塞进localStorage,然后跳转。跳转后页面加载,前端路由守卫从localStorage读token,有就放行。看起来没问题。但用户直接复制URL发给别人,别人打开也是登录态——因为token在前端,服务端根本没校验。更糟的是,token过期后前端不知道,跳转过去页面渲染一半接口全挂。
正确的做法分两步。第一步,登录接口用`credentials: 'include'`发请求,服务端验证成功后写Session或者签发JWT并Set-Cookie。响应体里可以带用户信息,但别把token当唯一凭证。第二步,前端拿到成功响应后,不要用前端路由跳,用`window.location.replace('/dashboard')`或者`window.location.href`。这样浏览器会重新发起文档请求,带上Cookie,服务端中间件读到Session,放行,返回dashboard的HTML。
fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ username, password }) }) .then(res => res.json()) .then(data => { if (data.code === 200) { window.location.replace('/dashboard'); } else { showError(data.msg); } }); 注意`credentials: 'include'`。不加这行,跨域情况下浏览器不会带Cookie,服务端Session读不到,跳转过去还是登录页。同域的话默认会带,但显式写出来更稳。
还有一个细节:跳转用`replace`而不是`href`。登录成功后用户按后退键,不应该回到登录页。`replace`把当前历史记录替换掉,后退直接到登录前的页面。这个体验差别很小,但用户能感觉到。
服务端那边,Session的过期时间别设太短。我见过设15分钟的,用户填个表单超过15分钟,提交时跳登录页,数据全丢。设2小时或者滑动过期更合理。JWT的话,access token短一点,refresh token长一点,但刷新逻辑别放在跳转之后做——跳转前就得确保token有效。
最后说个反直觉的点。有些团队用Ajax登录后,前端跳转用`location.href`,但服务端返回的是302重定向到登录页。为什么?因为Session Cookie的`SameSite`属性设成了`Strict`,跨站请求不带Cookie。登录请求本身是同站,没问题。但跳转后的dashboard请求,如果是从第三方页面点链接过来的,`Strict`模式下不带Cookie,服务端以为没登录,又踢回登录页。改成`Lax`或者`None`(配合Secure)能解决。这个坑排查起来很费时间,因为登录接口明明返回了200。
所以Ajax登录跳转这件事,前端只负责发请求和跳转,登录态维持靠的是Cookie和服务端Session。跳转方式选对了,Cookie配对了,基本不会出问题。出问题的时候,先看Network里跳转后的请求带没带Cookie,再看服务端Session有没有存上。这两步一查,原因就出来了。
(责任编辑:时尚)













