前端鉴权机制实战总结 JWT和Session的最佳实践选择

南宫婷婷 安全 阅读 2,976
赞 27 收藏
二维码
手机扫码查看
反馈

前端鉴权方案对比:我常用的几种方式都有啥坑

最近在重构一个老项目的鉴权系统,顺便把之前用过的几种前端鉴权方案梳理了一下。说实话,每种方案都有各自的适用场景,但也有不少坑要踩。今天就来分享一下我实际开发中的体验。

前端鉴权机制实战总结 JWT和Session的最佳实践选择

Session + Cookie 方案:经典但有局限

这是最早接触的鉴权方式,后端存储 session,前端通过 cookie 传 token。代码相对简单:

// 登录请求
fetch('https://jztheme.com/api/login', {
  method: 'POST',
  credentials: 'include', // 重要:带上cookie
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    username: 'test',
    password: '123456'
  })
})

每次请求都自动带上 cookie,省事。但问题也很明显:跨域麻烦,需要后端设置 CORS 和 cookie 的 domain,而且移动端原生应用不好搞。

JWT Token 方案:灵活但要小心

现在大部分项目我都倾向于用 JWT,前端存储 token,请求时放在 header 里:

// 存储token
localStorage.setItem('token', response.token);

// 请求拦截器添加token
axios.interceptors.request.use(config => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers.Authorization = Bearer ${token};
  }
  return config;
});

// 响应拦截器处理过期
axios.interceptors.response.use(
  response => response,
  error => {
    if (error.response?.status === 401) {
      // 清除本地token,跳转登录页
      localStorage.removeItem('token');
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

优点是前后端分离彻底,跨域无障碍,后端无状态。但JWT 一旦签发无法主动失效,这是个大坑。还有就是 token 暴露在前端,安全性依赖于前端防护措施。

OAuth 2.0:复杂需求的选择

对于第三方登录或者复杂的权限管理,OAuth 2.0 是绕不过去的。用起来确实复杂:

// 获取授权码
const authUrl = https://example.com/oauth/authorize? +
  client_id=${clientId}& +
  redirect_uri=${encodeURIComponent(redirectUri)}& +
  response_type=code& +
  scope=read;

window.location.href = authUrl;

// 回调页面获取token
const code = new URLSearchParams(window.location.search).get('code');
fetch('/api/token', {
  method: 'POST',
  body: new URLSearchParams({
    grant_type: 'authorization_code',
    code: code,
    client_id: clientId,
    client_secret: clientSecret,
    redirect_uri: redirectUri
  })
});

这套流程走下来,代码量不小,调试也比较麻烦。但如果项目涉及多个系统间的权限打通,OAuth 几乎是唯一选择。

谁更灵活?谁更省事?

从开发效率来说,Session + Cookie 最省事,框架基本都有现成支持。但现代 SPA 应用还是 JWT 更合适,特别是配合 axios 拦截器,一套代码能覆盖大部分场景。

JWT 的最大问题是 token 刷新机制。我一般会这样处理:

// 添加刷新token的逻辑
let isRefreshing = false;
let refreshSubscribers = [];

function subscribeTokenRefresh(cb) {
  refreshSubscribers.push(cb);
}

function onRefreshed(token) {
  refreshSubscribers.forEach(cb => cb(token));
  refreshSubscribers = [];
}

// 在响应拦截器中处理401
axios.interceptors.response.use(
  response => response,
  async error => {
    const { config, response } = error;
    
    if (response?.status === 401 && !config._retry) {
      if (!isRefreshing) {
        isRefreshing = true;
        
        try {
          const refreshToken = localStorage.getItem('refreshToken');
          const res = await fetch('/api/refresh', {
            method: 'POST',
            body: JSON.stringify({ refreshToken })
          });
          
          const newToken = await res.json();
          localStorage.setItem('token', newToken.access_token);
          
          onRefreshed(newToken.access_token);
          isRefreshing = false;
        } catch (e) {
          isRefreshing = false;
          // 刷新失败,跳转登录
          window.location.href = '/login';
        }
      }
      
      return new Promise(resolve => {
        subscribeTokenRefresh(token => {
          config.headers.Authorization = Bearer ${token};
          resolve(axios(config));
        });
      });
    }
    
    return Promise.reject(error);
  }
);

我的选型逻辑

简单来说,我会根据项目规模和复杂度来选择:

  • 小项目、内部系统:直接 Session + Cookie,简单高效
  • 中大型项目、需要跨域:JWT + Refresh Token,注意刷新机制的实现
  • 复杂权限体系、多系统集成:OAuth 2.0,前期投入大,后期维护省心

其实还有一个现实因素:团队成员的技术栈熟悉程度。如果后端同事更习惯 Session,那也没必要为了追求新技术而增加沟通成本。

踩坑提醒:这几点一定注意

JWT 存储安全是个大问题。我之前就把敏感信息直接存在 localStorage 里,后来改成 sessionStorage 并且做了加密处理:

// 简单加密存储
function setSecureToken(token) {
  const encrypted = btoa(JSON.stringify({
    t: token,
    e: Date.now() + 3600000 // 1小时后过期
  }));
  sessionStorage.setItem('auth_token', encrypted);
}

function getSecureToken() {
  const encrypted = sessionStorage.getItem('auth_token');
  if (!encrypted) return null;
  
  try {
    const data = JSON.parse(atob(encrypted));
    if (data.e > Date.now()) {
      return data.t;
    } else {
      sessionStorage.removeItem('auth_token');
      return null;
    }
  } catch {
    return null;
  }
}

另外,token 过期时间设置也很关键。太短用户频繁登录,太长安全风险高。我现在一般设置 access token 15分钟,refresh token 7天,平衡用户体验和安全性。

以上是我个人对前端鉴权几种方案的对比总结,有更优的实现方式欢迎评论区交流。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论