身份认证问题--jwt--token--登录拦截

450 阅读1分钟
  • 对于前后端分离模式的开发,大多使用 JWT(json web token)进行身份认证。
  • token:当成功登录后,后端就返回一个凭据(本质是一个长长的字符串),有了这个凭据之后,后续再发请求接口,带上它就可以访问那些需要权限的接口。
  • 流程图:

  image.png

  • 生成token基本步骤
    • 在项目中安装 jsonwebtoken : npm i jsonwebtoken
    • 加载模块 const jwt = require('jsonwebtoken')
    • 在用户登录成功之后,调用 jwt.sign() 方法创建 token,有如下四个参数:
      • 参数1:必填,对象形式;希望在token中保存的数据
      • 参数2:必填,字符串形式;加密的钥匙;后续验证token的时候,还需要使用
      • 参数3:可选,对象形式;配置项,比如可以配置token的有效期(单位秒)
      • 参数4:可选,函数形式;生成token之后的回调
      • 生成的token前面,必须拼接 Bearer这个字符串。
  • 参考代码:
// 后端--生成 token

const jwt = require('jsonwebtoken')
// 用户登陆方法
app.post('/login', (req, res) => {
  // 其它代码......
  // 调用生成 token 的方法
  const tokenStr = jwt.sign({name: 'xxx' }, 'l-ddui', { expiresIn: 20 });
  const token = 'Bearer ' + tokenStr
  // 把 token 字符串 返回 给客户端浏览器
  res.send({ code: 200, msg: '登录成功',  token})
})



//前端--保存 token

localStorage.setItem('token',token)


// ajax 请求携带 token

$.ajax({
    method: "",
    url: "",
    // 按照规范请求头传递 Authorization
    headers: {
      Authorization: localStorage.getItem('token'),
    },
    data: {...},
    success: (res) => {	
     ...
    })

  • 中间件验证 token
    • 选择使用 express-jwt 第三方模块进行身份认证。从模块名可以看出,该模块是专门配合express使用的。
    • 安装 express-jwt:npm i express-jwt
  • 参考代码:
const jwt=require('express-jwt')
// app.use(jwt().unless());
// jwt() 用于解析 token ,并将 token 中保存的数据赋值给 req。user
// unless() 约定某个接口不需要身份认证
app.use(jwt({
	secret:'l-ddui',        // 生成 token 时的钥匙,必须和生成 token 时设置的统一
  algorithms: ['HS256']  // 必填,加密算法,无需了解
   }).unless({
  path: ['/user/login', '/user/register'] // 除了这两个接口,其他都需要认证
}))
  • 上述代码完成后,当一个接口请求到了服务器后,它会自动验证请求头中的 Authorization 字段了,并且会自动完成:
    • 如果没有问题
      • 将token中保存的数据赋值给req.user
      • next( )
    • 如果有问题,则抛出错误  next ( 错误信息 )
  • 错误处理 -- 中间件技术
    • 在所有的路由最后,加入错误处理中间件,来提示token方面的错误。
app.use((err, req, res, next) => {
  if (err.name === 'UnauthorizedError') {
    // res.status(401).send('invalid token...');
    res.status(401).send({ status: 1, message: '身份认证失败!' });
  }
});
  • 前端统一处理401错误(身份认证出错)
$(document).ajaxError(function( event, request, settings ) {
  if(request.status === 401) {
    // alert(request.responseJSON.msg)
    return (location.href = './login.html')
  }
})
  • 前端携带 token
  1. 统一携带
$.ajaxSetup({
  headers: {
    Authorization: localStorage.getItem('token'),
  }
})
  1. 选择性携带--(附登录拦截代码)
// $.ajaxPrefilter() 可以在调用 $.get()  $.post() $.ajax() 之后,立即调用
$.ajaxPrefilter(function (options) {
    // 开发环境服务器地址
    let baseURL = 'http://127.0.0.1:10086'

    // 手动添加根路径
    options.url = baseURL + options.url
  
    // 如果服务器路径包括 /my/ 则进行身份认证
    if (options.url.indexOf('/my/') != -1) {
        options.headers = {
            Authorization: localStorage.getItem('token') || ''
        }

      
      // 登录拦截 -- complete 方法
        options.complete = function (res) {
            // console.log(res.responseJSON);

            let obj = res.responseJSON
            if (obj.status == 1 && obj.message == "身份认证失败!") {
                // 销毁 token
                localStorage.removeItem('token')
                // 跳转到首页
                location.href = '/login.html'
            }
        }
    }
})