关于JWT认证
开头
- 但凡有登陆模块的程序,都会涉及到用户身份认证的问题;目的就是确保来访问是真的用户
- 所以记录一下最近这一块学习/实验的笔记
- 实现代码均为部分,整体代码点这里
工作流程
- 用户登陆,如果登陆信息和服务端保存的信息一致,服务端会通过一些机制生成一串字符串,返回给客户端
- 客户端保存该字符串,并在之后的每次请求中携带该字符串
- 服务端接收到请求后,会验证该字符串是否有效、是否过期;如果都没有就正常返回数据,如果失效、过期就返回对应的提示信息
- 这串字符串就是token令牌
小插曲
- 做登陆等需要处理用户账号密码等私密的操作时,要对这些隐私信息进行加密,在做操作、保存等
- 目的:1.职业操守,开发人员要尊重用户的这些私密信息;2.防止被其他人攻击,拿到密码等私密信息进行一些不好的操作
加密的代码实现
- 需要用到bcrypt模块
- 对注册密码进行加密并保存,方便演示,数据直接保存到本地数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60...
const express = require('express')
const router = express.Router()
const bcrypt = require('bcrypt')
// 保存注册信息
let users=[]
// 生成salt的等级
const saltRounds = 10
// 注册
router.post('/register', (req, res, next) => {
/**
body={
name:'shuaxin',
psw:'shuaxin'
}
*/
// 接受前端数据
let obj = req.body
// 生成salt
const salt = bcrypt.genSaltSync(saltRounds)
// 利用salt对密码进行hash生成加密字符串
const hash = bcrypt.hashSync(obj.psw, salt)
// 然后保存该条数据等操作
users.push({
name:obj.name,
psw:hash
})
// 返回提示
res.json({
msg: 'register succ'
})
})
// 登陆
router.post('/login', (req, res, next) => {
let obj = req.body
// 查找对应的账号 然后校对密码是否一致
for (let index in users) {
if (users[index].name === obj.name) {
// 校对结果为Boolean
// 参数列表(用户输入的面,存储的加密后的密码)
let result = bcrypt.compareSync(obj.psw, users[index].psw)
if (result) {
res.json({
code: 1,
msg: '登陆成功',
token
})
} else {
res.json({
code: -1,
msg: '检查账号密码是否匹配'
})
}
break
}
}
})
...
生成token代码实现
- 用到的模块jsonwebtoken
- 登陆成功返回生成的token
- 在上文代码的登陆逻辑区域操作,省略重复代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31...
// 引入模块
const jwt = require('jsonwebtoken')
// 设置私钥
const sk = 'shuaxin'
...
// 登陆
router.post('/login', (req, res, next) => {
let obj = req.body
for (let index in users) {
if (users[index].name === obj.name) {
let result = bcrypt.compareSync(obj.psw, users[index].psw)
// 生成token 参数:(要加密的内容,秘钥,时间(s为单位))
let token = jwt.sign(users[index], sk, { expiresIn: 10 });
if (result) {
res.json({
code: 1,
msg: '登陆成功',
token
})
} else {
res.json({
code: -1,
msg: '检查账号密码是否匹配'
})
}
break
}
}
})
... - 验证token是否有效、过期
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31...
// 引入模块
const jwt = require('jsonwebtoken')
// 设置私钥
const sk = 'shuaxin'
...
router.post('/testToken', (req, res, next) => {
let obj = req.body
// 过期后会抛异常
try {
// 校验token 结果为Boolean
// 参数列表(token,私钥)
var decoded = jwt.verify(obj.token, sk);
if (decoded) {
res.json({
code: 1,
msg: '有效的token'
})
} else {
res.json({
code: -1,
msg: 'token无效'
})
}
} catch (error) {
res.json({
code: -1,
msg: 'token过期'
})
}
})
其他笔记
- 看jwt文档的时候看到了HMAC SHA256、RSA SHA256
- 这两个是不同的加密算法模式,前者是默认的,后者可以自己设置
- 通过搜索引擎了解到常见加密算法模式有:DES,AES, RSA, MD5, SHA, HMAC, base64,区别有待学习
- SHA256是一种使用了哈希长度为256的算法,太菜了,底层就不懂了
- DES,AES, RSA, MD5, SHA, HMAC, base64区别
关于JWT的优缺点
优缺点都是对比出来的,和常用的session进行对比,由于实践有限,session没用过,总结是搜索引擎搜索后,罗列的自己能理解的
JWT优点
- 减轻服务器压力,因为生成后直接返回给客户端,服务端就无须保存,session需要维持每个用户的状态
- 避免跨域攻击,因为服务端会对每个请求进行token校验,不存在或者无效都没法获取数据,而token保存在用户自己的客户端,只要不在这里被窃取,来自网络的伪造请求一般都无法完成
- 适合移动端,没做过移动端,查了查资料,因为session会话状态的控制需要依赖cookie,客户端对cookie的支持很有限,所以session不适合移动端
- 实现单点登录,因为cookie不能跨域,session依赖cookie,token不存在这个问题
JWT缺点
- 注销登录、忘记密码等后,token还有效,这样服务端需要加一些额外操作来处理
- 使用UID和token对应
- 每个用户加密token使用单独的salt,过期改一下salt即可
- token有效期续签问题,续签的目的就是为了防止用户频繁需要登录,token也需要额外的一些解决方案
- 每次请求,都更新一个新token,如果之间没有请求,过了token有效期,就需要重新登录了
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!