前几天排查登录页面时碰到一个问题:未登录访问某个页面,跳到登录页,登录成功后应该回到原来的地址。但原地址里的查询参数没能完整带回来。
参数怎么丢的
当时的目标地址类似这样:
1 | /view/dashboard/todo?key=order&proj_key=jd |
如果直接把它拼到 callback 后面:
1 | /view/auth/login?callback=/view/dashboard/todo?key=order&proj_key=jd |
问题就出在这个 &。浏览器解析外层 URL 时,会把 proj_key=jd 当成登录页的另一个参数,而不是 callback 的一部分。结果读取 callback 时,只拿到了:
1 | /view/dashboard/todo?key=order |
后来回头看,其实就是把一个带参数的 URL 原样塞进了另一个 URL。这里要先把整个目标地址编码,再作为参数放进去:
1 | const loginUrl = `/view/auth/login?callback=${encodeURIComponent(target)}` |
登录页读取时可以用 URLSearchParams,不用自己去找 ?callback= 的位置,再用 substr 截字符串:
1 | const fallback = '/view/project-list' |
URLSearchParams 读取时已经会对参数值解码,不需要再无条件调用一次 decodeURIComponent。另外,回跳地址也不能拿到什么就跳什么。这个例子只允许 /view/ 下的路径,其他情况回到默认页;实际项目还要按自己的路由规则做校验。
登录页反复跳转
排查过程中还遇到了另一个问题:鉴权中间件放行的是 /view/auth,但实际访问的登录页是 /view/auth/login。
未登录时访问登录页,中间件发现没有登录,又把请求重定向回登录页,结果就开始循环,URL 里的 callback 也越套越长。把实际登录页路径加入白名单后,才停下来。登录接口也要确认在放行范围内,其他受保护页面则继续走鉴权。
之后再检查这个功能,我会用带多个参数的地址试一遍,也会试试参数里本身有编码字符的情况。同时确认直接打开登录页不会循环,乱填或填写外部回跳地址时会回到默认页。