手把手教你入门vue+springboot开发(十一)--token详细解读


前言

JWT是一种在web应用中广泛使用的令牌格式,用于在用户和服务器之间传递安全可靠的信息。JWT通常包含了用户的身份信息和一些其他的元数据,被用作身份验证和授权。因此,人们经常将JWT简称为令牌(token)。本篇我们结合前面实现的代码详细解读一个token的使用过程。


一、后端代码解读

我们先来看登录过程,以下为UserController.java的login函数:

@PostMapping("/login")
public Result<String> login(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password) {
    //根据用户名查询用户
    User loginUser = userService.findByUserName(username);
    //判断该用户是否存在
    if (loginUser == null) {
        return Result.error("用户名错误");
    }

    //判断密码是否正确  loginUser对象中的password是密文
    if (Md5Util.getMD5String(password).equals(loginUser.getPassword())) {
        //登录成功
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", loginUser.getId());
        claims.put("username", loginUser.getUsername());
        String token = JwtUtil.genToken(claims);
        //把token存储到redis中
        ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
        operations.set(token,token,1,TimeUnit.HOURS);

        return Result.success(token);
    }
    return Result.error("密码错误");
}

用户登录时,后端会根据用户名查询数据库users表,校验密码。密码校验正确表示登录成功,此时,根据id和username信息生成JWT令牌token,并保存到redis中,然后返回token信息给前端,后续前端来其它请求时都要带上这个token。值得注意的是,token信息保存到redis时,可以设置过期失效时间,上面代码设置的是一个小时。

再来看看拦截器处理,即每次前端请求处理之前需要做什么,以下代码为LoginInterceptor.java的preHandle函数:

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    //令牌验证
    String token = request.getHeader("Authorization");
    //验证token
    try {
        //从redis中获取相同的token
        ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
        String redisToken = operations.get(token);
        if (redisToken==null){
            //token已经失效了
            throw new RuntimeException();
        }
        Map<String, Object> claims = JwtUtil.parseToken(token);

        //把业务数据存储到ThreadLocal中
        ThreadLocalUtil.set(claims);
        //放行
        return true;
    } catch (Exception e) {
        //http响应状态码为401
        response.setStatus(401);
        //不放行
        return false;
    }
}

前端来的每次请求中,浏览器都需要在请求头header中携带token到后端,请求头的名称为 Authorization,值为登录时下发的JWT令牌token。后端会到redis中查询该token是否存在,如果没找到,表示token已经失效,则http响应状态码为401,表示未通过鉴权。如果找到,则解析token内容保存到线程局部变量中,供业务使用。

二、前端代码解读

再来看看前端的登录过程,以下代码为Login.vue的login函数:

//调用接口,完成登录
let result =  await userLoginService(registerData.value);
ElMessage.success(result.msg ? result.msg : '登录成功')
//把得到的token存储到pinia中
tokenStore.setToken(result.data)
//跳转到首页 路由完成跳转
router.push('/user')

前端登录成功后,会把获取的token保存起来,然后跳转到首页。
然后每次请求时,会在请求头中带上token,以下代码为前端拦截器代码:
request.js

import {useTokenStore} from '@/stores/token.js'
//添加请求拦截器
instance.interceptors.request.use(
    (config)=>{
        //请求前的回调
        //添加token
        const tokenStore = useTokenStore();
        //判断有没有token
        if(tokenStore.token){
            config.headers.Authorization = tokenStore.token
        }
        return config;
    },
    (err)=>{
        //请求错误的回调
        Promise.reject(err)
    }
)

import router from '@/router'
//添加响应拦截器
instance.interceptors.response.use(
    result => {
        //判断业务状态码
        if(result.data.code===0){
            return result.data;
        }

        //操作失败
        ElMessage.error(result.data.msg?result.data.msg:'服务异常')
        //异步操作的状态转换为失败
        return Promise.reject(result.data)
        
    },
    err => {
        //判断响应状态码,如果为401,则证明未登录,提示请登录,并跳转到登录页面
        if(err.response.status===401){
            ElMessage.error('请先登录')
            router.push('/')
        }else{
            ElMessage.error('服务异常')
        }
       
        return Promise.reject(err);//异步的状态转化成失败的状态
    }
)

在前端响应拦截器中,如果收到401,表示token过期,提示“请先登录”然后返回到登录界面。


总结

以上设计会带来一个问题,如果token过期失效时间设置过长,安全性得不到保障,只要别人截获了token就可以长时间反复使用该token来获取或者篡改信息。如果token过期失效时间设置过短,会经常需要重新登录来获取新的token,用户体验很差。为了解决这个问题,一个常用的做法就是无感刷新token。下一篇我们就来研究一下这个无感刷新token如何实现。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值