React项目中规范使用token的注意事项

一、初始路由设计

token在初始界面时一般涉及两个组件:Layout和Login

import Layout from "../pages/Layout";
import Login from "../pages/Login";

import { createBrowserRouter } from "react-router-dom";

const router=createBrowserRouter([
  {
    path:"/",
    element: <Layout />  
  },
  {
    path: "/login",
    element: <Login />
  },
])

export default router

 Login作为登录表单页负责获取token对其进行管理存储,后跳转到其他子组件出口的面板页Layout

二、封装token的存取删除的方法

对于token的各类操作复用性较强,可以直接封装成utils函数

//封装token相关的方法  存  取  删


//如果有单独的常量文件,可以此条语句写入并导入当前文件
const TOKENKEY = 'token_key'

function setToken(token) {
  localStorage.setItem(TOKENKEY, token)
}

function getToken() {
  return localStorage.getItem(TOKENKEY)
}

function removeToken() {
  return localStorage.removeItem(TOKENKEY)
}

export { setToken, getToken, removeToken }

三、使用Redux管理token

token作为一个用户的标识数据,需要在很多个模块中实现状态共享

1.redux中编写获取token的异步获取和同步修改action

//src/store/modules/user.js


//用户相关的管理
import { createSlice } from "@reduxjs/toolkit";
import { request } from "@/utils";

const userStore=createSlice({
  name:'user',
  //初始状态
  initialState:{
    token: ''
  },
  //同步修改方法
  reducers:{
    setToken(state,action){
      state.token=action.payload
    }
  }
})
//解构出actionCreater
const {setToken} =userStore.actions

//获取reducer函数
const userReducer=userStore.reducer

//异步方法 完成登录获取token
const fetchLogin=(loginForm)=>{

  return async (dispatch)=>{
  //1.发送异步请求
  const res=await request.post('/authorizations',loginForm)
  //2.提交同步方法
    dispatch(setToken( res.data.token))
}
}

export { setToken, fetchLogin }

export default userReducer

2.Login组件负责提交action并把表单数据传递过来

//src/pages/Login/index.js
  

//点击登录回调,values自动获取表单数据
  const onFinish = async (values) => {
    //key为设置的name值
    //触发异步action  fetchLogin
    //保证token获取到之后才进行跳转工作
    await dispatch(fetchLogin(values))
    //跳转首页
    navigate('/')
    //提示
    message.success('登录成功')
  }

四、token持久化

问题:

redux是基于浏览器内存的存储方式,刷新浏览器时状态会恢复为初始值,token会丢失

解决方案:

1.获取token时:同时存入redux和本地localStorage

//src/store/modules/user.js


import { setToken as _setToken,getToken } from "@/utils";


//同步修改方法
  reducers:{
    setToken(state,action){
      state.token=action.payload
      //存进本地
      _setToken(action.payload)
    }
  }

2.初始化token时:在redux中先查看本地是否有token值,如果有则用本地值,没有就置为空字符串

//src/store/modules/user.js


import { setToken as _setToken,getToken } from "@/utils";



initialState:{
    //先从本地取,如果本地有,就用本地的
    token: getToken() ||''
  },

五、在axios中注入token

1.在请求拦截器中配置token请求头

// 添加请求拦截器
// 在请求发送之前 做拦截 插入一些自定义的配置 [参数的处理]
request.interceptors.request.use((config) => {
  // 操作这个config 注入token数据
  // 1. 获取到token
  // 2. 按照后端的格式要求做token拼接
  const token = getToken()
  if (token) {
    //根据自身情况是否拼接字符串
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
}, (error) => {
  return Promise.reject(error)
})

2.在响应拦截器中处理token失效

// 添加响应拦截器
// 在响应返回到客户端之前 做拦截 重点处理返回的数据
request.interceptors.response.use((response) => {
  // 2xx 范围内的状态码都会触发该函数。
  // 对响应数据做点什么
  return response.data
}, (error) => {
  // 超出 2xx 范围的状态码都会触发该函数。
  // 对响应错误做点什么
  // 监控401 token失效
  console.dir(error)
  if (error.response.status === 401) {
    removeToken()
    router.navigate('/login')
    //无法跳转登录页,会因为报错卡在这里,此时强制刷新即可
    window.location.reload()
  }
  return Promise.reject(error)
})

注意:navigate和useNavigate的区分

import router from "@/router"
router.navigate('/login')
//写在函数外
import { useNavigate } from 'react-router-dom'
//写在函数中
const navigate = useNavigate()
navigate('/')

两种都可以进行页面导航,他们的区别是什么呢?

1. useNavigate

  • 用法useNavigate 是一个 React Hook,用于在函数组件中进行导航。
  • 特点:
    • 只能在函数组件中使用。
    • 直接返回一个 navigate 函数,用于实现导航。

2. router.navigate

  • 用法router.navigate 通常是在 React Router 的更高层次的 API(如在类组件中或路由配置中)中使用的。
  • 特点:
    • 可能需要对 router 对象有更多的上下文理解。
    • 一般用于类组件或在特定的上下文中。

 笼统地说,就是useNavigate只能用于函数组件中,而像request.js这样的工具函数用navigate

六、使用token做路由权限控制

有些路由页面内的内容信息比较敏感,如果用户没有经过登录获取到有效的token,是没有权限跳转的,根据token的有无控制当前路由是否可以跳转就是路由的权限控制

1.封装高阶组件

// 封装高阶组件
// 核心逻辑: 有token 正常跳转  无token 去登录

import { getToken } from '@/utils'
import { Navigate } from 'react-router-dom'

export function AuthRoute ({ children }) {
  const token = getToken()
  if (token) {
    //接收到组件正常渲染
    return <>{children}</>
  } else {
    //重定向 to跳转路径  replace替换当前为根路径
    return <Navigate to={'/login'} replace />
  }
}

2.修改路由

import Layout from "../pages/Layout";
import Login from "../pages/Login";
import { AuthRoute } from "@/components/AuthRoute";

import { createBrowserRouter } from "react-router-dom";

const router=createBrowserRouter([
  {
    path:"/",
    element: <AuthRoute ><Layout /></AuthRoute>  
  },
  {
    path: "/login",
    element: <Login />
  },
])

export default router
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值