美文网首页
114.JWT(json web token)实现权限认证过程

114.JWT(json web token)实现权限认证过程

作者: wo不是黄蓉 | 来源:发表于2022-10-11 21:56 被阅读0次
    前方高能最新消息热点公众号首图.jpg

    JWT(json web token)实现权限认证过程

    b站跟着哈默的视频一起敲的代码,和视频里面内容不一样的地方

    • 视频略过了项目搭建的过程
    • 视频中使用的是选项式api,我用的是组合式api(顺便实战一下vue3的编程风格)

    最终的实现效果:
    制作登录页面实现获取token,登录成功后使用jsonwebtoken验证token有效性,验证成功之后跳转到list页面,在List页面做权限用户控制,获取信息接口不做权限控制,新增接口做权限控制。
    使用js-base64对token进行加密,服务端使用basic-auth验证token。

    使用element3+vue3搭建客户端框架:

    npm install vue@next 安装最新版vue

    npm i -g @vue/cli 安装最新版本vue-cli

    vue create jwt-client 创建客户端项目

    npm install element-plus --save 使用element-plus

    main.js中

    import { createApp } from "vue";
    //引入elementplus
    import ElementPlus from "element-plus";
    import "element-plus/dist/index.css";
    import App from "./App.vue";
    
    const app = createApp(App);
    app.use(ElementPlus).mount("#app");
    

    npm install babel-eslint 解决 eslint 的 Parsing error: Unexpected token 错误

    @vue/cli-plugin-babel 解决vue文件template报错问题

    npm install axios

    npm install vue-router@4

    import router from "./router/index";
    
    const app = createApp(App);
    app.use(ElementPlus).use(router).mount("#app");
    

    npm install js-base64

    新建views文件夹,新建login.vue文件,实现登录页面,login.vue

    登录成功后,token接口返回token信息,前端将token存在localStorage中,登录成功后跳转到列表页

    <template>
      <el-form :model="form" label-width="80px" ref="ruleFormRef">
        <el-form-item label="用户名" prop="username">
          <el-input v-model="form.username" />
        </el-form-item>
        <el-form-item label="密码" prop="password">
          <el-input v-model="form.password" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="onSubmit(ruleFormRef)">登录</el-button>
          <el-button type="primary" @click="resetForm(ruleFormRef)">重置</el-button>
        </el-form-item>
      </el-form>
    </template>
    
    <script setup>
    import { reactive, ref } from "vue";
    import axios from "axios";
    import { useRouter } from "vue-router";
    const { push } = useRouter();
    //设置ref
    const ruleFormRef = ref();
    //设置响应式数据
    const form = reactive({
      username: "",
      password: "",
    });
    // 提交表单
    const onSubmit = (formEl) => {
      if (!formEl) return;
      console.log(formEl, form);
      formEl.validate((valid) => {
        if (valid) {
          //默认用得get请求,请求方式不对请求不到
          axios
            .post("/token", {
              username: form.username,
              password: form.password,
            })
            .then((res) => {
              const result = res.data;
              if (result.status) {
                localStorage.setItem("token", result.token);
                verifyToken();
              }
              console.log(res);
            });
        } else {
          return false;
        }
      });
    };
    
    //重置表单
    const resetForm = (formEl) => {
      if (!formEl) return;
      formEl.resetFields();
    };
    
    //验证token有效性
    const verifyToken = () => {
      axios
        .post("/verify", {
          token: localStorage.getItem("token"),
        })
        .then((res) => {
          debugger;
          const result = res.data;
          if (result.isValid) {
            //路由跳转
            // this.$router.push("/list");
            push({
              path: "/list",
            });
            ElMessage({
              message: "登录成功",
              type: "success",
            });
          }
        });
    };
    </script>
    

    列表页,使用按钮模拟权限,获取文章内容不需要用户权限控制,新增文章内容需要进行用户权限控制。

    <template>
      <div class="home">
        <el-button @click="getContent">获取文章内容</el-button>
        <el-button @click="addContent">新增文章内容</el-button>
      </div>
    </template>
    <script setup>
    import axios from "axios";
    import { ElMessage } from "element-plus";
    import { Base64 } from "js-base64";
    const getContent = () => {
      axios.get("/content").then((res) => {
        console.log(res);
        ElMessage({
          message: res.data.msg,
          type: "success",
        });
      });
    };
    const addContent = () => {
      axios({
        url: "/content",
        method: "post",
        headers: {
          Authorization: _encode(),
        },
      }).then((res) => {
        if (res.data.status) {
          ElMessage({
            message: res.data.msg,
            type: "success",
          });
        } else {
          ElMessage({
            message: res.data.msg,
            type: "error",
          });
        }
      });
    };
    //使用base64加密token
    const _encode = () => {
      const token = localStorage.getItem("token");
      const encoded = Base64.encode(`${token}:`);
      return `Basic ${encoded}`;
    };
    </script>
    

    配置vue.config.js中proxy代理访问5000端口数据
    使用koa+koa-router搭建服务端:

    新建jwt-server目录npm init 初始化package.json

    npm install koa 创建服务器

    npm install koa-bodyparser 解析请求中的body中的数据

    npm i @koa/router --save

    npm install jsonwebtoken

    npm install basic-auth

    新建app.js

    const Koa = require("koa");
    const bodyParser = require("koa-bodyparser");
    
    const app = new Koa();
    //获取Body内容需要使用Bodyparser
    //开启一个后端服务,使用5000端口
    app
      .use(bodyParser())
      .listen(5000);
    

    实现获取token接口,使用koa-router模拟请求路径,最后导出router,在app.js中引入,并且使用

    新建api文件夹,新建token.js

    //使用@koa/router生成访问路径
    const Router = require("@koa/router");
    //模拟用户信息
    const users = [
      { id: 1, username: "zhangsan", password: 123456, nickname: "张三" },
      { id: 2, username: "lisi", password: 123456, nickname: "李斯" },
    ];
    //生成token信息
    const { generatorToken } = require("../../core/utils");
    
    //验证token信息得类
    const Auth = require("../../middlewares/auth");
    const router = new Router();
    
    
    router.post("/token", async (ctx, next) => {
       //从body中获取传参
      const { username, password } = ctx.request.body;
      const token = verifyUsernamePassword({ username, password });
    
      if (!token) {
        ctx.body = {
          errCode: 10001,
          msg: "用户名或密码不正确",
          request: `${ctx.method} ${ctx.path}`,
          status: false,
        };
    
        return;
      }
      ctx.body = {
        token,
        status: true,
      };
    });
    
    //验证token有效性
    router.post("/verify", async (ctx) => {
      const token = ctx.request.body.token;
      const isValid = verifyToken(token);
      ctx.body = {
        isValid,
      };
    });
    
    //验证用户名和密码
    function verifyUsernamePassword(user) {
      //在此可以定义后端返回得数据格式
      const index = users.findIndex((item) => {
        return item.username === user.username && item.password == user.password;
      });
    
      const userInfo = users[index];
      if (!userInfo) {
        return undefined;
      }
    
      //2:user权限数字
      const token = generatorToken(user.id, Auth.USER);
      return token;
    }
    
    //验证token
    function verifyToken(token) {
      return Auth.verifyToken(token);
    }
    module.exports = router;
    

    在app.js中使用router

    const Koa = require("koa");
    const bodyParser = require("koa-bodyparser");
    
    const tokenRouter = require("./app/api/token");
    
    const app = new Koa();
    //获取Body内容需要使用Bodyparser
    app
      .use(bodyParser())
      .use(tokenRouter.routes())
      .listen(5000);
    

    utils.js中generatorToken方法用来生成token

    const jwt = require("jsonwebtoken");
    const { secretKey, expiresIn } = require("../config/config");
    //生成token令牌
    const generatorToken = function (uuid, scope) {
      const token = jwt.sign({ uuid, scope }, secretKey, { expiresIn });
    
      return token;
    };
    module.exports = { generatorToken };
    

    中间件auth.js,验证token信息以及进行权限认证相关逻辑

    const jwt = require("jsonwebtoken");
    const { secretKey } = require("../config/config");
    
    class Auth {
      static verifyToken(token) {
        try {
          jwt.verify(token, secretKey);
          return true;
        } catch (error) {
          return false;
        }
      }
    }
    
    module.exports = Auth;
    

    通用配置config.js,生成令牌和验证时使用

    module.exports = {
      secretKey: "jK123uu_s$!",
      expiresIn: 24 * 60 * 60,
    };
    

    内容相关api

    const Auth = require("../../middlewares/auth");
    
    const Router = require("@koa/router");
    const router = new Router();
    
    router.get("/content", (ctx) => {
      ctx.body = {
        result: "获取内容成功",
        msg: "操作成功",
        status: true,
      };
    });
    
    router.post("/content", new Auth(5).middleware, (ctx) => {
      ctx.body = {
        result: "新增内容成功",
        msg: "操作成功",
        status: true,
      };
    });
    
    module.exports = router;
    

    middleware方法

    const jwt = require("jsonwebtoken");
    const { secretKey } = require("../config/config");
    var auth = require("basic-auth");
    
    class Auth {
      constructor(level) {
        Auth.USER = 2;
        Auth.ADMIN = 8;
        this.level = level;
      }
      get middleware() {
        return async (ctx, next) => {
          var token = auth(ctx.request);
          let errMsg = "token不合法";
            //token不存在
          if (!token || token.name === "null") {
            ctx.body = {
              errCode: 10005,
              msg: errMsg,
              request: `${ctx.body} ${ctx.path}`,
              status: false,
            };
    
            return;
          }
    
          try {
              //验证token
            var decoded = jwt.verify(token.name, secretKey);
          } catch (e) {
              //token过期
            if (e.name === "tokenExpiredError") {
              errMsg = "token已过期";
            }
            ctx.body = {
              errCode: 10005,
              msg: errMsg,
              request: `${ctx.body} ${ctx.path}`,
            };
    
            return;
          }
            //判断用户权限
          if (decoded.scope < this.level) {
            errMsg = "没有权限";
            ctx.body = {
              errCode: 10005,
              msg: errMsg,
              request: `${ctx.body} ${ctx.path}`,
            };
            return;
          }
          await next();
        };
      }
      static verifyToken(token) {
        try {
          jwt.verify(token, secretKey);
          return true;
        } catch (error) {
          return false;
        }
      }
    }
    module.exports = Auth;
    

    完整代码:https://github.com/Cloverao/jwt

    相关文章

      网友评论

          本文标题:114.JWT(json web token)实现权限认证过程

          本文链接:https://www.haomeiwen.com/subject/egbzartx.html