跳到主要内容

5.3 登录、认证与权限

本章目标: 知道登录状态如何进入页面,理解前端控制体验与后端保障权限的边界。本章是方案说明,附带任务 API 不包含登录接口。

两种常见方案​

方案浏览器怎么带凭据适用时的重点
Cookie 会话浏览器按 Cookie 规则自动发送HttpOnly、Secure、SameSite、CSRF 防护
Bearer access token客户端加 Authorization 头token 获取/过期/刷新、存储与 XSS 风险

同源的后台管理应用通常可以优先评估 Cookie/BFF 方案。对接身份平台、移动客户端或独立 API 时,根据现有认证架构采用 OAuth/OIDC 等成熟流程。

不要为学习页面自己发明 token 签名和身份协议。ASP.NET Core 已有认证授权中间件,成熟身份平台也有配套客户端。

登录页面需要的状态​

登录流程通常包含:检查当前会话、未登录、提交登录、已登录、凭据过期。至少区分“正在检查”与“确实未登录”,否则刷新页面可能先误跳登录页。

登录失败保留用户名,密码按产品要求清理。错误信息避免泄露账号是否存在。退出时清理前端身份缓存,并让服务端会话或刷新凭据失效。

同源 fetch 默认会带同源 Cookie。HttpOnly Cookie 不能由 JS 读取,但浏览器仍会自动发送,因此HttpOnly 不等于自动解决 CSRF。

有状态 Cookie 认证的写入请求应按服务端方案携带防伪 token,结合 SameSite 和 origin 检查等措施。跨域凭据配置见CORS 章节。

Bearer 请求片段​

以下仅展示“已有合法 access token 后如何发送”,不实现登录或存储:

async function getCurrentUser(accessToken: string) {
const response = await fetch('/api/me', {
headers: { Authorization: `Bearer ${accessToken}` },
});
if (response.status === 401) throw new Error('登录已过期');
if (response.status === 403) throw new Error('没有访问权限');
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return await response.json();
}

JS 可访问的 token 存在 XSS 风险。localStorage 不会因为它叫“本地存储”就自动安全;存储方式必须与身份方案一起设计。前端包中不能放客户端密钥或 JWT 签名密钥。

路由守卫负责导航体验​

session 模块已实现时的设计片段
router.beforeEach(async to => {
await session.ensureLoaded();
if (to.meta.requiresAuth && !session.isAuthenticated) {
return { name: 'login', query: { redirect: to.fullPath } };
}
});

这里的 router、session 和 login 路由需要按实际项目实现,不属于任务示例的现成接口。ensureLoaded 应协调并发检查和失败状态,避免每次跳转都重复请求。

登录后处理 redirect 时仅允许站内合法路径,避免开放重定向。

隐藏按钮不是授权

用户可以绕过 UI 直接请求 API。服务端必须对每个敏感接口及资源执行授权,例如 [Authorize]、Minimal API 的 .RequireAuthorization() 和任务归属检查。

401、403 与重试​

401 通常表示未认证或凭据失效;403 表示已确认身份但权限不足。不要把所有 403 都处理为重新登录。

刷新 token 的流程需要协调并发、限制次数并防止无限重试。写入请求也不能无条件重放,因为服务端可能已经执行操作。学习阶段先用现有身份方案完成明确的过期提示。

练习​

画出“访问受保护页面 → 检查会话 → 登录 → 返回页面 → API 授权”的流程,并指出哪一步真正保障数据权限。

参考答案

API 的服务端授权与资源归属检查保障数据权限。路由守卫和按钮可见性提供交互体验;即使整个前端被绕过,API 仍应拒绝无权请求。

参考:ASP.NET Core 认证、授权、Vue Router 导航守卫。