node登陆验证之jsonwebtoken
使用jsonwebtoken进行node端登陆注册验证
简介
简介:Token在计算机身份认证中是令牌(临时)的意思,在词法分析中是标记的意思。一般作为邀请、登录系统使用。
Token 是在服务端产生的。如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回 Token 给前端。前端可以在每次请求的时候带上 Token 证明自己的合法地位。如果这个 Token 在服务端持久化(比如存入数据库),那它就是一个永久的身份令牌(参考资料)
为什么使用它
token解决的问题如下
- Token 完全由应用管理,所以它可以避开同源策略
- Token 可以避免 CSRF 攻击(又引出一个知识点,推荐文章,前端知识真是如同太平洋广,如同马里亚纳海沟深)
- Token 可以是无状态的,可以在多个服务间共享
其它(cookie、session、token)
本实验主要采用token进行前后端验证,但是同时也有必要了解其他验证方式的原理,推荐看看下面的文章
博客链接:🔺🔺🔺前端鉴权的兄弟们:cookie、session、token、jwt、单点登录 - 掘金 (juejin.cn),通过该博客,你可了解到
- 基于 HTTP 的前端鉴权背景
- cookie 为什么是最方便的存储方案,有哪些操作 cookie 的方式
- session 方案是如何实现的,存在哪些问题
- token 方案是如何实现的,如何进行编码和防篡改?jwt 是做什么的?refresh token 的实现和意义
- session 和 token 有什么异同和优缺点
- 单点登录是什么?实现思路和在浏览器下的处理
代码
参考视频(强烈建议看,逻辑清晰,几乎不带一句废话,虽然有点长):1小时搞定NodeJs(Express)的用户注册、登录和授权_哔哩哔哩_bilibili🔺🔺🔺
步骤(该案例模拟的用户名为唯一值unique)
相关模块以及初始化
1
2
3
4
5
6
7
8
9
10
11const express = require('express'); //expree服务器
const bcrypt = require('bcrypt'); //加密
const jwt = require('jsonwebtoken'); //token
const User = require('./User.js'); //用户模块
const app = express();
// 定义密钥进行token加密,也可通过openssl进行私钥公钥配对,后面有解释
let SECRET = 'dongyuan666';
// 接收前端传递的json数据
app.use(express.json());用户名唯一,密码加密(User模块)
- bcrypt模块 -> set函数 ->
bcrypt.hashSync(val, number)
1
2
3
4
5
6
7
8
9
10
11
12const UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true //唯一值
},
password: {
type: String,
set: (val) => {
return bcrypt.hashSync(val, 10) //加密
}
},
});User完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22const mongoose = require('mongoose');
const bcrypt = require('bcrypt')
mongoose.connect('mongodb://localhost:27017/test-token', {
useNewUrlParser: true,
useCreateIndex: true,
});
const UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true
},
password: {
type: String,
set: (val) => {
return bcrypt.hashSync(val, 10) //加密
}
},
});
module.exports = mongoose.model('User', UserSchema)- bcrypt模块 -> set函数 ->
处理用户注册
- 判断当前用户是否已被注册:
User.find({ username: req.body.username,})
- 写入数据库:
User.create({username: req.body.username,password: req.body.password,})
1
2
3
4
5
6
7
8
9
10
11
12
13
14app.post('/register', async (req, res) => {
// console.log(req.body);
let users = await User.find({
username: req.body.username,
});
if (users.length != 0) return res.end('该用户已注册');
await User.create({
username: req.body.username,
password: req.body.password,
});
res.end('注册成功');
});- 判断当前用户是否已被注册:
处理用户登录
- 判断数据库是否有当前用户:
User.findOne({ username: body.username })
- 判断密码是否正确:
bcrypt.compareSync(body.password, user.password);
- 生成token:
jwt.sign({id}, SECRET, {options})
- 定义SECRET:
let SECRET = '自定义密钥内容';
- 定义SECRET:
- 发送token
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24let SECRET = '自定义密钥内容';
app.post('/login', async (req, res) => {
let body = req.body;
//1.判断用户是否存在
let user = await User.findOne({ username: body.username });
if (!user) return res.status(422).json({ errorCode: 1, message: '用户不存在' });
// 用户存在
// 2.密码校验
let isPasswordValid = bcrypt.compareSync(body.password, user.password);
if (!isPasswordValid) return res.status(422).json({ errorCode: 2, message: '密码错误' });
// 3.生成token
let token = jwt.sign(
{ id: String(user.id) }, //密码不要放进来,放一个唯一的东西就可以了
SECRET, //密钥
{
expiresIn: 60 * 60 * 24, //24h后失效
} //配置项
);
res.send(token);
});- 判断数据库是否有当前用户:
处理用户验证
- 获取token:
req.headers.authorization
- 解析token:
jwt.verify(raw, SECRET)
- 查找用户:
User.findById(id)
1
2
3
4
5
6
7
8
9app.get('/profile', async (req, res) => {
// 获取token
let raw = String(req.headers.authorization).split(' ')[1];
// 解析token并获取id
let id = jwt.verify(raw, SECRET).id;
// 查找用户
let user = await User.findById(id);
res.send(user);
});- 获取token:
将token验证过程拆解成中间件,并进行验证
1
2
3
4
5
6
7
8
9
10
11
12
13
14// 中间件
async function auth(req, res, next) {
// 获取token
let raw = String(req.headers.authorization).split(' ')[1];
// 解析token并获取id
let id = jwt.verify(raw, SECRET).id;
// 查找用户
req.user = await User.findById(id);
next();
}
app.get('/market', auth, async (req, res, next) => {
res.send(req.user);
});完整代码
1 | const express = require('express'); |
🔺非对称加密
在Node.Js中使用JWT实现Token用户验证🔺🔺🔺 or node token验证
非对称加密:RS256
私钥(private key):用于发布令牌
公钥(public key):用于验证令牌
非对称加密步骤
使用openssl生成公钥私钥
1
2
3openssl
OpenSSL> genrsa -out private.key 1024
OpenSSL> rsa -in private.key -pubout -out public.key新建一个文件用于读取密钥并导出(config.js)
1
2
3
4
5
6
7
8
9const fs = require('fs');
const path = require('path');
const PRIVATE_KEY = fs.readFileSync(path.resolve(__dirname, './keys/private.key'));
const PUBLIC_KEY = fs.readFileSync(path.resolve(__dirname, './keys/public.key'));
module.exports.PRIVATE_KEY = PRIVATE_KEY;
module.exports.PUBLIC_KEY = PUBLIC_KEY;导入密钥(app.js)
1
const { PRIVATE_KEY, PUBLIC_KEY } = require('./config.js');
使用私钥进行加密
1
2
3
4
5
6
7
8let token = jwt.sign(
{ id: String(user.id) }, //密码不要放进来,放一个唯一的东西就可以了
PRIVATE_KEY, //私钥加密
{
expiresIn: 60 * 60 * 24, //24h后失效
algorithm: 'RS256', //非对称加密
} //配置项
);使用公钥进行解密
1
jwt.verify(raw, PUBLIC_KEY, { algorithms: ['RS256'] })
Token安全之道
建议阅读:token拷贝到别人电脑上,禁止授权🔺
简短:
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Dong!
评论