JSON web Token 通过JSON形式作为在web应用中的令牌,可以在各方之间安全的把信息作为JSON对象传输、 信息传输、授权
- JWT的认证流程:
- 前端把账号密码发送给后端的接口
- 后端核对账号密码成功后,把用户id等其他信息作为JWT负载,把它和头部分别进行base64编码拼接后签名,形成一个JWT(token)。
- 客户端将JWT存储在本地,每次请求时都会把JWT放在HTTP请求头的Authorization字段内
- 后端检查是否存在,如果存在就验证JWT的有效性(签名是否正确,token是否过期)
- 验证通过后后端使用JWT中包含的用户信息进行其他的操作,并返回对应结果
一、知识点描述
1. JWT工作机制
-
基于Token的无状态认证协议
-
JWT由三部分组成(
Header.Payload.Signature):- Header:声明令牌类型(
JWT)及签名算法(如HS256或RSA),经Base64编码。 - Payload:包含用户信息(如用户ID、角色)及标准声明(
exp过期时间、iat签发时间等),同样Base64编码。 - Signature:使用密钥对编码后的Header和Payload进行签名,确保数据完整性
- Header:声明令牌类型(
-
生命周期:生成(服务端签发)→ 传输(客户端存储)→ 验证(签名校验)
2. 路由守卫(AOP控制)
- 前端路由层面的切面控制
- 通过路由meta字段标记权限需求
- 导航守卫自动检查登录状态并重定向
3. Axios拦截器
- 请求拦截器:自动附加Authorization头
- 响应拦截器:集中处理401等认证异常
- 统一错误处理管道,避免重复代码
4. 安全实践
- 敏感信息不存储于JWT(使用userId而非完整对象)
- HTTPS传输保障
- 客户端安全存储(localStorage/SessionStorage)
- 服务端密钥分离管理
二、使用缘由与优势
1. 为什么使用JWT?
- 解决传统Session问题: 消除服务端Session存储,天然支持分布式系统,避免集群同步问题
- 跨域支持友好: 通过简单添加Authorization头即可实现跨域认证,优于Cookie方案
- 信息自包含: Payload可携带非敏感用户信息,减少数据库查询次数
2. 为什么需要路由守卫?
- 集中访问控制: 避免在每个页面组件中重复编写权限校验逻辑
- 无缝跳转体验: 自动记录原始请求路径(redirect参数),登录后自动跳转目标页
- 防御性编程: 防止未授权用户通过直接URL访问受保护页面
3. 为什么封装Axios拦截器?
- 降低耦合度: 认证逻辑与业务组件解耦,修改认证策略只需调整一处
- 异常处理标准化: 统一处理网络错误、超时、Token过期等场景,提升代码健壮性
- 简化开发: 自动携带凭证,开发者无需在每个请求中手动添加Token
4. 安全设计的意义
- 防篡改: Signature签名机制防止Token被恶意修改
- 防泄露: 短期有效期(2h)+ 客户端安全存储降低被盗风险
- 防CSRF: 相比Cookie方案,Header携带方式天然免疫CSRF攻击
三、方案核心价值
- 提升系统扩展性 无状态特性使服务端容易水平扩展,适合微服务架构
- 优化用户体验 401自动跳转、凭证自动续传等机制让用户无感知完成认证流程
- 降低维护成本 模块化设计使认证逻辑与业务代码分离,升级维护只需修改独立模块
- 符合现代开发趋势 前后端完全分离,前端应用(SPA/移动端)可复用同一认证接口
JWT认证系统集成方案
一、服务端实现(Node.js/Express)
1. 依赖安装
npm install express jsonwebtoken cors body-parser
2. JWT工具类(utils/jwtUtil.js)
const jwt = require('jsonwebtoken');
const secret = 'your-secret-key'; // 生产环境建议使用环境变量
module.exports = {
// 生成Token(有效期为2小时)
generateToken: (user) => {
return jwt.sign(
{ userId: user.id, username: user.username },
secret,
{ expiresIn: '2h' }
);
},
// 验证中间件
verifyToken: (req, res, next) => {
const token = req.headers.authorization?.split(' ');
if (!token) return res.status(401).json({ message: '未提供认证令牌' });
jwt.verify(token, secret, (err, decoded) => {
if (err) {
const message = err.name === 'TokenExpiredError'
? '令牌已过期'
: '无效的认证令牌';
return res.status(401).json({ message });
}
req.user = decoded;
next();
});
}
};
3. 路由示例(routes/auth.js)
const router = require('express').Router();
const jwtUtil = require('../utils/jwtUtil');
// 登录接口
router.post('/login', (req, res) => {
// 验证用户凭证(示例代码,需完善实际验证逻辑)
const { username, password } = req.body;
const user = { id: 1, username: 'demo' }; // 模拟用户数据
// 生成Token
const token = jwtUtil.generateToken(user);
res.json({
token,
user: { id: user.id, username: user.username }
});
});
// 受保护接口
router.get('/profile', jwtUtil.verifyToken, (req, res) => {
res.json({ user: req.user });
});
module.exports = router;
二、前端实现(Vue/React)
1. Axios实例配置(src/api/axios.js)
import axios from 'axios';
const service = axios.create({
baseURL: process.env.VUE_APP_API_URL,
timeout: 10000
});
// 请求拦截器(添加Token)
service.interceptors.request.use(config => {
const token = localStorage.getItem('jwt_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
}, error => {
return Promise.reject(error);
});
// 响应拦截器(处理异常)
service.interceptors.response.use(
response => response.data,
error => {
if (error.response.status === 401) {
// Token失效处理
localStorage.removeItem('jwt_token');
window.location.href = '/login'; // 跳转登录页
}
return Promise.reject(error.response?.data || '请求失败');
}
);
export default service;
2. 路由守卫(Vue Router示例)
router.beforeEach((to, from, next) => {
const isAuthenticated = !!localStorage.getItem('jwt_token');
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!isAuthenticated) {
next({ path: '/login', query: { redirect: to.fullPath } });
} else {
next();
}
} else {
next();
}
});
3. 登录组件示例
import api from '../api/axios';
export default {
methods: {
async handleLogin() {
try {
const res = await api.post('/login', {
username: this.username,
password: this.password
});
localStorage.setItem('jwt_token', res.token);
// 跳转到首页或目标页面
this.$router.push(this.$route.query.redirect || '/');
} catch (err) {
console.error('登录失败:', err);
}
}
}
}
三、使用说明
-
服务端配置:
- 替换
your-secret-key为强密钥(推荐使用环境变量) - 添加CORS配置(开发环境)
JavaScript app.use(cors({ origin: process.env.CLIENT_URL, credentials: true })); - 替换
-
前端配置:
- 在.env文件中配置接口地址
VUE_APP_API_URL=http://your-api-domain.com- 受保护路由配置meta字段
JavaScript { path: '/dashboard', component: Dashboard, meta: { requiresAuth: true } } -
安全增强建议:
- 启用HTTPS
- 添加Refresh Token机制
- 设置适当的Token过期时间
- 使用HttpOnly Cookie存储Token(需调整CORS配置)
四、异常处理流程
-
401未授权:
- 自动清除本地Token
- 重定向到登录页
- 携带原始请求路径参数
-
Token过期:
- 服务端返回标准错误格式
- 前端自动触发登出流程
-
网络错误:
- 统一处理并显示错误提示
- 保留最后请求状态便于重试