欢迎光临
我们一直在努力

accesstoken和refreshtoken的理解

一个企业级完整的 React AuthProvider + 自动刷新 AccessToken + 过期退出 示例。
核心思路:

  • AccessToken 有效期短(例如 30 分钟)

  • RefreshToken 有效期长(例如 7 天)

  • 前端 UI 状态与后端权限保持一致

  • AccessToken 过期 → 自动刷新

  • RefreshToken 过期 → 自动退出登录

  • 下面是完整示例:

    import React, { createContext, useState, useEffect, useContext } from 'react';
    import axios from 'axios';
    import { login as loginApi, logout as logoutApi, refreshToken as refreshTokenApi } from '../api/auth';

    export const AuthContext = createContext(null);

    export const AuthProvider = ({ children }) => {
    const [user, setUser] = useState(null);
    const [token, setToken] = useState(null);
    const [loading, setLoading] = useState(true);
    const [isAuthenticated, setIsAuthenticated] = useState(false);

    // 初始化:检查 token + refresh token
    useEffect(() => {
    const initAuth = async () => {
    const storedToken = localStorage.getItem('token');
    const storedRefreshToken = localStorage.getItem('refreshToken');
    const storedUser = localStorage.getItem('user');

    if (!storedToken || !storedRefreshToken || !storedUser) {
    setLoading(false);
    return;
    }

    setToken(storedToken);
    setUser(JSON.parse(storedUser));
    setIsAuthenticated(true);

    // 这里可以尝试刷新 token,如果已过期
    try {
    await tryRefreshToken();
    } catch (error) {
    logout();
    }

    setLoading(false);
    };

    initAuth();
    }, []);

    // 登录
    const login = async (credentials) => {
    const response = await loginApi(credentials);
    const { code, message, data } = response;

    if (code !== 200) {
    setIsAuthenticated(false);
    setUser(null);
    setToken(null);
    throw new Error(message || '登录失败');
    }

    const { token: accessToken, refreshToken, userType, userName } = data;
    const userData = { userType, userName };

    setToken(accessToken);
    setUser(userData);
    setIsAuthenticated(true);

    localStorage.setItem('token', accessToken);
    localStorage.setItem('refreshToken', refreshToken);
    localStorage.setItem('user', JSON.stringify(userData));
    };

    // 登出
    const logout = async () => {
    try {
    await logoutApi();
    } catch (error) {
    console.error('登出API失败', error);
    } finally {
    setToken(null);
    setUser(null);
    setIsAuthenticated(false);

    localStorage.removeItem('token');
    localStorage.removeItem('refreshToken');
    localStorage.removeItem('user');
    }
    };

    // 尝试刷新 token
    const tryRefreshToken = async () => {
    const storedRefreshToken = localStorage.getItem('refreshToken');
    if (!storedRefreshToken) throw new Error('没有 refresh token');

    try {
    const res = await refreshTokenApi({ refreshToken: storedRefreshToken });
    const { accessToken, refreshToken } = res.data;

    setToken(accessToken);
    localStorage.setItem('token', accessToken);
    localStorage.setItem('refreshToken', refreshToken);
    } catch (error) {
    throw new Error('Refresh token 已过期');
    }
    };

    // Axios 拦截器,自动刷新 token 或退出登录
    useEffect(() => {
    const interceptor = axios.interceptors.response.use(
    (res) => res,
    async (error) => {
    const originalRequest = error.config;

    if (error.response?.status === 401 && !originalRequest._retry) {
    originalRequest._retry = true;

    try {
    await tryRefreshToken();
    originalRequest.headers['Authorization'] = 'Bearer ' + localStorage.getItem('token');
    return axios(originalRequest);
    } catch (e) {
    logout();
    return Promise.reject(e);
    }
    }

    return Promise.reject(error);
    }
    );

    return () => {
    axios.interceptors.response.eject(interceptor);
    };
    }, []);

    const value = {
    user,
    token,
    loading,
    isAuthenticated,
    login,
    logout,
    };

    return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
    };

    export const useAuthStore = () => {
    const context = useContext(AuthContext);
    if (!context) throw new Error('useAuthStore 必须在 AuthProvider 内使用');
    return context;
    };


    ✅ 效果说明

  • 页面刷新 → 尝试恢复 token,并刷新一次 AccessToken,如果 RefreshToken 有效

  • AccessToken 过期 → 自动刷新 token,无需登出

  • RefreshToken 过期 → 前端自动登出,并清理 localStorage

  • 所有 axios 请求 401 → 自动刷新或登出


  • 前端 RefreshToken 自动续签 + AccessToken 过期退出逻辑 的完整流程


    JWT + RefreshToken 生命周期图

    用户登录


    后端返回:
    {
    accessToken (30分钟),
    refreshToken (7天)
    }

    ├─ 前端保存:
    │ localStorage.token = accessToken
    │ localStorage.refreshToken = refreshToken
    │ localStorage.user = userData

    前端正常访问接口 (带 AccessToken)


    后端验证 AccessToken
    ├─ 有效 → 返回数据
    └─ 过期 → 返回 401


    前端 Axios 拦截器捕获 401


    尝试刷新 AccessToken (用 RefreshToken)

    ┌──────┴────────┐
    │ │
    RefreshToken 有效 RefreshToken 过期
    │ │
    ▼ ▼
    后端返回新 AccessToken 前端登出


    前端保存新 AccessToken


    重试原请求 → 用户无感知


    🔹 说明

  • AccessToken(短期)

    • 用于每次接口访问

    • 过期快(30分钟)

    • 过期 → 自动刷新

  • RefreshToken(长期)

    • 用于刷新 AccessToken

    • 过期慢(7天)

    • 过期 → 强制登出

  • 前端状态逻辑

    • isAuthenticated = token存在 && token未过期

    • 页面刷新 → 尝试用 RefreshToken 刷新 AccessToken

    • RefreshToken 无效 → 自动登出

  • 企业级特点

    • 用户体验:不会因为 AccessToken 过期而突然登出

    • 安全性:AccessToken 短期有效,盗用风险低

    • 全局统一:Axios 拦截器 + 后端校验 + 前端状态一致

  • 情况前端 UI后端验证
    AccessToken 有效 显示登录 接口正常返回数据
    AccessToken 过期,RefreshToken 有效 自动刷新 token → 显示登录 返回新 AccessToken
    AccessToken 过期,RefreshToken 过期 前端 logout → 显示未登录 401 Unauthorized

    核心:AccessToken 到期不等于登出,只有 RefreshToken 到期才真正登出。


    [用户访问页面 / 刷新页面]


    [AuthProvider useEffect 初始化]


    localStorage 检查 token / refreshToken / user

    ┌──────┴──────┐
    │ │
    token / refreshToken 不存在 → logout → 显示未登录


    token / refreshToken 存在 → 尝试刷新 token

    ┌──────┴──────┐
    │ │
    刷新成功 → 更新 AccessToken → 保持登录状态
    刷新失败 → logout → 显示未登录


    [用户访问接口]


    后端验证 AccessToken
    ┌─────────┴─────────┐
    │ │
    AccessToken 有效 → 返回数据
    AccessToken 过期 → 返回 401


    [Axios 拦截器捕获 401]

    ┌──────┴──────┐
    │ │
    尝试刷新 token RefreshToken 已过期 → logout

    刷新成功 → 更新 AccessToken → 重试请求 → 用户无感知
    刷新失败 → logout → 显示未登录

    🔹 说明

  • 前端缓存(localStorage)

    • 只是“假设登录状态”,不保证有效

    • 主要作用:刷新页面时可以尝试恢复登录

  • AccessToken(短期)

    • 用于每次接口访问

    • 过期 → 后端返回 401 → 前端自动刷新或登出

  • RefreshToken(长期)

    • 用于刷新 AccessToken

    • 过期 → 必须登出,UI 才显示未登录

  • 企业级特点

    • 用户体验好:AccessToken 过期不影响 UI

    • 安全性高:AccessToken 短期有效,盗用风险低

    • 前端状态与后端权限同步,保证不会假象登录

  • 前端 后端
    ─────────────── ───────────────

    用户登录 ───────────────▶ 验证账号密码


    返回 AccessToken(30m)
    返回 RefreshToken(7d)

    └─────────▶ 前端存 localStorage
    token + refreshToken + user

    ────────────────────────────────────────────────────────────

    用户访问接口 ─────────────▶ 后端验证 AccessToken
    ├─ AccessToken 有效 → 返回数据
    └─ AccessToken 过期 → 返回 401

    ────────────────────────────────────────────────────────────

    Axios 拦截器捕获 401 ─────────▶ 尝试刷新 AccessToken

    ┌────────────┴────────────┐
    │ │
    RefreshToken 有效 RefreshToken 过期
    │ │
    后端返回新 AccessToken 后端返回 401
    │ │
    前端更新 token → 重试原请求 前端 logout → 清理 localStorage
    │ │
    ▼ ▼
    用户无感知,继续操作 用户真正登出,UI显示未登录

    赞(0)
    未经允许不得转载:171主机测评 » accesstoken和refreshtoken的理解
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址