基于token进行登录,每次请求携带token

一,什么是token?

Token,也称为“令牌”,是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。比如如下形式:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJEenUyMDIwMDEwMTEwMzEiLCJleHAiOjE3MTQwNTM2MDV9.VBIWPBzGJRhwCB_jhI-wjZF8ErEFfpQkZOUmFxYQs5k

二、为什么使用Token

传统验证用户身份的方式,大多为基于服务器验证的方式,即cookie+session的方式,由于HTTP协议是无状态的,导致程序需要验证每一次请求,从而辨别客户端的身份。

用户登录成功将其信息存入session中,用户每次请求都会将携带session id的cookie一起发送器服务端,进行校验,随着Web、应用程序、以及移动端的崛起,这种验证方式弊端逐渐显现,尤其是在可扩展性方面。

引发的问题比如用户增多导致内存开销较大、CORS(跨域资源共享)以及CSRF(跨站请求伪造)等。

引入Token验证机制后,请求会发送token而不再是发送cookie能有效够防止CSRF,即使在客户端使用cookie存储token,但cookie也只有存储功能,而不再具备验证功能,因此安全性得到了极大的提高。

而且只要token设计的足够复杂,除非用户泄露,否则几乎没有被破解的可能,加上token是有时效的,在有限的时间加上有限的算力,更是无懈可击,这也类似于加密资产比如比特币钱包对应的私钥,安全性极高。

另外Token可以有效减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。

三、Token的使用

什么是jwt?

JWT(JSON WEB TOKEN):JSON网络令牌,JWT是一个轻便的安全跨平台传输格式,定义了一个紧凑的自包含的方式在不同实体之间安全传输信息(JSON格式)。它是在Web环境下两个实体之间传输数据的一项标准。实际上传输的就是一个字符串。广义上讲JWT是一个标准的名称;狭义上JWT指的就是用来传递的那个token字符串。

前端axios请求拦截器

service.interceptors.request.use(config => {
    if (localStorage.getItem('token') !== null && localStorage.getItem('token') !== "" && localStorage.getItem('token') !== undefined) {
        config.headers['Authorization'] = 'Bearer ' + localStorage.getItem('token'); // 假设你存储token在localStorage中
        config.headers['Content-Type'] = 'application/json;charset=UTF-8'
        config.headers['Accept'] = 'application/json'
        config.headers['Access-Control-Allow-Origin'] = '*'
    } else {
        //跳转登录
        router.push({path: '/login'})
    }
    return config
}, error => {
    return Promise.reject(error)
})

spring boot使用jwt

 <dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>4.3.0</version>
</dependency>

utils

package com.dzu.utils;

import cn.hutool.core.date.DateUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.dzu.domain.User;

import java.util.Date;
import java.util.List;

public class JwtTokenUtils {

    private static final String SECRET = "dzuojsystem"; // 你的密钥,应该保密
 

    public static String createToken(User user) {
        //将用户名放进去作为载荷进行加密
        return JWT.create().withAudience(user.getUsername())
                //传入当前时间对象,并指定两小时后token过期
                //也可以指定时分秒天,比如offsetMinute就表示指定有效期为30分钟
                .withExpiresAt(DateUtil.offsetMinute(new Date(), 30))
                //使用密钥作为token的密钥
                .sign(Algorithm.HMAC256(SECRET));
    }

    public String parseTokenUsername(String token) {
        try {
            //验证token
            return JWT.decode(token).getAudience().get(0);
        } catch (Exception e) {
            //有异常就是验证不通过了
            throw new RuntimeException("token验证失败");
        }
    }

    //判断token是否有效且合法

    public boolean validateToken(String token) {
        // 创建token验证器
        Algorithm algorithm = Algorithm.HMAC256(SECRET);
        JWTVerifier verifier = JWT.require(algorithm)
                .withIssuer("dzuojsystem")
                .build();

        // 验证token
        try {
            DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

配置拦截器 (可以自定义异常抛出)

//省略导包,自行导入

//这是拦截器的具体的方法
@Controller
@CrossOrigin
public class ProjectInterceptor implements HandlerInterceptor {
    private static final String SECRET = "dzuojsystem"; // 你的密钥,应该保密
    @Resource
    private UserMapper userMapper;

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //首先要确定访问的是一个方法
        if (!(handler instanceof HandlerMethod)) {
            //这里表示当访问的是一个html页面的时候就直接结束,当访问的是一个方法的时候才会继续向下运行
            //比如拦截"/**"请求,然后访问/aa.html此时就会直接结束,但是访问的是/aa这个方法的话,就会打印"开始拦截"
            return true;
        }

        //然后判断请求中是否有token
        //从请求头中获取token
        String token = request.getHeader("Authorization").substring(7);
        System.out.println("authorization----token:"+token);
        //主要用来判断字符串类型的变量是否为空
        if (StrUtil.isBlank(token) || token == null) {
            throw new Exception("无token,请重新登陆");
        }

        //然后解析token中的载荷,但这一步无法判断token是否过期
        String Username;
        try {
            //解析token中的主体中的数据,结果为[username],就是之前加密的数据
            List<String> ff = JWT.decode(token).getAudience();
            //解析token从中获取载荷中的第一个数据,如果这串代码运行异常则说明该token字符串有问题
            Username = ff.get(0);
        } catch (Exception e) {
            //此时说明token字符串已经被串改,导致解析失败
            throw new Exception("token验证失败,请重新登陆");
        }
        //通过用户名查询信息
        User user = userMapper.selectOne(new QueryWrapper<User>().eq("username", Username));
        if (user == null) {
            throw new Exception("用户不存在,该token不合法");
        }
        //验证token
        //通过添加密钥来创建验证对象,从而验证token是否过期,因为即使过期了,上面也能解析到数据
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
        try {
            jwtVerifier.verify(token);
        } catch (JWTVerificationException e) {
            throw new Exception("token验证失败,请重新登陆");
        }
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

@Controller
public class SpringMvcSupport implements WebMvcConfigurer {
    @Resource
    private ProjectInterceptor projectInterceptor;

    public void addInterceptors(InterceptorRegistry registry) {
        //这里是注册拦截器
        registry.addInterceptor(projectInterceptor)
                .addPathPatterns("/**")
                //放行接口,自己决定放行那些接口
                .excludePathPatterns("/user/register")
                .excludePathPatterns("/user/login")
                .excludePathPatterns("/email/sendMail")
                .excludePathPatterns("/jwt/validateToken");
    }
}

记得前端登录的时候,要把生成的token存起来,例如localstorage里面。

大致的流程

后端生成token--->前端保存token------>通过前端的拦截器给请求头添加token----->后端通过拦截器验证token。

大致就是这样,可能会有错误,请包涵!

相关推荐

  1. 基于token进行登录请求携带token

    2024-04-26 01:22:04       33 阅读
  2. flask 实现token生成以及携带token请求接口

    2024-04-26 01:22:04       48 阅读
  3. Vue3 实现基于token 用户登录

    2024-04-26 01:22:04       40 阅读
  4. 基于axios给请求添加token

    2024-04-26 01:22:04       34 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-26 01:22:04       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-26 01:22:04       100 阅读
  3. 在Django里面运行非项目文件

    2024-04-26 01:22:04       82 阅读
  4. Python语言-面向对象

    2024-04-26 01:22:04       91 阅读

热门阅读

  1. oracle_申明与赋值

    2024-04-26 01:22:04       34 阅读
  2. 成为程序员后你都明白了什么?

    2024-04-26 01:22:04       35 阅读
  3. 【课设资源分享】基于jsp的俱乐部会员系统

    2024-04-26 01:22:04       35 阅读
  4. Ubuntu22.04.4 - SSH - 笔记

    2024-04-26 01:22:04       26 阅读
  5. Beego框架相关内容

    2024-04-26 01:22:04       32 阅读
  6. 2024年GPLT团体程序设计竞赛题解(无L3-3)

    2024-04-26 01:22:04       28 阅读
  7. Day6: 5道C++ 面向对象高频题整理

    2024-04-26 01:22:04       33 阅读
  8. optim.lr_scheduler.StepLR学习

    2024-04-26 01:22:04       34 阅读
  9. 洛谷 P5960 [模板] 差分约束 题解 SPFA

    2024-04-26 01:22:04       35 阅读
  10. Thread 类的基本用法

    2024-04-26 01:22:04       25 阅读
  11. 流程图画图规范

    2024-04-26 01:22:04       29 阅读