Json Web Token在前后端实践思考分析

 更新时间:2022年11月18日 08:37:06   作者:那个曾经的少年回  
这篇文章主要为大家介绍了Json Web Token在前后端实践思考分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

1、前言

啥也不说了,直接进入正题,来学习一下Token在前端和后端的简单应用分析

Token是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码进行对比,判断用户名和密码是否正确,并作出相应提示,在这样的背景下,Token便应运而生。

Token实际上就是在第一个登录的时候通过用户名和密码,在服务端验证OK后生成的一串字符串,也可以说是验证通过后服务端为其签发一个令牌,随后前端在访问服务端接口时,客户端就可以携带这个TOken令牌访问服务器,服务端只需要验证令牌的有效性即可。

下面便是请求接口的一个大致过程

  • 先登录,获取Token
  • 调用业务接口,后端要先验证Token
  • 验证OK,才继续调用业务接口返回数据
  • 验证失败,则返回给前端,比如Token过期,则重新跳转到登录

2、后端

登录接口,通过用户名和密码,或者手机号验证码的方式通过验证

public async Task<dynamic> Login([FromServices] IAuthService authService, [FromBody] FormLoginRequest loginModel)
{
  return await authService.login(loginModel);
  // authoService.login中的逻辑
  // 判断是否匹配,匹配成功
  // 创建token并写入redis,并设置超期时间
  // 之前业务接口调用时,直接从redis中获取
  // 如果有超期,返回给前端一个标识
}

这里有一个创建Token的过程,我们来看一下token的组成

我找了一个公司正在开发项目中的token进行解析查看。主要结构如上图所示。解密以后最重要的信息便是uid,或者说是用户在后端中的唯一的用户id,那么通过uid便可以查询到相关的身份认证信息。

截图所示便是JSON Web Token的组成结构,从截图左侧仔细可以看到,中间有两个.将JWT分成了三个部分

  • HEADER

alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256)
typ属性表示这个令牌(token)的类型(type)

  • PAYLOAD 中间部分存放的就是实际要传输的数据
  • Signature部分是对前面的两部分的数据进行签名,防止数据篡改。
  • 最终生成便是

首先明确一点,是在后端生成的Token,后端会先定义一个秘钥,这个秘钥只有后端服务器才知道

不能泄露给用户,然后使用Header中指定的签名算法(默认情况是HMAC SHA256), 

算出签名以后将Header、Payload、Signature三部分拼成一个字符串,每个部分用`.`分割开来,
就可以返给用户了。

前端在登录认证通过获得Token并保存到前端以后,再调用业务接口的时候每次便会携带Token

后端服务会通过全局注册的环绕AOP,处理每次前端有请求到达后端的时候来对token校验

  AllowAnonymousAttribute allowAnonymousAttribute = descriptor.MethodInfo.GetCustomAttribute<AllowAnonymousAttribute>(false);
  // 判断可不验证token的接口
  if (allowAnonymousAttribute != null)
  {
      await next(); 
      return;
  }
  //获取请求头中的Authorization
  string token = context.HttpContext.Request.Headers["Authorization"];
  // 相当于对前端传递的token进行转换
  string tokenKey = "sso." + Utils.MD5(token);
  // redis获取,看看是否有效,直接取出返回
  string loginUserJson = await RedisHelper.GetAsync(tokenKey);
  if (!loginUserJson.IsNullOrWhiteSpace()) {
    RedisSSOVerifyResult resultInfo = JsonSerializer.Deserialize<RedisSSOVerifyResult>(loginUserJson);
    if(resultInfo.ExpiresAt > DateTime.now()) {
      loginUser = resultInfo.LoginUser;
    }
    else {
      RedisHelper.RemoveAsync(tokenKey); // 无效了 从redis中移除
      throw new ValidException("Token认证过期,请重新登录", -2);  // 这里用-2跟前端做好约定
    }
  } else {
    throw new ValidException("Token认证过期,请重新登录", -2);  // 这里用-2跟前端做好约定
  }

大致的一个token认证过程是这样的,实际项目中相对来说还是比较复杂的,这是我从公司项目中扣取出来的。还有很多代码没有列出来,要不然会显得比较臃肿,而且主要逻辑不容易查看。

3、前端

通过登录页面,输入登录名和密码,或者手机号和验证码,获取到token,现将token存储到localStorage中,再通过token获取其他业务接口的数据。 通常可能首先通过token获取个人信息或者一些权限数据(这里只是提一下)。

  const adminLogin = async () => {
      //   state.loading = true
      const res = await loginByMobile({
        mobile: state.loginForm.phone,
        captchaValue: state.loginForm.verificationCode,
      });
      state.loading = false;
      if (res?.code === 200) {
        localStorage.setItem(
          "token",
          JSON.stringify({
            ...res.data,
            account: state.loginForm.phone,
          })
        );
        store.dispatch("fetchMenu");
      }
    };

我这里登录完,直接通过token来获取当前登录用户的个人信息以及后台勾选的菜单权限,后端分别通过两个接口进行的数据返回。

    async fetchMenu({ commit }) {
      try {
        const information = await getMyInformation()
        if (information?.code === 200) {
          console.log(information, 'information')
          commit("setMyInformation" , information.data)
          const res = await getMyMenu()
          if(res?.code === 200) {
            commit("changeMenuList",res.data)
            window.location.href = "/"
          }
        }
      } catch (error) {
      }
    },

这里是axios针对每次的请求添加请求头的Authorization

instance.interceptors.request.use(
  (request) => {
    const token = localStorage.token
      ? JSON.parse(localStorage.token)
      : {};
    request.headers = {
      "Authorization": token.authorization || '',
      "Content-Type": "application/x-www-form-urlencoded",
      "Content-Type": "application/json",
    };
    return request;
  },
  (error) => Promise.reject(error)
);

这里是针对后端接口返回数据的判断处理,其中有一个-2的特殊判断,这里是跟后端返回一起约定的code

instance.interceptors.response.use(
  (response) => {
    // token
    if (response.data.code === -2) {   
      // token失效
      ElMessage({
        message: "身份认证无效,请重新登录",
        type: "warning",
      });
      // localStorage.clear();
      clear()
      window.location.href = "/";
      return false;
    }
    if (response.data.code !== 200) {
      return Promise.reject(new Error(response.data.message));
    }
    /// ..... 其他的逻辑判断
    return response.data;
  },
}

上面通过 code为-2 进行判断 ,然后清除掉缓存数据,那么在vue-router路由中会进行判断处理

router.beforeEach((to, _from, next) => {
  NProgress.start()
  if (to.path === '/login' || to.path === '/init-password' ||  to.path === '/login-cellphone') {
    next()
    return false;
  }
  if (!localStorage.getItem('token')) {
    next('/login')
    return false
  }
  if (to.name) {
    next()
    return false
  }
  if (childrenPath.some((item) => to.path.includes(item))) {
    next()
    console.log('child');
    return false
  }
  // 如果找不到路由跳转到404
  next("/404")
  return false
})

总结

前端和后端大致的一个过程就在这里简单说完了,梳理完了以后,发现自己更清楚了,其实还有很多的问题要去处理,比如

  • 请求业务接口Token超期失效了该怎么办? 可以通过每次调用业务接口的前,只要验证Token成功,就延迟Token的超期时间,但是这种方式每次都要去处理Token的时间,相对来说就比较麻烦,而且对服务器有一定的损耗。
  • 那还有更好的办法吗? 当然也是有的。比如通过双Token进行无痛刷新,就是当一个token失效或者超期后,通过另外一个refresh_token来重新获取token的处理,获取成功后,再重新调用期间异常的业务接口
  • 当然肯定还有其他的方式吧,暂时能想到的就这么多了。

以上就是Json Web Token在前后端实践思考分析的详细内容,更多关于Json Web Token前后端的资料请关注脚本之家其它相关文章!

相关文章

  • bootstrap手风琴制作方法详解

    bootstrap手风琴制作方法详解

    这篇文章主要为大家详细介绍了bootstrap手风琴的制作方法,制作声明式触发手风琴,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-01-01
  • echarts环形图内部圆、外部圆形及阴影设置方法

    echarts环形图内部圆、外部圆形及阴影设置方法

    近期要做图表,我选择了ECharts做可视化图表,图表的样式有阴影,这篇文章主要给大家介绍了关于echarts环形图内部圆、外部圆形及阴影设置的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2023-11-11
  • 表单提交(插入效果)javascript

    表单提交(插入效果)javascript

    表单提交(插入效果)javascript...
    2006-08-08
  • js判断鼠标左、中、右键哪个被点击的方法

    js判断鼠标左、中、右键哪个被点击的方法

    这篇文章主要介绍了js判断鼠标左、中、右键哪个被点击的方法,主要通过event.button事件来判断鼠标点击的类型,需要的朋友可以参考下
    2015-01-01
  • 相关JavaScript在览器中实现可视化的四种方式

    相关JavaScript在览器中实现可视化的四种方式

    这篇文章主要介绍了相关JavaScript在览器中实现可视化的四种方式,文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的小伙伴可以参考一下
    2022-09-09
  • 点击显示指定元素隐藏其他同辈元素的方法

    点击显示指定元素隐藏其他同辈元素的方法

    点击显示指定元素并隐藏其他同辈元素,下面有个不错的方法,需要的朋友可以参考下
    2014-02-02
  • 基于javascript实现图片预加载

    基于javascript实现图片预加载

    这篇文章主要介绍了javascript图片预加载的方法,实例分析了javascript实现图片预加载的思路,具有一定参考借鉴价值,需要的朋友可以参考下
    2016-01-01
  • 微信JSSDK上传图片

    微信JSSDK上传图片

    做过微信开发的都知道,在部分android机型里微信不支持网页上传图片的,这是由于这些机型的文件上传存在内存泄漏,会导致微信闪退,所以微信内置浏览器将文件上传屏蔽,本篇文章给大家介绍使用微信jssdk如何上传图片,需要的朋友可以关注下
    2015-08-08
  • 纯js实现瀑布流展现照片(自动适应窗口大小)

    纯js实现瀑布流展现照片(自动适应窗口大小)

    用瀑布流来展现照片再好不过了,我的思路大概是一张一张的图片插入,当这一行的图片保持长宽比例不变并且高度低于250时就完成一个了循环,即这一行插入进去了
    2013-04-04
  • 用Webpack构建Vue项目的实践

    用Webpack构建Vue项目的实践

    这篇文章主要介绍了用Webpack构建Vue项目的实践,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11

最新评论