109-《前后端分离实战》-身份认证

目录

  • 加密的概念
  • MD5加密使用
  • token概念和使用
  • 身份认证实现

一、加密基础

  • 前提

    • web应用中浏览器和服务器一定会有用户数据或支付数据等敏感信息在网络中进行传输,我们需要保证数据的安全传输,所以加密是必需的。

1.1 加密的概念

  • 概念:加密指利用了一定的算法根据原数据得到一个新的数据(加密数据)。而浏览器和服务器之间针对敏感数据一般使用的是其加密的数据完成用户登录等操作的交互。

  • 加密算法分类(针对web应用)

    • 非对称加密

      • 接受数据的一方有一对公钥和密钥,公钥主要是提供给发送数据的一方进行加密使用。而密钥只有接受数据的一方有。所以只要确保接受方密钥安全,基本上就可以杜绝大多数的数据传输的安全隐患。https协议底层就用的非对称加密,目前比较普及的就是rsa以及美国国家加密算法
    • 对称加密

      • 指数据发送方和接受方都有同一个加密算法(密钥)。在数据的传输过程中,都是用同一个加密算法完成数据的加密和解密。但如果其中一方的加密算法被破解,直接导致数据被暴露和丢失(篡改)

1. 2 MD5加密

  • 概念:MD5是一种常见的单向哈希加密算法。指使用了随机哈希散列值生成128位的加密字符串。所谓单向是指只能根据原数据得到其加密数据,但无法破解加密数据。具有一定的安全性。

  • 特点

    • 同样的原数据,会得到一样的加密数据。理论上只要一个数据库保存了所有原数据的md5加密数据,就可以实现md5算法的破解。
  • 使用

    • 每个编程语言都可以模拟实现md5加密,不过需要学习二进制以及十六进制的算法。可以使用已经写好的md5函数进行加密,得到原数据的加密数据
    xml 复制代码
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <script src="./MD5.js"></script>
        <script src="./jquery.min.js"></script>
    </head>
    <body>
        用户名 : <input type="text" id="username">
        密码:  <input type="password" id="userpass"> 
        <button onclick="login()">登录</button>
        <script>
            let word ='ad123';//原数据,假设为密码
            let word2 ='zs123';//原数据,假设为密码
            //得到md5算法的加密数据
            let code = hex_md5(word);
            let code2 = hex_md5(word2);
            console.log(code,code2);
            //登录
            function login(){
                let username = $("#username").val();
                let userpass = $('#userpass').val();
                let code = hex_md5(userpass);
                $.ajax({
                    url:'',
                    type:"post",
                    data:{
                        username,
                        userpass:code
                    },
                    success:function(res){
                        console.log(res.message);
                    }
                })
            }
        </script>
    </body>
    </html>

二、Token

  • 前提:身份认证是以Token为基础。学习token为了实现身份认证

2.1 token概念

  • 概念:token是一种基于jwt规范的一种落地安全数据的通用格式。本质上token是一个json数据(字符串)。每个token都有三个组成成分,保证数据在浏览器和服务器之间的安全传输。

2.2 jwt

  • 概念是jsonwebtoken的简称,是一种规定浏览器和服务器如何进行安全传输的数据规范,是一种标准,不是落地的技术方案,具体的落地方案为token。

  • jwt规范指定一个安全的数据格式是由三个部分组成

    • header:头部。主要规定preload的加密算法,一般为sha1加密算法。
    • preload:载荷:指需要安全传输的原数据
    • singuture:签名算法。基于Header里的算法以及ca证书,再次进行加密(对Header和preload再次进行加密,保证数据安全)。
  • jwt为了保证这三个部分构成的数据能够较快在浏览器和服务器之间进行传输,一般推荐json作为该数据的格式。一般我们称由这三个部分构成的加密数据为token,token格式如下

    css 复制代码
    header.preload.signuture

2.3 jsonwebtoken

  • npm提供了跟jwt(使用全称-jsonwebtoken)同名的包,用于生成和解密token,而这个包就是token的落地技术方案。我们可以在nodejs环境下进行token的新建和解密

    css 复制代码
    npm i jsonwebtoken
  • jsonwebtoken提供的两个api

    • 生成token:sign

      ini 复制代码
      const jwt = require('jsonwebtoken');
      let token = jwt.sign(preload,"自定义密钥字符串",配置对象);
      例子:生成一个20秒有效的token,负荷为:{username:"admin",userpass:"ad123"}
      let data = {username:"admin",userpass:"ad123"};
      let token = jwt.sign(data,"userkey",{
          expiresIn:20
      });
      console.log(token);
      • 第一个参数是自定义 的加密字符串,在解密时使用,一般一个token对应一个密钥字符串,即不同的token使用不同的密钥,用户自己编写,
      • 第二个参数就是需要安全传输的原数据。一般为一个对象
      • 第三个就是针对token本身的配置对象,比如设置token的有效期,一般为数字,指多少秒,也支持8h8d这样的字符串,表示8小时、8天
    • 解密token:verify

      ini 复制代码
      const jwt = require('jsonwebtoken');
      let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwidXNlcnBhc3MiOiJhZDEyMyIsImlhdCI6MTY5NzU5NDU4MCwiZXhwIjoxNjk3NTk0NjAwfQ.6ngi-rzJDRZfgIl3V9kQoG_YJ9cQKiR3wbh9zV3NtD8";
      //密钥和加密的是同一个
      let data = jwt.verify(token,'userkey');
      console.log(data);
  • 401错误

    • 当token已过有效期后,如果再次解密就会报已过有效期错误,请求状态码为401

三、身份认证

  • 目的:在用户进入需要登录的页面或需要进行非查询的操作时(比如修改用户信息,查看订单、修改头像等)需要验证用户是否已经登录同时处于有效期,才会继续操作,否则退回到登录界面。

3.1 大概思路

  • 前端在进行登录时,传递数据到后端,后端判断正确后将用户信息生成token并返回给前端,前端拿到token并保存到本地。后续要进入授权页面或进行授权操作时就需要将token作为请求头指定属性的值传递给后端,后端需要下载token专门鉴定npm包-expressjwt用于实现后端自动鉴定token是否有效。如果token失效,后端会直接返回401错误给前端,请求不会处理。但如果token有效,则正常服务器处理请求,并返回数据给前端。

3.2 前端部分

  • 第一部分:获取token并保存到本地

    • 1.用户进行登录时,就把数据发送给后端,登录成功后会接收到token,需要保存到token

      ini 复制代码
      $("#loginBtn").click(async function () {
        //发送请求
        let username = $("#username").val();
        let userpass = $("#password").val();
        //将密码进行MD5加密
        userpass = MD5.hex_md5(userpass);
        //发送请求
        let res = await userHttp.login({username,userpass});
        if (res.code == 200) {
           //保存token到本地
          //以下两种方式一样效果,二选一
          // localStorage.setItem('token',res.token);
          localStorage.token = res.token;
          //相对地址
          location.href = "index.html#/MoviesList";
        } else {
          //用户反馈
          alert(res.message);
        }
      });
  • 第二部分:判断token是否有效(进入需授权页面或进行需授权操作)

    • 一般为封装请求函数中添加对token的支持。比如之前写的axios.js可以修改一下,提供对token的支持

      javascript 复制代码
      export default function  ({url,type = "get",data={}}){
          //option就是调用请求需要的参数,使用了函数的默认参数,type和data带有默认值,也就是说为默认值可以不传这两个参数
          return new Promise(function(resolve,reject){
              $.ajax({
                  url,type,data,
                  //将token作为请求头的指定属性值发送给后端,后端利用express-jwt进行自动判断
                  headers:{
                      Authorization:localStorage.token
                  },
                  success:function(res){
                      resolve(res);//用await接受
                  },
                  //请求发生错误时触发
                  error:function(res){
                      if(res.status ==401){
                          alert('已过凭证有效期,请重新登录');
                          location.href="./login.html"
                      }
                  }
              })
          })
      }
    • 在需要鉴权的页面调用请求即可。比如在进入授权页面时可以发送一个获取登录用户信息的请求来验证用户是否已经登录

3.3 后端部分

3.3.1 处理登录请求

  • 1.后端项目下载jsonwebtoken和express-jwt :在后端项目根目录下打开终端:

    css 复制代码
    npm i jsonwebtoken  express-jwt
  • 2.处理用户的登录请求,判断正确后就将用户信息作为原数据生成token,并返回给前端

    javascript 复制代码
    const jwt = require('jsonwebtoken');
    router.post('/login',async function(req,res){
      let {username,userpass} = req.body;
      let isLogin = await userModel.find({username,userpass});
      if(isLogin.length >0){
        //生成token,负荷需要一个原生的JavaScript对象,不然会报错
        let token = jwt.sign({user:isLogin[0]},'user',{
          expiresIn:86400  //有效期一天 
        });
        res.send({code:200,message:'登录成功',token})
      }else{
        res.send({code:-1,message:'用户名或密码错误'})
      }
    });
    • 注意一点,生成token时需要保证负荷preload是一个原生的JavaScript对象,不然会报一下错误

3.3.2 进行token有效判断

  • 1.需要下载express-jwt

  • 2.需要书写针对express-jwt的验证配置代码,用于完成鉴权的配置操作。一般会在后端新建一个auth.js,将配置代码书写其中,之后引入到app.js中发挥作用

    php 复制代码
    auth.js:
    const {expressjwt} = require('express-jwt');
    const jwtAuth = expressjwt({
      //密钥必须是跟生成token用的是一样的
        secret: 'user',
        algorithms: ['HS256'], // 设置 jwt 的算法
        // 设置为 false,表示如果不带 token 的请求,不进行验证
        // 设置为 true,表示请求带不带 token 都要验证,如果不带 token 的请求,直接验证失败
        credentialsRequired: false
    }).unless({
        // 不需要进行验证的路由,即白名单VIP
        // path: ['/users/login', '/users/register']
        path: [//login/,//register/]
    })
    module.exports = jwtAuth;
  • 3.在app.js引入auth.js并使用,需注意的需要再路由关联app.use之前引入并使用,重启服务器后就会开始工作。

    ini 复制代码
    app.js:
    //引入auth
    var auth = require('./util/auth.js');
    //var app = express();
    //应用express-jwt:会在前端发送任意请求都会触发,只不过会忽略白名单里的请求地址
    app.use(auth);
    //路由匹配机制
    //app.use('/users', usersRouter);
  • 4.express-jwt识别token时强制要求token带有指定的前缀Bearer+空格,所以在生成token时需要手动加上该前缀,保证express-jwt的正常工作。不然会直接认为是401错误.需要修改后端登录处理的部分代码,修改后再将token发送给前端,前端保存的就是带有前缀的token

    ini 复制代码
    let token = jwt.sign({user:isLogin[0]},'user',{
          expiresIn:86400  //有效期一天 
        });
    token  = "Bearer "+token

3.4 身份认证主要流程

  • 前端

    • 用户登录时,需要将接受到的token保存到本地
    • 修改请求的函数,将token作为请求头并体统处理401错误的支持
  • 后端

    • 保证已经下载了express-jwtjsonwebtoken
    • 新建/util/auth.js书写express-jwt的配置代码,并引入到app.js并使用:app.use(auth)
    • 修改处理登录请求的函数,提供token 生成并返回给前端。注意加前缀Bearer
相关推荐
GISer_Jing42 分钟前
前端面试通关:Cesium+Three+React优化+TypeScript实战+ECharts性能方案
前端·react.js·面试
落霞的思绪2 小时前
CSS复习
前端·css
咖啡の猫4 小时前
Shell脚本-for循环应用案例
前端·chrome
百万蹄蹄向前冲6 小时前
Trae分析Phaser.js游戏《洋葱头捡星星》
前端·游戏开发·trae
朝阳5816 小时前
在浏览器端使用 xml2js 遇到的报错及解决方法
前端
GIS之路7 小时前
GeoTools 读取影像元数据
前端
ssshooter7 小时前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
Jerry8 小时前
Jetpack Compose 中的状态
前端
dae bal9 小时前
关于RSA和AES加密
前端·vue.js
柳杉9 小时前
使用three.js搭建3d隧道监测-2
前端·javascript·数据可视化