关于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有效期,就需要重新登录了

参考文章
JWT优缺点
JWT详解


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!