第一次听说jwt鉴权登录的时候,觉得是一个非常牛逼的技术,非常高深,直到我最近开发一个网站使用jwt后我才发现,其实jwt非常简单,只要你看过一遍就能明白了。下面和我一起来学习下吧👇🏻
什么是jwt鉴权登录
jwt全称:json web token
它的功能可以简单概述为两种:其一是签发token,其二是验证token。
我简单用生活中的事情举个例子:
- 签发token就好比公安局给你的身份证,身份证就是你的token,它代表着你,记录着你的信息。
- 验证token就好比你坐火车进站刷身份证,火车站的系统能根据你的身份证确认你的信息
jwt就这两个功能
下面介绍下jwt的签发token是如何做的
token签发
首先看下token的组成,token是由三部分组成的
- header(这部分一般都是默认的,不需要我们管)
js
{
"alg": "HS256", // 加密算法
"typ": "JWT" // 令牌类型
}
还有很多其它的参数,这些参数都是可以使用默认的
- 隐私信息,这些信息可以使用用户信息组成
js
{
userId: '11111',
name: '张三'
}
这些信息由用户自己定义
- secretKey,这个说白了就是一个字符串,随便什么都可以,一般为了保密,都会设定一个字符串,然后使用加密算法加密后使用
js
abcdef
以上这三部分就是token的组成了,有了这三部分就可以构建最终的token,构建方法就将这三部分分别加密,然后使用 . 将这三部分连接起来
js
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsImlhdCI6MTc0Mzc1OTY2NywiZXhwIjoxNzQzNzYzMjY3fQ.Gha7H8S12RXhWSd4BXdtQVAYETivKDgyX_VtqpNKTAo
这个token例子就是我生成的
生成方法
js
const token = jwt.sign(
{
userId: user.id
},
process.env.SECRET,
{ expiresIn: '1h' }
);
第一个参数:用户信息
第二个参数:secretKey
第三个参数:过期时间
我们登录过程中将生成的token返回给前端,返回的方式可以加在cookie中
前端传递token给服务端
js
// 前端获取到token在请求时需要传递给后端,一般是在header离传递过去
// 以axios为例
// 传递方式一般会使用 'Bearer token'这种方式
axios.post('https://api.com/login', {
headers: {
authorization: 'Bearer token'
}
})
后端接收方式
js
// 服务端会在中间件中校验登录信息,便于所有接口都能够使用
module.exports = async function(req, res, next) {
try{
//判断Token是否存在
const authHeader = req.headers.authorization || req.query.token;
const token = authHeader && authHeader.split(' ')[1];
if(!token){
throw new Error('当前接口需要认证才能访问。')
}
const decoded = jwt.verify(token, process.env.SECRET);
const { userId } = decoded;
const user = await User.findByPk(userId);
if(!user) {
throw new NotFoundError('用户不存在');
}
req.userInfo = user;
next();
} catch (error){
fail(res,error);
}
}
好了,以上差不多就是jwt的整体能力,它的流程还是挺简单的,通过这篇文章你就能实现一个非常简单的jwt鉴权登录了,快去试一试
当然这只是登录的其中一种方式,还有就是大企业都选择的SSO单点登录方式,这个好像是挺牛的,我还没搞懂,等我搞懂,下一节我们一起来探讨下,不会让大家等太长时间的
下面是jwt鉴权登录的实例,大家可以体验下
express服务端
js
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const cors = require('cors');
const app = express();
app.use(express.json());
app.use(cors());
const PORT = 8008;
const JWT_SECRET = 'jwt_secret_code_custom';
// 模拟用户
let users = [
{ id: 'abc', name: '张三', email: '11@qq.com' }
];
// 登录接口
app.post('/api/login', async (req, res) => {
const { email, password } = req.body;
const user = users.find(u => u.email === email);
if (!user) return res.status(401).json({ message: '用户不存在' });
const validPass = await bcrypt.compare(password, user.password);
if (!validPass) return res.status(401).json({ message: '密码错误' });
// 生成JWT
const token = jwt.sign(
{ userId: user.id, email: user.email },
JWT_SECRET,
{ expiresIn: '1h' }
);
res.json({ token });
});
// 资源接口,接口会优先执行中间件
app.get('/api/protected', authenticateToken, (req, res) => {
res.json({
message: '这是受保护的内容',
user: req.user
});
});
// JWT验证中间件
function authenticateToken(req, res, next) {
try{
//判断Token是否存在
const authHeader = req.headers.authorization || req.query.token;
const token = authHeader && authHeader.split(' ')[1];
if(!token){
throw new Error('当前接口需要认证才能访问。')
}
const decoded = jwt.verify(token, process.env.SECRET);
const { userId } = decoded;
const user = await User.findByPk(userId);
if(!user) {
throw new NotFoundError('用户不存在');
}
req.userInfo = user;
next();
} catch (error){
console.log(error)
}
}
app.listen(process.env.PORT, () => {
console.log(`服务运行在 http://localhost:${PORT}`);
});
前端react
js
// App.js
import React, { useState } from 'react';
import axios from 'axios';
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
// 配置axios
axios.defaults.baseURL = 'http://localhost:8008/api';
// 登录组件
const Login = ({ setToken }) => {
const [formData, setFormData] = useState({ email: '', password: '' });
const handleSubmit = async (e) => {
e.preventDefault();
try {
const res = await axios.post('/login', formData);
setToken(res.data.token);
} catch (err) {
alert(err.response?.data?.message || '登录失败');
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
placeholder="邮箱"
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
required
/>
<input
type="password"
placeholder="密码"
value={formData.password}
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
required
/>
<button type="submit">登录</button>
</form>
);
};
// 受保护页面
const ProtectedPage = () => {
const [data, setData] = useState(null);
const fetchData = async () => {
try {
const token = localStorage.getItem('token');
const res = await axios.get('/protected', {
headers: { authorization: `Bearer ${token}` }
});
setData(res.data);
} catch (err) {
console.error('获取数据失败:', err);
}
};
return (
<div>
<h2>受保护页面</h2>
<button onClick={fetchData}>获取数据</button>
{data && <pre>{JSON.stringify(data, null, 2)}</pre>}
</div>
);
};
// 路由配置
const App = () => {
const [token, setToken] = useState(localStorage.getItem('token'));
const handleSetToken = (newToken) => {
localStorage.setItem('token', newToken);
setToken(newToken);
};
return (
<Router>
<Routes>
<Route path="/" element={
token ? <Navigate to="/protected" /> : <Login setToken={handleSetToken} />
} />
<Route path="/protected" element={
token ? <ProtectedPage /> : <Navigate to="/" />
} />
</Routes>
</Router>
);
};
export default App;
⭐️关注我,一起掌握新技术