【场景方案】适用于简单后台管理的登陆退出鉴权解决方案(vue)

前言

文章内容是我的crm解决方案里实现的原理的解析crm解决方案

该套方案只适用于中小型后台管理工程中,逻辑比较简单,安全性也不是很好,可作为解决方案初学者学习之路的垫脚石,觉得写的不错点个赞吧!

有些代码示例中看不到引入,因为我这里只记录实现的思路,具体的可以看我项目源码。


api管理

登陆的api可以放在公共api文件中,例如src/api/sys.js

// 系统级的接口
import request from "@/utils/request";

/**
 * 登请求
 */
export const login = (data) => {
  return request({
    url: "/sys/login",
    method: "POST",
    data,
  });
};

/**
 * 获取用户信息
 */
export const getUserInfo = () => {
  return request({
    url: "/sys/profile",
  });
};

/**
 * 获取所有权限
 */
export const permissionList = () => {
  return request({
    url: "/sys/permission",
  });
};

登陆逻辑方法

关于登陆的相关逻辑推荐写在vuex中的用户模块src\views\login\index.vue,这样的写好处是:

  1. 登陆相关逻辑可以在除了普通登陆的场景中使用,还可以在后续的需求中使用。
  2. 能储存一些用户状态,方便在各个页面中使用。

简单语言总结一下

登陆逻辑:

  1. 获取用户输入的账户和密码(密码需要做加密)
  2. 发送登陆请求
  3. 获取到token,设置好token,前端计时token(无感刷新token的方案以后再说)
  4. 跳转到首页

获取用户信息:

  1. 在全局路由守卫中获取用户信息(和权限一起处理)

退出登陆:

  1. 清空本地token和用户信息,以及本地所有数据
  2. 跳转至登陆页面
// 引入略...

export default {
  namespaced: true,
  state: () => ({
    token: getItem(TOKEN) || "",
    userInfo: {}, // 用户信息
  }),
  mutations: {
    // 设置token
    setToken(state, token) {
      state.token = token;
      setItem(TOKEN, token); // 具体实现先不管
    },
    // 设置用户信息
    setUserInfo(state, userInfo) {
      state.userInfo = userInfo;
    },
  },
  actions: {
    // 登陆动作
    login(context, userInfo) {
      const { username, password } = userInfo; // 拿到表单填入的
      return new Promise((resolve, reject) => { // 返回promise为了让组件调用的时候能做异步处理
        login({ // 调用登陆接口
          username,
          password: md5(password), // 加密处理(不推荐使用md5,推荐使用前后端配合的公私钥加密)
        })
          .then((data) => {
            this.commit("user/setToken", data.token); // 存储token
            // 登录后操作
            // 为什么不接着去请求用户数据,要放在权限相关的路由守卫中,是因为浏览器刷新的时候不会走这里,也就拿不到最新的用户数据
            router.push("/");
            setTimeStamp(); // 设置token获取的时间(具体实现先不用管)
            resolve();
          })
          .catch((err) => {
            reject(err);
          });
      });
    },

    // 获取用户信息
    async getUserInfo(context) {
      const res = await getUserInfo(); // 调用获取用户信息的接口
      this.commit("user/setUserInfo", res);
      return res; // 可以返回出去,可能以后会用到
    },

    // 退出登陆
    logout() {
      // 清空vuex相关数据
      this.commit("user/setToken", "");
      this.commit("user/setUserInfo", {});
      removeAllItem(); // 清空所有localStorage数据
      router.push("/login");
    },
  },
};

密码加密

md5加密

这个是最简单的密码明文加密方式,但个人认为只能使用在登陆动作上,不能使用在注册动作上。因为登陆时,数据库是能够找到对应用户名的密码,同样根据md5加密后与前端发送来的加密后密码做比对,一致则登陆成功。注册就不能这样子了。

并且,如果md5加密方式不做其他处理,使用暴力解法一般能够获取到原值。所以不推荐使用。

jsencrypt公私钥加密

使用公私钥加密可以纯运用在前端,比如“记住我”的动作,还可以用在与后端交互的登陆和注册动作,当然后者是需要后端提供公钥的。

下载第三方库jsencrypt,对应代码写在目录源码src\utils\jsencrypt.js中。


比较靠谱的登陆方案思路

知道了上面的思路后,我这里提一下一种比较靠谱的登陆方案(源码里没实现),可作为参考。

先填入用户账号和密码,并作人工验证(验证是否人工和防止高并发请求),验证通过后拿到人工验证成功码。

用账号和密码以及其他所需数据(例如手机端指纹识别码fingerprintjs2、人工验证成功码等等)去发送登陆请求,在此之前先向后端请求获取加密的公钥,用这个公钥加上前端生成的uuid码与数据通过JSEncrypt打包加密。

发送登陆请求后拿到token码,用token去发送请求获取用户的后端状态,例如,如果有需要验证的状态,就跳转到手机验证页面,输入手机验证码后发送请求与后端对比验证,对比成功后,再用公钥把手机端指纹识别码加密后发送给后端,后端返回用户数据、权限数据、界面数据等信息,前端存储起来,跳转到首页。 跳转到首页的同时做个回调通过账户id发送接口,进一步获取用户的详细信息。全程注意失败的情况。

使用token的好处就是,因为token是服务端的算法生成的,所以拿到客户端传来的token只需要解密即可,token都由客户端存储,减少服务端的压力。


登录鉴权

继续我们的代码实现。

这里登录鉴权主要分为两个角度:

  1. 当用户未登陆时,只能进入登陆页面。
  2. 登陆后,token没过期时,不能进入登陆页面。

具体在目录src\permission.js文件中,与用户信息获取逻辑一起处理,且用全局路由守卫去触发。

路由守卫的主要大致逻辑分为:

已有token:

  • 判断是否已有token,如果有token了,且在登陆页面,就自动跳转到首页。
  • 已有token进入除登陆页外的其他页面,判断是否有用户信息,没有说明是新打开的页面,就去请求接口获取用户信息。
  • 获取到用户信息后,处理里面的权限信息,例如路由权限(这里先不涉及,以后开个新的文章讲)。

没有token:

  • 判断没有token,说明没有登陆,那么看是否访问的白名单页面,不是就强制跳转至登陆页面。
/* 处理登陆的路由鉴权 */
import router from "./router";
import store from "./store";
/* 页面跳转进度条 */
import NProgress from "nprogress";
import "nprogress/nprogress.css";
NProgress.configure({ showSpinner: false });

// 白名单
const whiteList = ["/login"];
/**
 * 路由前置守卫
 */
router.beforeEach(async (to, from, next) => {
  // 没想到async还可以写在这里
  NProgress.start(); // 开启进度条
  // 存在 token说明已登陆,如果进去的是login页面给跳到首页,如果去其他页面放行
  if (store.getters.token) {
    if (to.path === "/login") {
      next("/");
      NProgress.done();
    } else {
      // 判断用户资料是否获取
      // 若不存在用户信息,则需要获取用户信息
      if (!store.getters.hasUserInfo) {
        // 为什么不把用户数据存在本地缓存,应该是为了安全考虑
        // 触发获取用户信息的 action,并获取用户当前权限
        const { permission } = await store.dispatch("user/getUserInfo");
        // 处理用户权限,筛选出需要添加的权限 ------------ 分割线之内的可以先不用看,以后讲动态菜单会去细讲
        const filterRoutes = await store.dispatch(
          "permission/filterRoutes",
          permission.menus
        );
        // 利用 addRoute 循环添加动态路由配置
        // console.log("filterRoutes", filterRoutes);
        filterRoutes.forEach((item) => {
          router.addRoute(item);
        });
        // 添加完动态路由之后,需要在进行一次主动跳转(完成用户的页面跳转动作)-----------
        return next(to.path);
      }
      next();
    }
  } else {
    // 没有token的情况下,可以进入白名单
    if (whiteList.indexOf(to.path) > -1) {
      next();
    } else {
      next("/login"); // 其余页面跳转到login页面
      NProgress.done();
    }
  }
});

router.afterEach(() => {
  NProgress.done(); // 保守的关掉页面跳转进度条
});

记住密码

这个动作实现比较简单,将输入信息通过cookie进行缓存即可。借助第三方库js-cookie实现。具体可以看src\views\login\index.vue文件里的内容。


写组件登陆逻辑

以上步骤都准备好,就可以在登陆页面src\views\login\index.vue上写逻辑了。


退出登陆方案

有两种动作:

  • 主动退出:用户点击登录按钮之后退出。
  • 被动退出:token过期或被其他人登陆”顶下来“时退出。

主动退出

在vuex目录src\store\modules\user.js文件写主动退出登陆的逻辑(前面代码示例有)。

被动退出

被动退出分为主动处理和被动处理

主动处理

前端通过计算token的时效,超过后自动退出登陆。

首先需要能处理token时效性判断的函数,写在目录src\utils\auth.js中,然后每次登陆的时候存储token获取的时间,在接口请求拦截器中判断是否超过token失效时间,超过就主动退出登陆。

被动处理

被动处理包含两个方面:

  • token失效,服务端会对token进行时效性记录,失效了就会在请求接口返回401。
  • 单点登陆,用户在其他设备登陆了,当前用户被挤下来(此项目暂不实现,原理也是后端接口返回一个特殊状态码进行判断)

token失效只需要在响应拦截器中对401做处理即可。


单点登录

这玩意就是当一个公司产品线多了时,不同产品之间想共享用户信息,搞出来个单点登录。

会有个用户中心服务,专门去统一管理这些用户信息。

然后单点登录分为两种实现方式

cookie+session模式

客户端想在子系统登录,客户端会向用户认证中心服务发送登录消息, 认证中心生成一个sid以set cookie的形式返回给客户端,并且自己往一张表里写入sid值: 用户信息xxxx记录用户信息,这个表就是session。

以后客户端请求都带上cookie,把里面的sid给子系统,子系统会拿着这个sid去和认证中心求证,后者在session中验证无误后回答子系统,子系统再返回对应的数据给客户端。

优点:

  • 可以通过删除sid让用户及时下线

缺点:

  • 成本高,当用户量大了,表的搭建成本和服务的防护也要提升

token模式

就是认证中心服务没有session表了,直接给客户端返回token,客户端和子系统请求的时候带上token,子系统自己拿token验证。

如果采用了双token模式,认证中心返回一个短期token和长期token,短token用于和子系统交互。当短期token失效了,再从新从认证中心用长token去重新更新token。如果想要让用户下线,对短token做不了文章,但只需要把长token失效就可以了,时间到客户端再访问认证中心直接过期。

双token前端实现参考:【场景方案】关于前端对接口行为的控制合集:轮番查询、并发请求、服务端通知、token无感刷新、请求取消

优点:

  • 认证中心压力小

缺点:

  • 让用户下线没那么及时
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值