【SpringBoot】SpringBoot整合JWT

news2024/11/18 3:54:05

目录

  • 先说token
  • 单点登录(SSO)
    • 简介
    • 原理
    • 单点登录的优势
    • 单点登录流程
    • 分布式单点登录方式
      • 方式一:session广播机制实现
      • 方式二:使用cookie+redis实现。
      • 方式三:token认证
  • JWT
    • 数字签名
    • JWT的作用
    • JWT和传统Session
      • 1、无状态:
      • 2、避免CSRF 攻击:
      • 3、适合移动端应用
      • 4、单点登录友好
      • 总结
    • JWT的核心应用
      • Authorization (授权)
      • Information Exchange (信息交换)
    • JWT的组成部分
      • header:头部信息
      • payload:有效载荷
      • signatur:签名算法
    • JWT的工作流程
      • 思路
      • 步骤
    • JWT的demo【单独案例】
      • 1.依赖
      • 2.生成Token
      • 3.输出结果
      • 解密
        • 在线token解密
        • 代码实现
        • 输出结果
      • 常见的异常整理
    • 常见异常信息
  • SpringBoot整合JWT
    • 1.依赖
    • 2.配置
    • 3.封装工具类
    • 4.其他类
    • 5.拦截器
    • 6.拦截器配置
    • 7.controller类
    • 8.测试

先说token

  • 随着前后端分离的普及以及分布式、微服务、Restful API的普遍应用,Token认证已经是所有系统都绕不开的一个技术话题。
  • 基于token的用户认证是一种服务端无状态的认证方式。所谓服务端无状态指的token本身包含登录用户所有的相关数据,而客户端在认证后的每次请求都会携带token,因此服务器端无需存放token数据。
  • 当用户认证后,服务端生成一个token发给客户端,客户端可以放到 cookie 或 localStorage 等本地存储中,在客户端每次发起请求时带上 token,服务端收到token通过验证后即可确认用户身份
  • 简单来说token就像是一个令牌,好比我们办的某个超市、健身房、酒店等机构的会员卡。而且如果某个机构是连锁性质的,那么这一个会员卡是可以在所有连锁单位都能认证的,这种就是当下流行的一个词:单点登录

单点登录(SSO)

简介

  • 单点登录(Single Sign-On,简称SSO)是一种身份验证和访问控制的技术,它允许用户使用一组凭据(如用户名和密码)登录到多个相关但相对独立的系统中,而不需要再次输入凭据。
  • 通过SSO,用户只需登录一次,就可以访问多个应用或系统,简化了用户的登录流程和管理,提高了用户体验。

原理

  • 在一个典型的SSO系统中,有一个中心身份提供者(Identity Provider,简称IdP),其负责处理用户的身份验证和生成令牌。
  • 当用户尝试访问其他应用或系统时,这些应用或系统会将用户重定向到IdP进行身份验证。
  • 一旦用户成功登录,IdP会生成一个令牌,并返回给应用或系统。
  • 应用或系统可以使用此令牌来验证用户的身份,并为其提供相应的访问权限。

单点登录的优势

  • SSO的好处包括提高用户体验、减少用户的密码负担、简化身份验证和访问管理、提高安全性等。
  • 通过使用SSO,用户可以通过一个登录凭据访问多个应用或系统,无需记住多个用户名和密码。
  • 同时,SSO还可以加强安全性,通过集中管理和控制用户的访问权限,减少安全漏洞的发生。

单点登录流程

在这里插入图片描述

分布式单点登录方式

方式一:session广播机制实现

  • 简单来说:就是把session复制到另一台服务器中
  • 缺点:
    • 模块较多时,拷贝session比较浪费资源;
    • 比如 中间会存在多份一样的数据 ;
    • session默认过期时间30分钟,过期需要重新登录

方式二:使用cookie+redis实现。

  • cookie客户端技术:存在浏览器中,每次发送请求,带着cookie值进行发送
  • redis,读取速度快,基于key-value存储(keys *)
  • 用户登录后,把数据分别放到两个地方cookie、redis
    • redis:在key里生成唯一随机值(ip、用户id、uuid) ,在value里放用户数据
    • cookie:把redis里面生成key值放到cookie里面。
  • 访问项目其他模块时,发送请求带着cookie进行发送,然后其他模块去获取cookie值,也就是拿着cookie去redis中查询,如果能查到数据表示这个用户已登录。

方式三:token认证

  • 按照一定规则生成字符串,字符串可以包含用户信息——jwt

JWT

  • JSON Web Token,通过数字签名的方式,以JSON对象为载体,在不同的服务终端之间安全的传输信息

数字签名

  • 数字签名(又称公钥数字签名)是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。
  • 它是一种类似写在纸上的普通的物理签名,但是在使用了公钥加密领域的技术来实现的,用于鉴别数字信息的方法。
  • 一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。
  • 数字签名是非对称密钥加密技术数字摘要技术的应用

JWT的作用

  • jwt最常见的场景就是授权认证,一旦用户登录,后续每个请求都将包含jwt
  • 系统在每次处理用户请求之前,都要先进行jwt安全校验,痛过之后才能进行处理

JWT和传统Session

1、无状态:

  • token 自身包含了身份验证所需要的所有信息,使得我们的服务器不需要存储 Session 信息,这显然增加了系统的可用性和伸缩性,大大减轻了服务端的压力。
  • 也导致了它最大的缺点:当后端在token 有效期内废弃一个 token 或者更改它的权限的话,不会立即生效,一般需要等到有效期过后才可以。另外,当用户 Logout 的话,token 也还有效。除非,我们在后端增加额外的处理逻辑。

2、避免CSRF 攻击:

  • 攻击者就可以通过让用户误点攻击链接,达到攻击效果。
  • 防止误触操作,避免请求直接获取本地的session值进行请求访问。

3、适合移动端应用

  • 使用 Session 进行身份认证的话,需要保存一份信息在服务器端,而且这种方式会依赖到 Cookie(需要 Cookie 保存 SessionId),所以不适合移动端。
  • 但是,使用 token 进行身份认证就不会存在这种问题,因为只要 token 可以被客户端存储就能够使用,而且 token 还可以跨语言使用。

4、单点登录友好

  • 使用 Session 进行身份认证的话,实现单点登录,需要我们把用户的 Session 信息保存在一台电脑上,并且还会遇到常见的 Cookie 跨域的问题。
  • 但是,使用 token 进行认证的话, token 被保存在客户端,不会存在这些问题。

总结

JWT传统Session
存储位置客户端服务器
存储数据TokenSession ID + 服务器端存储的会话数据
存储方式无状态有状态
跨域支持支持需要额外配置
可扩展性
安全性
网络开销
扩展性
动态更改权限需要重新签发Token服务器端配置即可
服务器状态管理无需管理需要管理和维护

JWT的核心应用

Authorization (授权)

  • 这是使用JWT的最常见场景。
  • 一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。
  • 单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用。

Information Exchange (信息交换)

  • 对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。
  • 因为JWTs可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。
  • 另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。

JWT的组成部分

JWT的结构由三部分组成,分别是标头、有效负载、签名算法,中间使用 点 进行隔开。

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMmY0MzMyYy01MmRhLTQ0MDktODJjZS1hODBkZjNmMDIwYjMiLCJzdWIiOiJhbGwiLCJpYXQiOjE3MTcxMTc3MTMsImV4cCI6MTcxNzExOTUxMywidXNlcm5hbWUiOiJ6aGFuZ3NhbiIsInVzZXJJZCI6IjEwMDEifQ.wfrMHoLZubZksALfad5BAG7oNUXbMwrXxHhgRTAtOtI

header:头部信息

  • 通常由两部分组成:令牌的类型 和 所用的加密算法
  • 然后将该JSON对象数据进行Base64 URL编码,得到的字符串就是JWT令牌的第一部分。
  • 例如:{ “typ”: “JWT”, “alg”: “HS256” }。然后要转成base64字符串。

payload:有效载荷

  • 有效数据存储区,主要定义自定义字段和内置字段数据。
  • 通常会把用户信息和令牌过期时间放在这里,同样也是一个JSON对象,里面的key和value可随意设置,然后经过Base64 URL编码后得到JWT令牌的第二部分
  • 由于这个部分是没有加密的(因为Base64是编码,可以直接解码),建议只存放一些非敏感信息
Payload的内置字段说明
iss(Issuer)令牌的签发者
sub(Subject)所面向的用户或实体
aud(Audience)令牌的接收者
exp(Expiration Time)令牌的过期时间(以UNIX时间戳表示)
nbf(Not Before)令牌的生效时间(以UNIX时间戳表示)
iat(Issued At)令牌的签发时间(以UNIX时间戳表示)
jti(JWT ID)令牌的唯一标识符

原文链接:https://blog.csdn.net/qq_46921028/article/details/131298671

signatur:签名算法

  • 使用头部Header定义的加密算法,对前两部分Base64编码拼接的结果进行加密
  • 加密时的秘钥服务私密保存
  • 加密后的结果在通过Base64Url编码得到JWT令牌的第三部分。
  • 签名的作用:防止JWT令牌被篡改。
var encodestr = base64urlEncode(header) + "." + base64urlEncode(paylod);
var signature = HMACSHA256(encodestr,"secret");

JWT的工作流程

思路

  • 在身份验证中,当用户成功登录系统时,授权服务器将会把 JSON Web Token(JWT)返回给客户端,用户需要将此凭证信息存储在本地(cookie或浏览器缓存)。
  • 当用户发起新的请求时,需要在请求头中附带此凭证信息,当服务器接收到用户请求时,会先检查请求头中有无凭证,是否过期,是否有效。
    • 如果凭证有效,将放行请求;
    • 若凭证非法或者过期,服务器将回跳到认证中心,重新对用户身份进行验证,直至用户身份验证成功。

步骤

  1. 发送登录请求,携带username、password
  2. 进行验证,验证通过返回JWT
  3. 请求头携带JWT发请求到应用服务
  4. 验证携带过来的JWT的合法性
  5. 验证通过返回,执行后续操作

在这里插入图片描述

JWT的demo【单独案例】

1.依赖

	<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    ...
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
    </dependencies>
  • 如果jdk大于1.8,还需要引入以下依赖

            <dependency>
                <groupId>javax.xml.bind</groupId>
                <artifactId>jaxb-api</artifactId>
                <version>2.3.0</version>
            </dependency>
            <dependency>
                <groupId>com.sun.xml.bind</groupId>
                <artifactId>jaxb-impl</artifactId>
                <version>2.3.0</version>
            </dependency>
            <dependency>
                <groupId>org.glassfish.jaxb</groupId>
                <artifactId>jaxb-core</artifactId>
                <version>2.3.0</version>
            </dependency>
            <dependency>
                <groupId>javax.activation</groupId>
                <artifactId>activation</artifactId>
                <version>1.1.1</version>
            </dependency>
    

2.生成Token

@SpringBootTest
class JwtApplicationTests {
    /** AES 算法 */
    private static final String ALGORITHM_AES="AES";
    @Test
    public void testCreatJwt() throws NoSuchAlgorithmException {
        //定义秘钥,可以自己定义,随便一个字符串都可以,专业一些的话就用密钥生成工具吧
        String secretKey = getKey();
        System.out.println("生成的密钥是:" + secretKey);
        // 使用Jwts工具类构建一个令牌
        String token = Jwts.builder()
                // 1.设置JWT头部信息(类型和加密算法)
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")
                // 2.设置JWT载荷数据
                .setId(UUID.randomUUID().toString()) //内置字段jti:表示唯一ID
                .setSubject("all") //内置字段sub:面向所有用户
                .setIssuedAt(new Date()) //内置字段ita:token创建时间
                .setExpiration(new Date(System.currentTimeMillis() + 30 * 60 * 1000)) //内置字段exp:token过期时间,30分钟
                .claim("username", "zhangsan") //自定义字段,kv格式
                .claim("userId", "1001") //自定义字段
                // 3.设置JWT签名信息(加密算法,秘钥)
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact(); //最后调用compact()方法生成最终的token

        //由于使用UUID生成唯一标识,所以每次生成的token都不一样
        System.out.println("token = " + token);
    }

    /**
     * 生成密钥
     * @return
     * @throws NoSuchAlgorithmException
     */
    private String getKey() throws NoSuchAlgorithmException {
        /**
         * 创建KeyGenerator实例
         *      algorithm密钥算法
         *          AES
         *          DES
         *          DESede
         *          HmacSHA1
         *          HmacSHA224
         *          HmacSHA256
         *          HmacSHA384
         *          HmacSHA512
         *          RC2
         */
        KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM_AES);
        //指定生成密钥的大小;AES密钥长度只能=128、192、256
        keyGenerator.init(256);
        //指定生成密钥随机源:keyGenerator.init(SecureRandom secureRandom)
        //指定生成密钥大小、随机源:keyGenerator.init(int size, SecureRandom secureRandom)

        /**
         * 借助Base64转换生成的密钥
         *      通常加密后要把密钥保存下来,解密时使用密钥重建SecertKey,生成的密钥是字节数组不利于保存,所以借助Base64转换成字符串
         */
        return Base64.getEncoder().encodeToString(keyGenerator.generateKey().getEncoded());
    }
}

3.输出结果

生成的密钥是:pI9g7kkh0IgqHC27U7FYgAQtquy9PGPINCUvko2Qyyo=
token = eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMmY0MzMyYy01MmRhLTQ0MDktODJjZS1hODBkZjNmMDIwYjMiLCJzdWIiOiJhbGwiLCJpYXQiOjE3MTcxMTc3MTMsImV4cCI6MTcxNzExOTUxMywidXNlcm5hbWUiOiJ6aGFuZ3NhbiIsInVzZXJJZCI6IjEwMDEifQ.wfrMHoLZubZksALfad5BAG7oNUXbMwrXxHhgRTAtOtI

解密

在线token解密

地址:https://tooltt.com/jwt-decode/
在这里插入图片描述

代码实现

刚才的token过期了,重新生成了一下

    @Test
    public void testcheckToken() {
        // 秘钥,刚才生成的密钥
        String secretKey = "Y28Ijg521FgN31ZgpD1hZpOYd8fTMrZwNcMgds+D91I=";
        // 待验证的token
        String tokenStr = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4M2M3NTVkNC1jNzJlLTRlZjctYjY1MC1jYjdlZWRkYWNjYWIiLCJzdWIiOiJhbGwiLCJpYXQiOjE3MTcxMjQxNjQsImV4cCI6MTcxNzEyNTk2NCwidXNlcm5hbWUiOiJ6aGFuZ3NhbiIsInVzZXJJZCI6IjEwMDEifQ.gM89JWUOAQu8KpYgXbom9KGXB1ZcqSUqzj5eW8cg_HU";
        // 通过密钥验证签名是否被篡改
        JwtParser jwtParser = Jwts.parser();
        Jws<Claims> claimsJws = jwtParser
                .setSigningKey(secretKey)
                .parseClaimsJws(tokenStr);

        // 获取头
        JwsHeader header = claimsJws.getHeader();
        // 获取载荷
        Claims body = claimsJws.getBody();
        // 获取签名
        String signature = claimsJws.getSignature();
        System.out.println("头信息:" + header);
        System.out.println("载荷信息:" + body);
        System.out.println("签名信息:" + signature);
    }
输出结果
头信息:{typ=JWT, alg=HS256}
载荷信息:{jti=83c755d4-c72e-4ef7-b650-cb7eeddaccab, sub=all, iat=1717124164, exp=1717125964, username=zhangsan, userId=1001}
签名信息:gM89JWUOAQu8KpYgXbom9KGXB1ZcqSUqzj5eW8cg_HU

常见的异常整理

常见异常信息

异常原因
SignatureVerificationException签名不一致异常
TokenExpiredException令牌过期异常
AlgorithmMismatchException算法不匹配异常
InvalidClaimException:失效的payload异常

SpringBoot整合JWT

1.依赖

用这个吧,反正后面要写OAuth2

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

2.配置

# JWT 配置
jwt:
  secret: Y28Ijg521FgN31ZgpD1hZpOYd8fTMrZwNcMgds+D91I= # 加密密钥
  expire: 1800 # token有效时长 S
server:
  port: 9999
spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
	# ...... 其他配置

3.封装工具类

package com.kgc.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Calendar;
import java.util.Map;

/**
 * @author: zjl
 * @datetime: 2024/5/31
 * @desc: 复兴Java,我辈义不容辞
 */
@Component
public class JWTUtils {
    @Value("${jwt.secret}")
    public  String secret;

    @Value("${jwt.expire}")
    public  Integer tokenExpire;

    /**
     * 生成 JWT 令牌
     * @param map 传入的 Payload 数据
     * @return 返回生成的令牌
     */
    public String getToken(Map<String,String> map){
        JWTCreator.Builder builder = JWT.create();

        // 遍历传入的 Payload 数据,并添加到 Builder 中
        map.forEach((k,v)->{
            builder.withClaim(k,v);
        });

        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND,tokenExpire);

        // 设置过期时间为 XX 秒后
        builder.withExpiresAt(instance.getTime());

        // 使用 HMAC256 签名算法进行签名,并返回令牌字符串
        return builder.sign(Algorithm.HMAC256(secret)).toString();
    }

    /**
     * 获取令牌中的 Payload 数据
     * @param token 要解析的令牌字符串
     * @return 解码后的令牌对象(DecodedJWT)
     */
    public DecodedJWT verify(String token){
        // 创建一个 JWTVerifier 实例,使用相同的密钥构建,并对令牌进行验证和解码
        return JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
    }
}

4.其他类

  • 实体类

    @Data
    @NoArgsConstructor
    @ToString
    @AllArgsConstructor
    public class User {
        private int id;
        private String userCode;
        private String userName;
        private String userPassword;
        private String phone;
    }
    
  • mapper

    public interface UserMapper {
        @Select("SELECT * FROM SMBMS_USER WHERE USERCODE=#{userCode}")
        User selectUserByUserCode(String userCode);
    }
    
  • service

    @Service
    @Slf4j
    public class UserService {
        @Resource
        private UserMapper userMapper;
        public User login(String userCode,String userPassword){
            User user = userMapper.selectUserByUserCode(userCode);
            if(user!=null && userPassword.equals(user.getUserPassword())){
                return user;
            }
            return null;
        }
    }
    
  • 统一返回模板

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Result<T> {
    
        private int code;
        private String message;
        private T data;
    
        public Result(T data) {
            this.code = 200;
            this.message = "success";
            this.data = data;
        }
    
        public Result(T data, boolean success, String message) {
            if (success) {
                this.code = 200;
                this.message = "success";
            } else {
                this.code = 500; // 自定义错误状态码(示例为500)
                this.message = message;
            }
            this.data = data;
        }
    
        public Result(int code, String message) {
            this.code = code;
            this.message = message;
            this.data = null;
        }
    
        /**
         * 返回执行失败的结果(默认状态码为500)
         *
         * @param message 提示信息
         * @return 失败的结果对象
         */
        public static <T> Result<T> fail(String message) {
            return new Result<>(500, message);
        }
    
        /**
         * 返回执行失败的结果(自定义状态码和提示信息)
         *
         * @param code    状态码
         * @param message 提示信息
         * @return 失败的结果对象
         */
        public static <T> Result<T> fail(int code, String message) {
            return new Result<>(code, message);
        }
    }
    

5.拦截器

package com.kgc.interceptor;

/**
 * @author: zjl
 * @datetime: 2024/5/31
 * @desc: 复兴Java,我辈义不容辞
 */

import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kgc.utils.JWTUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * JWTInterceptor是一个拦截器,用于验证请求头中的JWT令牌是否有效。
 * 当有请求进入时,该拦截器会首先从请求头中获取令牌,并尝试验证其有效性。
 * 如果令牌验证成功,则放行请求;否则,拦截请求并返回相应的错误信息。
 */
@Component
public class JWTInterceptor implements HandlerInterceptor {
    @Resource
    private JWTUtils jwtUtils;
    @Resource
    private ObjectMapper objectMapper;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 创建一个Map对象,用于存储响应信息
        Map<String, Object> map = new HashMap<>();

        // 从请求头中获取令牌
        String token = request.getHeader("token");

        try {
            jwtUtils.verify(token); // 验证令牌的有效性
            return true; // 放行请求
        } catch (SignatureVerificationException e) {
            e.printStackTrace();
            map.put("msg", "无效签名!");
        } catch (TokenExpiredException e) {
            e.printStackTrace();
            map.put("msg", "token过期!");
        } catch (AlgorithmMismatchException e) {
            e.printStackTrace();
            map.put("msg", "token算法不一致!");
        } catch (Exception e) {
            e.printStackTrace();
            map.put("msg", "token无效!!");
        }

        map.put("state", false); // 设置状态为false

        // 将Map转化为JSON字符串(使用Jackson库)
        String json = objectMapper.writeValueAsString(map);

        response.setContentType("application/json;charset=UTF-8"); // 设置响应的Content-Type
        response.getWriter().println(json); // 将JSON字符串写入响应中

        return false; // 不放行请求
    }
}

6.拦截器配置

/**
 * InterceptorConfig 是一个配置类,用于添加拦截器。
 * 在这个类中,我们可以配置需要拦截的接口路径以及排除不需要拦截的接口路径。
 * 在这个例子中,我们添加了JWTInterceptor拦截器来对请求进行token验证,
 * 并设置了"/user/test"接口需要进行验证,而"/user/login"接口则被排除在验证之外,即所有用户都放行登录接口。
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Resource
    private JWTInterceptor jwtInterceptor;
    /**
     * 添加拦截器配置
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor)
                .addPathPatterns("/user/test")         // 对"/user/test"接口进行token验证
                .excludePathPatterns("/user/login");  // 所有用户都放行登录接口
    }
}

7.controller类

package com.kgc.controller;

import com.auth0.jwt.exceptions.*;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.kgc.pojo.User;
import com.kgc.service.UserService;
import com.kgc.utils.JWTUtils;
import com.kgc.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * @author: zjl
 * @datetime: 2024/5/31
 * @desc: 复兴Java,我辈义不容辞
 */
@RestController
@Slf4j
@RequestMapping("/user")
public class UserController {
    @Resource
    private UserService userService;
    @Resource
    private JWTUtils jwtUtils;
    @RequestMapping("/login")
    public Result<Map<String, Object>> login(User user) {
        // 打印用户名和密码
        log.info("用户名: [{}]", user.getUserCode());
        log.info("密码: [{}]", user.getUserPassword());
        // 创建结果对象
        Result<Map<String, Object>> result;
        try {
            // 调用userService的login方法进行用户认证
            User loginUser = userService.login(user.getUserCode(),user.getUserPassword());
            if(loginUser == null){
                return new Result<>(0, "认证失败");
            }
            // 获取用户ID和用户名,并将其放入payload
            Map<String, String> payload = new HashMap<>();
            payload.put("id", String.valueOf(loginUser.getId()));
            payload.put("name", loginUser.getUserName());
            // 生成JWT的令牌
            String token = jwtUtils.getToken(payload);
            // 构造成功的结果对象
            result = new Result<>(200, "认证成功");
            result.setData(new HashMap<>());
            result.getData().put("token", token); // 响应token
        } catch (Exception e) {
            // 构造失败的结果对象
            result = Result.fail(500, e.getMessage());
        }
        return result;
    }

    @RequestMapping("/test")
    public Result<Map<String, Object>> test(HttpServletRequest request) {
        // 创建结果对象
        Result<Map<String, Object>> result;
        try {
            Map<String, Object> map = new HashMap<>();
            // 处理自己的业务逻辑

            // 从请求头中获取token
            String token = request.getHeader("token");
            if(StringUtils.isEmpty(token)){
                return new Result<>(0, "请先登录!");
            }
            // 校验并解析token
            DecodedJWT verify = jwtUtils.verify(token);
            // 打印解析出的用户id和用户名
            log.info("用户id: [{}]", verify.getClaim("id").asString());
            log.info("用户name: [{}]", verify.getClaim("name").asString());
            // 构造成功的结果对象
            result = new Result<>(200, "请求成功!");
            result.setData(map);
        } catch (Exception e) {
            // 构造失败的结果对象
            result = Result.fail(500, e.getMessage());
        }
        return result;
    }

    @RequestMapping("/other")
    public Map<String, Object> test(String token) {
        Map<String, Object> map = new HashMap<>();
        try {
            jwtUtils.verify(token);
            map.put("msg", "验证通过~~~");
            map.put("state", true);
        } catch (TokenExpiredException e) {
            map.put("state", false);
            map.put("msg", "Token已经过期!!!");
        } catch (SignatureVerificationException e){
            map.put("state", false);
            map.put("msg", "签名错误!!!");
        } catch (AlgorithmMismatchException e){
            map.put("state", false);
            map.put("msg", "加密算法不匹配!!!");
        } catch (Exception e) {
            e.printStackTrace();
            map.put("state", false);
            map.put("msg", "无效token~~");
        }
        return map;
    }
}

8.测试

  • 先登录,用户名密码正确、不正确的
  • 正确的生成token
  • 然后访问其他接口,带token和不带token的。【header里带token】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1721031.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

精雕细琢,B 端 UI 设计展典雅风范

精雕细琢&#xff0c;B 端 UI 设计展典雅风范

virtualbox中ubuntu22.04网络配置

第一&#xff1a;添加两个网卡&#xff0c;网卡1是NAT方式&#xff0c;网卡2是仅主机模式&#xff08;两个顺序不能颠倒&#xff09; 第二步&#xff1a;启动ifconfig查看网络

LabVIEW车体静强度试验台测控系统

LabVIEW车体静强度试验台测控系统 开发了一种基于LabVIEW的车体静强度试验台测控系统&#xff0c;通过自动化技术提高试验的精度和效率。系统采用LabVIEW软件与S7-200 SMART PLC硬件平台相结合&#xff0c;实现了对液压缸作用力的精确控制和试验数据的实时采集及管理。 传统的…

11.1 排序算法

目录 11.1 排序算法 11.1.1 评价维度 11.1.2 理想排序算法 11.1 排序算法 排序算法&#xff08;sorting algorithm&#xff09;用于对一组数据按照特定顺序进行排列。排序算法有着广泛的应用&#xff0c;因为有序数据通常能够被更高效地查找、分析和处理。 如图 1…

MySQL事务与MVCC

文章目录 事务和事务的隔离级别1.为什么需要事务2.事务特性1_原子性&#xff08;atomicity&#xff09;2_一致性&#xff08;consistency&#xff09;3_持久性&#xff08;durability&#xff09;4_隔离性&#xff08;isolation&#xff09; 3.事务并发引发的问题1_脏读2_不可重…

香橙派 AI pro:AI 加速初体验

香橙派 AI pro&#xff1a;AI 加速初体验 在AI领域&#xff0c;不断涌现的硬件产品为开发者提供了前所未有的便利和可能性。今天&#xff0c;我要介绍的这款产品——香橙派 AIpro&#xff0c;就是其中的佼佼者。在昇腾 AI 芯片的加持下&#xff0c;这款开发板有着出色的算力。…

【WEEK14】 【DAY3】Swagger第一部分【中文版】

2024.5.29 Wednesday 目录 16.Swagger16.1.Swagger简介16.1.1.前后端分离16.1.2.前后端分离时代16.1.3.产生的问题16.1.4.解决方案16.1.5.Swagger 16.2.SpringBoot集成Swagger16.2.1.新建swagger-demo项目16.2.2.导入依赖16.2.2.1.springfox-swagger216.2.2.2.springfox-swagge…

CTFHUB-信息泄露-备份文件下载

目录 网站源码 bak文件 vim缓存 .DS_Store 当开发人员在线上环境中对源代码进行了备份操作&#xff0c;并且将备份文件放在了 web 目录下&#xff0c;就会引起网站源码泄露。 需要用到的工具&#xff1a; dirsearch (目录扫描工具在python3环境下) 网站源码 可以利用bur…

串口屏变量图标显示电量

1、首先制作好电量的图标图片&#xff0c;如下图&#xff1a; 2、然后可以使用美图秀秀逐个修改图片的像素为一致&#xff0c;比如像素为55*32&#xff0c;修改后如下 3、然后打开DGUS_V7.647软件&#xff0c;点击ICL生成工具 4、导入图片&#xff0c;点击生成ICL&#xff0c;如…

2024年中国CRM行业发展方向和前景 | 《连接型CRM》文章精选

01、创新突破&#xff0c;技术为本 中国经济发展处于增速换挡期&#xff0c;企业数字化需求旺盛&#xff0c;同时云计算、大数据、物联网、区块链、5G等新技术的发展&#xff0c;为CRM系统的应用与发展提供了更多的机遇和可能。 近些年来&#xff0c;技术的发展对CRM的重要性…

LabVIEW远程开发与调试

在现代项目开发中&#xff0c;远程开发与调试已经成为一种常见的模式&#xff0c;特别是在使用LabVIEW进行工程项目时。本文将详细分析LabVIEW远程开发与调试的优缺点&#xff0c;并从多个角度说明如何建议客户采用这种方式&#xff0c;以提高项目效率和质量。 优点 灵活性和便…

【UnityShader入门精要学习笔记】第十六章 Unity中的渲染优化技术 (上)

本系列为作者学习UnityShader入门精要而作的笔记&#xff0c;内容将包括&#xff1a; 书本中句子照抄 个人批注项目源码一堆新手会犯的错误潜在的太监断更&#xff0c;有始无终 我的GitHub仓库 总之适用于同样开始学习Shader的同学们进行有取舍的参考。 文章目录 移动平台上…

Webrtc支持HEVC之FFMPEG支持HEVC编解码(一)

一、前言 Webrtc使用的FFMPEG(webrtc\src\third_party\ffmpeg)和官方的不太一样,使用GN编译,各个平台使用了不一样的配置文件 以Windows为例,Chrome浏览器也类似 二、修改配置文件 windows:chromium\config\Chrome\win\x64 其他平台: chromium\config\Chrome\YOUR_SYS…

“仿RabbitMQ实现消息队列”---整体架构与模块说明

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、概念性框架理解 我们主要实现的内容&#xff1a; 1.Broker服务器&#xff1a;消息队列服务器&#xff08;服务端&…

新宏观范式和产业趋势下,纷享销客如何助力企业出海?

出海&#xff0c;已不再是企业的“备胎”&#xff0c;而是必须面对的“大考”&#xff01;在这个全球化的大潮中&#xff0c;有的企业乘风破浪&#xff0c;勇攀高峰&#xff0c;也有的企业在异国他乡遭遇了“水土不服”。 面对“要么出海&#xff0c;要么出局”的抉择&#xff…

阿里云 通过EIP实现VPC下的SNAT以及DNAT

192.168.0.85 有公网地址192.1680.95无公网地址 在192.168.0.85&#xff08;有公网地址服务器上操作&#xff09; #开启端口转发 echo "net.ipv4.ip_forward 1" >> /etc/sysctl.conf sysctl -p#仅允许192.168.0.95 iptables -t nat -I POSTROUTING -s 192.16…

【为什么 Google Chrome 打开网页有时极慢?尤其是国内网站,如知网等】

要通过知网搜一点资料&#xff0c;发现怎么都打不开。而且B站&#xff0c;知乎这些速度也变慢了&#xff01;已经检查过确定不是网络的问题。 清空了记录&#xff0c;清空了已接受Cookie&#xff0c;清空了缓存内容……没用&#xff01;&#xff01;&#xff01; 不断搜索&am…

eDP V1.4协议介绍

一、说明 eDP的全称是Embedded DisplayPort嵌入式显示端口,主要应用与短距离系统内应用,例如手机、一体式台式机等。eDP V1.4b是基于DP V1.3标准制作完成,但因应用场景的不同,还是有很多区别。 电压摆幅不同,eDP相对较低; eDP功耗相对较低; DP有线材和连接器的要求,eD…

【风控】可解释机器学习之InterpretML

【风控】可解释机器学习之InterpretML 在金融风控领域&#xff0c;机器学习模型因其强大的预测能力而备受青睐。然而&#xff0c;随着模型复杂性的增加&#xff0c;模型的可解释性逐渐成为一个挑战。监管要求、业务逻辑的透明度以及对模型决策的信任度&#xff0c;都迫切需要我…

Java垃圾回收_1

一、垃圾回收 1.如何判断对象可以回收 &#xff08;1&#xff09;引用计数法 存在循环引用问题&#xff0c; Java未使用这种算法 在引用计数法中&#xff0c;每个对象都有一个引用计数器&#xff0c;记录着指向该对象的引用数量。当引用计数器为零时&#xff0c;表示没有任…