前端实现登录、登出、请求数据的一些思路整理(基于React、JWT技术)
登录、登出和数据请求是两种不同的数据交互方式,是互相独立的。
- 登录、登出基于
JWT(JSON WEB TOKEN)
技术,通过请求后台的登录接口,由服务器生成一个token
凭证返回给前台;前台拿到token
之后存储到本地的localStorage
里面,每次进行数据请求的时候都带上token
传给后台,由后台决定根据token
返回不同的数据和状态。
根据以上思路整理出前端代码
-
① 登录和登出
// 获取当前项目的请求前缀 const BaseAPI = process.env.REACT_APP_API; // 定义本地 token 的键 const localStorageKey = '__auth_provider_key__'; // 定义获取本地 token 的方法 export const getToken = () => window.localStorage.getItem(localStorageKey); // 定义清除本地 token 的方法 export const removeToken = () => window.localStorage.removeItem(localStorageKey); // 定义登录方法(核心) export const login = (data: { username: string, password: string }) => { return fetch(`${BaseAPI}/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }).then(async response => { if(response.ok){ const result = response.json(); window.localStorag.setItem(result.token || ''); return result; }else{ return Promise.reject(data); } }) }
-
② 将
user
的数据和状态共享为全局数据// 创建一个 context 对象 interface User { id: string; name: string; token: string; } interface AuthForm { username: string; password: string; } const AuthContext = React.createContext<{ user: User | null, loginMethod: (form: AuthForm) => Promise<void>, logoutMethod: () => Promise<void> } || undefined>(undefined); AuthContext.displayName = 'AuthContext'; // 创建一个 provider,将数据传递给消费组件进行共享 export const AuthProvider = () => { const [user, setUser] = useState<User | null>(null); const loginMethod = (form: AuthForm) => login(form).then(setUser); const logoutMethod = () => logout().then(setUser(null)); return <AuthContext.Provider value={{user, loginMethod, logoutMethod}} /> } // 由 app 组件作为消费组件,全局共享 user 信息 <AuthProvider> <App></App> </AuthProvider> // 额外封装一个 hooks,用于在消费组件下面的任意地方获取 AuthContext 的数据 export const useAuth = () => { const context = React.useContext(AuthContext); if(!context){ throw new Error("useAuth 必须在 AuthProvide 中使用") }else{ return context; } }
-
③ 封装请求数据的
hooks
// 封装请求方法 import qs from 'qs'; interface Config extends RequestInit { data?: object; token?: string; } export const http = async (endPoint: string, {data, token, ...customConfig}: Config = {}) => { const config = { method: 'GET', headers: { Authorization: token ? `Bearer ${token}` : '', 'Content-Type': data ? 'application/json' : '' }, ...customConfig }; if(config.method.toUpperCase() === 'GET'){ endPoint += data ? `${qs.stringify(data)}`: ''; }else{ config.body = JSON.stringify(data || {}); } return window.fetch(`${BaseAPI}/${endPoint}`, config).then(async response => { if(response.status == 401){ await logout(); return Promise.reject({message: '认证过期,请重新登录'}) } const data = await response.json(); if(response.ok){ return data }else{ return Promise.reject(data); } }) } // 根据 user 的数据,封装数据请求 hooks export const useHttp = () => { const { user } = useAuth(); return (...[endPoint, config]: Parameters<typeof http>) => http(endPoint, {...config, token: user?.token}); } // 使用 const httpRequest = useHttp(); httpRequest("lists").then(setLists);