You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nextjs+Strapi+Redux:如何在Token过期时自动清理用户状态?

问题解答

1. Token过期时从状态移除用户

你可以在Token过期检查逻辑中触发Redux的登出action,同步清空用户状态和Cookie:

具体实现步骤:

  • 编写Redux登出action与对应的reducer,负责清空用户信息并更新认证状态:
// redux/actions/authActions.js
export const logout = () => (dispatch) => {
  // 清除Cookie中的Token
  document.cookie = "token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
  // 更新Redux状态
  dispatch({
    type: "LOGOUT",
    payload: null
  });
};

// redux/reducers/authReducer.js
const initialState = {
  user: null,
  isAuthenticated: false
};

export default function authReducer(state = initialState, action) {
  switch(action.type) {
    case "LOGOUT":
      return {
        ...state,
        user: null,
        isAuthenticated: false
      };
    // 其他登录、获取用户详情的case逻辑...
    default:
      return state;
  }
}
  • 在请求拦截器(以Axios为例)中加入Token过期校验,一旦过期立即触发登出:
// utils/api.js
import axios from "axios";
import { logout } from "../redux/actions/authActions";
import store from "../redux/store";
import jwtDecode from "jwt-decode";

const api = axios.create({
  baseURL: process.env.NEXT_PUBLIC_STRAPI_URL
});

api.interceptors.request.use(async (config) => {
  const token = document.cookie.split('; ').find(row => row.startsWith('token='))?.split('=')[1];
  if (token) {
    const decoded = jwtDecode(token);
    // 校验Token是否过期(exp为时间戳,单位秒)
    if (decoded.exp * 1000 < Date.now()) {
      store.dispatch(logout());
      window.location.href = '/login?expired=true';
    } else {
      config.headers.Authorization = `Bearer ${token}`;
    }
  }
  return config;
});
  • 针对Next.js服务端渲染场景,在getServerSideProps或Middleware中补充Token校验,避免服务端渲染过期用户信息:
// pages/protected-page.js
export async function getServerSideProps(context) {
  const token = context.req.cookies.token;
  if (token) {
    const decoded = jwtDecode(token);
    if (decoded.exp * 1000 < Date.now()) {
      // 清除服务端Cookie
      context.res.setHeader('Set-Cookie', 'token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT');
      return {
        redirect: {
          destination: '/login',
          permanent: false
        }
      };
    }
  }
  // 其他服务端逻辑...
  return { props: {} };
}

2. 是否需要设置过期登出时间?

如果你的应用是敏感场景(如后台管理、支付系统),建议设置定时登出,既提升安全性也优化用户体验;普通业务场景可仅依赖每次请求的Token检查。

基于Next.js + Strapi实现定时登出:

登录时从Strapi返回的JWT中解析过期时间,计算剩余时长后设置定时器自动触发登出:

// 登录页面逻辑
import { useDispatch } from "react-redux";
import { loginSuccess } from "../redux/actions/authActions";
import { logout } from "../redux/actions/authActions";
import jwtDecode from "jwt-decode";

const LoginPage = () => {
  const dispatch = useDispatch();

  const handleLogin = async (credentials) => {
    const res = await fetch(`${process.env.NEXT_PUBLIC_STRAPI_URL}/api/auth/local`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(credentials)
    });
    const data = await res.json();
    if (data.jwt) {
      const decoded = jwtDecode(data.jwt);
      const expiresIn = decoded.exp * 1000 - Date.now();
      
      // 设置定时登出定时器
      setTimeout(() => {
        dispatch(logout());
        window.location.href = '/login?expired=true';
      }, expiresIn);

      // 同步保存Token到Cookie和Redux
      document.cookie = `token=${data.jwt}; path=/; max-age=${expiresIn/1000}`;
      dispatch(loginSuccess(data.user));
    }
  };

  return <div>{/* 登录表单组件 */}</div>;
};

注意:页面刷新后定时器会失效,需在全局布局组件中重新初始化定时器:

// components/Layout.js
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { logout } from "../redux/actions/authActions";
import jwtDecode from "jwt-decode";

const Layout = ({ children }) => {
  const dispatch = useDispatch();
  const { isAuthenticated } = useSelector(state => state.auth);

  useEffect(() => {
    if (isAuthenticated) {
      const token = document.cookie.split('; ').find(row => row.startsWith('token='))?.split('=')[1];
      if (token) {
        const decoded = jwtDecode(token);
        const expiresIn = decoded.exp * 1000 - Date.now();
        const timer = setTimeout(() => {
          dispatch(logout());
          window.location.href = '/login?expired=true';
        }, expiresIn);
        // 组件卸载时清除定时器
        return () => clearTimeout(timer);
      }
    }
  }, [isAuthenticated, dispatch]);

  return <div>{children}</div>;
};

3. 定时登出 vs 每次检查Token:哪种更合理?

建议两者结合:

  • 每次请求检查Token:是核心安全保障,因为定时器可能因页面刷新、浏览器休眠、JS线程阻塞等失效,每次请求前验证Token能确保过期后不会发起无效请求,避免服务端返回401错误。
  • 定时登出:是体验优化,能在Token过期前自动完成登出,避免用户操作到一半才触发过期提示,同时减少不必要的请求。

针对你现有逻辑,可保留每次请求的Token检查,同时添加定时登出逻辑,让两者互补。

内容的提问来源于stack exchange,提问作者Danny Coder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 08:02:45