美文网首页
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