记录一个JWT工具模块

本文详细介绍了如何在Spring Boot项目中集成JWT,包括模块结构、依赖引入、配置类JWTConfig、YAML配置、TokenService接口及其实现、配置注入和测试。重点展示了如何创建和验证JWT令牌,以及关键步骤的代码示例。
摘要由CSDN通过智能技术生成

1.模块结构

2.引入依赖

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
        </dependency>

3.创建JWT配置类

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

@Data
@ConfigurationProperties(prefix = "com.security.config.jwt")
public class JWTConfig {
    private String sign;  //保存签名信息
    private String issuer;  //保存签发者
    private String secret;  //加密的密钥
    private long expire;  //失效时间
}

4.创建application.yml配置文件

com:
 security:
   config:
     jwt:
      sign: xl
      issuer: xiaoli
      secret: xiaolixiaoliaidabuli
      expire: 10000 #单位:秒
spring:
  application:
    name: JWT-TEST

5.创建TokenService

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;

import javax.crypto.SecretKey;
import java.util.Map;

/**
 * Token 操作接口
 */
public interface ITokenService {
    public SecretKey generalKey();  //获取当前JWT数据的加密KEY

    public String createToken(String id, Map<String, Object> subject);  //创建Token的数据内容,同时要求保存用户的id以及所需要的附加数据

    public Jws<Claims> parseToken(String token);  //解析Token数据

    public boolean verifyToken(String token);  //验证Token有效性

    public String refreshToken(String token);  //刷新Token
}

6.实现TokenService

import com.cloud.jwt.config.JWTConfig;
import com.cloud.jwt.service.ITokenService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

//此时的组件中的代码需要其他的模块去引用,所有未必会与扫描包相同
public class TokenServiceImpl implements ITokenService {
    @Autowired  //SpringBoot容器启动时会自动提供Jacks示实例
    private ObjectMapper objectMapper;

    @Autowired
    private JWTConfig jwtConfig;  //获取JWT的相关配置属性

    @Value("${spring.application.name}") //通过SpEL进行配置注入
    private String applicationName;  //应用名称

    private SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;  //签名算法

    @Override
    public SecretKey generalKey() {
        byte[] encodeKey = Base64.encodeBase64(Base64.encodeBase64(this.jwtConfig.getSecret().getBytes()));
        SecretKey key = new SecretKeySpec(encodeKey, 0, encodeKey.length, "AES");
        return key;
    }

    @Override
    public String createToken(String id, Map<String, Object> subject) {
        //使用JWT数据结构进行开发,目的之一就是不需要进行JWT数据的分布式存储,所以所谓的缓存主键、数据库都用不到
        //所有Token都存在有保存失效的问题,所以需要通过当前时间来进行计算
        Date nowDate = new Date();  //获取当前时间
        Date expireDate = new Date(nowDate.getTime() + this.jwtConfig.getExpire() * 1000);  //证书过期时间
        Map<String, Object> cliams = new HashMap<>();  //保存所有附加数据
        cliams.put("client", "admin");
        Map<String, Object> headers = new HashMap<>(); // 保存头信息
        headers.put("author", "小李小李爱搭不理");  //作者,也可以通过配置处理
        //后续会有很多模块引用此组件,为了后续的安全,最佳的做法就是设置一个模块名称的信息
        headers.put("module", this.applicationName);
        JwtBuilder builder = null;
        try {
            builder = Jwts.builder()  //进行JWTBuilder对象实例化
                    .setClaims(cliams)  //保存附加的数据内容
                    .setHeader(headers)  //保存头信息
                    .setId(id)  //保存ID信息
                    .setIssuedAt(nowDate)  //签发时间
                    .setIssuer(this.jwtConfig.getIssuer())  //设置签发者
                    .setSubject(this.objectMapper.writeValueAsString(subject))  //索要传递的数据转为JSON
                    .signWith(this.signatureAlgorithm, this.generalKey())  //获取签名算法
                    .setExpiration(expireDate);  //配置失效时间
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        return builder.compact();  //构建Token
    }

    @Override
    public Jws<Claims> parseToken(String token) {
        if (this.verifyToken(token)) {  //只有Token有效才进行解析
            Jws<Claims> claims = Jwts.parser().setSigningKey(this.generalKey()).parseClaimsJws(token);
            return claims;
        }
        return null;  //解析失败返回null
    }

    @Override
    public boolean verifyToken(String token) {
        try {
            Jwts.parser().setSigningKey(this.generalKey()).parseClaimsJws(token).getBody();
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public String refreshToken(String token) {
        if (this.verifyToken(token)) {
            Jws<Claims> jws = this.parseToken(token);  //解析Token数据
            try {
                return this.createToken(jws.getBody().getId(), this.objectMapper.readValue(jws.getBody().getSubject(), Map.class));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
                return null;
            }
        }
        return null;
    }
}

7.配置注入属性

import com.cloud.jwt.config.JWTConfig;
import com.cloud.jwt.service.ITokenService;
import com.cloud.jwt.service.impl.TokenServiceImpl;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties({JWTConfig.class})  //配置注入属性
public class JWTAutoConfiguration {
    @Bean("tokenService")
    public ITokenService getTokenServiceBean() {
        return new TokenServiceImpl();
    }
}

8.添加spring.factories

org.springframework.boot.context.properties.EnableConfigurationProperties=com.cloud.jwt.autoconfig.JWTAutoConfiguration

9.编写测试类测试

注意:测试的话需要修改TokenServiceImpl实现类里的ObjectMapper注入,会提示找不到;

修改:去掉@Autowired,直接new ObjectMapper组件即可,测试完记得改回来!

import com.cloud.jwt.StartJWTConfiguration;
import com.cloud.jwt.service.ITokenService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwsHeader;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@SpringBootTest(classes = StartJWTConfiguration.class)
public class testTokenService {

    @Autowired
    private ITokenService tokenService;
    //testCreateToken()方法生成的Token
    private String jwt = "eyJhdXRob3IiOiJjZGlpcC5jbiIsIm1vZHVsZSI6IkpXVC1DRElJUCIsImFsZyI6IkhTMjU2In0.eyJzdWIiOiJ7XCJ1c2VyX2lkXCI6XCIxXCIsXCJ1c2VyX25hbWVcIjpcIuWwj-eqnVwifSIsImlzcyI6ImNkaWlwLmNuIiwiZXhwIjoxNjQ3NTE0MTQyLCJpYXQiOjE2NDc1MDQxNDIsImNsaWVudF9pZCI6ImFkbWluIiwianRpIjoiY2RpaXAtZDI4ZjhkZmYtYzI5ZC00NDBkLWFkMjAtODNhYzU1YjE4OTBjIn0.fupPvV7Uzhl_VClRgqv3uJTdLJuJ6CWLb7wqhciYk3M";

    @Test
    public void testCreateToken() {
        Map<String, Object> map = new HashMap<>();
        map.put("user_id", "1");
        map.put("user_name", "小窝");
        String id = "xiaoli-" + UUID.randomUUID();  //JWT-ID数据
        System.out.println(this.tokenService.createToken(id, map));
    }

    @Test
    public void testParseToken() {
        Jws<Claims> jws = this.tokenService.parseToken(jwt);
        System.out.println("JWT签名数据:" + jws.getSignature());
        JwsHeader header = jws.getHeader();  //获取头信息
        header.forEach((headerName, headerValue) -> {
            System.out.println("【JWT头信息】" + headerName + "=" + headerValue);
        });
        Claims claims = jws.getBody();
        claims.forEach((bodyName, bodyValue) -> {
            System.out.println("【JWT数据】" + bodyName + "=" + bodyValue);
        });
    }

    @Test
    public void testVerifyJWT(){
        System.out.println("【JWT数据验证】"+this.tokenService.verifyToken(jwt));
    }

    @Test
    public void testRefreshJWT(){
        System.out.println("【JWT刷新数据】"+this.tokenService.refreshToken(jwt));
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值