|
上个月帮朋友改一个老项目,体实ASP.NET WebForms 写的体实后台。登录之后,体实页面刷新,体实用户信息没了。体实他问我:能不能别用 Session,体实改成 token?体实我说可以,但得先接受一个事实——WebForms 的体实模型和 token 天然有点别扭。 WebForms 的体实核心是 ViewState 和 PostBack。你点一个按钮,体实整个页面连同隐藏字段一起提交回服务器,体实服务器重建控件树,体实跑事件,体实再吐一个新页面回来。体实整个过程依赖“页面是体实有状态的”这个假设。Token 呢,是无状态的,每次请求带一个字符串,服务器验完就完事。把这两个东西捏在一起,就像让一个习惯记笔记的人改用便签纸——能写,但得换个写法。 最直接的做法:登录成功后,服务端生成一个 token,塞进 Cookie 或者返给前端存 localStorage。之后每次请求,在 Global.asax 的 Application_BeginRequest 里读出来,验签,把用户信息挂到 HttpContext.Current.Items 上。页面里要用用户信息,就从 Items 里取。这样不碰 Session,也不依赖 ViewState 传身份。 protected void Application_BeginRequest(object sender, EventArgs e) { var token = Request.Headers["Authorization"] ?? Request.Cookies["auth_token"]?.Value; if (!string.IsNullOrEmpty(token)) { var principal = TokenHelper.Validate(token); if (principal != null) { HttpContext.Current.Items["User"] = principal; } } } 但这里有个坑。WebForms 的 PostBack 默认会带上 ViewState,而 ViewState 里可能藏着上次渲染时的用户控件状态。如果 token 过期了,页面还能从 ViewState 恢复出旧数据,用户以为还在登录态,实际请求已经该被拒了。所以验 token 失败时,不能只记个日志,得直接清掉 ViewState 并跳转登录页。我一般会在 Page_Load 里加一句: if (HttpContext.Current.Items["User"] == null && !IsLoginPage) { Response.Redirect("/login.aspx"); } 另一个麻烦是 UpdatePanel。局部刷新时,请求走的是异步 PostBack,Application_BeginRequest 照样触发,token 验证没问题。但如果你在 UpdatePanel 里返回 401,前端不会自动跳转,只会静默失败。得在 ScriptManager 里注册一个 EndRequest 处理器,检测到 401 就 window.location 跳登录。这个细节不写,用户会卡在“点了没反应”的状态。 Token 存哪儿也有讲究。Cookie 会自动带上,但 WebForms 的 Cookie 默认是 HttpOnly,前端 JS 读不到,适合纯服务端验证。localStorage 方便前端拿,但每次请求得手动塞到 header 里,WebForms 的服务器控件不会自动帮你做这件事。折中方案:登录后把 token 写进一个隐藏字段,每次 PostBack 自动提交。缺点是隐藏字段在 ViewState 里,页面大了会拖慢速度。我试过,一个中等复杂度的页面,ViewState 能涨到 30KB 以上,token 那几百字节倒不是主要问题,但心理上不舒服。 刷新 token 也得考虑。WebForms 没有统一的拦截器,你只能在 Application_BeginRequest 里判断:如果 token 快过期了,就生成一个新的,塞到 Response.Cookies 里。注意,BeginRequest 阶段改 Response 的 Cookie 有时会被后续的 Response.Redirect 冲掉。稳妥点,放到 EndRequest 里做。我踩过这个坑,调了一下午才发现是执行顺序的问题。 最后说个实在的观察:WebForms 项目用 token,别指望做到像 WebAPI 那样干净。ViewState 和 PostBack 的惯性太大,你只能在夹缝里塞一套轻量的验证逻辑。如果项目还能改架构,早点换 ASP.NET Core 的 Razor Pages 或者 Blazor,token 处理会舒服得多。但要是维护老系统,上面这套够用——能跑,能验,能踢人。别追求完美,先让登录态不丢。 |