一、什么是JWT?
JWT全称JSON Web Token,是一种用于身份验证的轻量级令牌。简单来说,它就是一个加密的JSON字符串,包含了用户的身份信息和一些其他元数据。
想象一下,你去电影院看电影,需要出示电影票。JWT就相当于这张电影票,证明你有权利进入特定的"区域"(访问特定的API接口)。
二、JWT的结构
JWT由三部分组成,用点(.)分隔:
- Header(头部) :包含令牌类型和加密算法
- Payload(载荷) :包含用户信息和其他声明(如过期时间)
- Signature(签名) :用于验证令牌的真实性
三、JWT的工作流程
JWT的工作流程非常简单,主要包括以下几个步骤:
- 用户使用用户名和密码登录
- 服务器验证成功后,生成JWT令牌并返回给客户端
- 客户端存储JWT令牌(通常在localStorage或Cookie中)
- 后续请求时,客户端将JWT令牌放入请求头中
- 服务器验证令牌的有效性,有效则处理请求,无效则返回错误
四、在React项目中实现JWT鉴权
结合下面的 api/config.js 文件,我们来看看如何在React项目中实现JWT鉴权。
1. Axios拦截器配置
import axios from 'axios';
// 创建axios实例
const service = axios.create({
baseURL: process.env.VITE_APP_API_URL, // 基础URL
timeout: 5000 // 请求超时时间
});
// 请求拦截器:添加JWT令牌
axios.interceptors.request.use((config)=>{
// 从localStorage中获取JWT令牌
const token = localStorage.getItem('token');
// 如果令牌存在,则添加到请求头
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
}, (error)=>{
// 处理请求错误
return Promise.reject(error);
});
// 响应拦截器:处理令牌过期等情况
axios.interceptors.response.use((response)=>{
// 正常响应处理
return response.data;
}, (error)=>{
// 处理响应错误
if (error.response && error.response.status === 401) {
// 令牌过期,跳转到登录页
localStorage.removeItem('token');
window.location.href = '/login';
}
return Promise.reject(error);
});
export default service;
2. 登录接口实现
import service from './config';
// 登录接口
export const login = (username, password) => {
return service.post('/api/login', {
username,
password
});
};
// 登出接口
export const logout = () => {
localStorage.removeItem('token');
window.location.href = '/login';
};
3. 登录组件实现
import React, { useState } from 'react';
import { login } from '../api/login';
const Login = () => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const handleLogin = async () => {
try {
const res = await login(username, password);
// 存储JWT令牌
localStorage.setItem('token', res.data.token);
// 跳转到首页
window.location.href = '/';
} catch (err) {
setError('用户名或密码错误');
}
};
return (
<div className="login-container">
<h2>登录</h2>
{error && <div className="error-message">{error}</div>}
<div className="form-group">
<label>用户名</label>
<input
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
</div>
<div className="form-group">
<label>密码</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<button onClick={handleLogin}>登录</button>
</div>
);
};
export default Login;
4. 路由守卫实现
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
// 路由守卫组件
const PrivateRoute = ({ component: Component, ...rest }) => {
const isAuthenticated = !!localStorage.getItem('token');
return (
<Route
{...rest}
render={(props) =>
isAuthenticated ? (
<Component {...props} />
) : (
<Redirect to={{ pathname: '/login', state: { from: props.location } }} />
)
}
/>
);
};
export default PrivateRoute;
五、JWT的优缺点
优点
-
无状态 :服务器不需要存储会话信息,减轻服务器负担
-
跨域支持 :JWT可以在不同域之间传递
-
自包含 :令牌包含了所有必要的用户信息,减少数据库查询
-
易于扩展 :可以添加自定义声明
缺点
-
无法作废已发布的令牌 :一旦令牌发布,在过期前始终有效(除非服务器维护黑名单)
-
令牌较大 :相比Session ID,JWT令牌通常更大,会增加网络传输负担
-
安全隐患 :如果令牌被窃取,攻击者可以冒充用户访问系统
-
刷新机制复杂 :需要实现令牌刷新机制以避免用户频繁登录
六、结语
JWT是一种简单、高效的身份验证机制,特别适合前后端分离的应用。通过在React项目中使用Axios拦截器,我们可以轻松实现JWT的添加和验证。
然而,JWT也有其局限性,我们需要注意安全问题,设置合理的过期时间,并实现令牌刷新机制。