项目场景:
问题:遵循版本稳定的前提下,搭建权限认证框架,基于SpringBoot2.x+SpringSecurity向上依赖+jjwt0.7.0构建用户认证鉴权,起因是某L觉得jjwt0.7.0版本,官方已经放弃维护,且从maven仓库对0.7.0版本使用量对比来看,遂放弃0.7.0转而升级为0.11.5,由此引发的token生成和解析的一系列BUG
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.14</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.isungent.ird</groupId>
<artifactId>epdx</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>epdx</name>
<description>Enterprise Data Platform</description>
<properties>
<java.version>1.8</java.version>
<mybatis-plus.version>3.5.3.2</mybatis-plus.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- NOTE: cannot exclude jackson because swagger needs it. -->
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.mybatis.spring.boot</groupId>-->
<!-- <artifactId>mybatis-spring-boot-starter-test</artifactId>-->
<!-- <version>2.3.1</version>-->
<!-- <scope>test</scope>-->
<!-- </dependency>-->
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter-test -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter-test</artifactId>
<version>${mybatis-plus.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.11.0</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<scope>runtime</scope>
<version>2.7.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
yml配置
spring:
datasource:
driverClassName: org.h2.Driver
url: jdbc:h2:mem:test-db
sql:
init:
data-locations: classpath:db/data.sql
encoding: utf8
schema-locations: classpath:db/schema.sql
h2:
console:
enabled: true
path: /h2-console
jwt:
secret: kiki
expiration: 18000
tokenHeader: Authorization
loginFailMaxNum: 5
tokenPrefix: authorization-
authUserName: AuthorizationUserName
authRole: admin
authEmail: admin@example.com
JwtTokenUtil
package cn.isungent.ird.epdx.common.util;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @author kiki
* @date 2023/10/10
* @description
*/
@Component
@ConfigurationProperties(prefix = "jwt")
public class JwtTokenUtil {
private static final String CLAIM_KEY_USERNAME = "sub";
private static final String CLAIM_KEY_CREATED = "$2a$10$khMzocJpIzuhkiqSFLQ/euwedqw/ocRjZUI0SsS/TDY0e4RZIxh0C";
private String secret;
private Integer expiration;
public Claims getClaimsFromToken(String token, String secret) {
Claims claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
return claims;
}
private String generateToken(Map<String, Object> claims) {
return Jwts.builder()
.setClaims(claims)
.setExpiration(generateExpirationDate())
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public String generateToken(String userEmail) {
Map<String, Object> claims = new HashMap<>(16);
claims.put(CLAIM_KEY_USERNAME, userEmail);
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
private Date generateExpirationDate() {
return new Date(System.currentTimeMillis() + expiration * 1000);
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public Integer getExpiration() {
return expiration;
}
public void setExpiration(Integer expiration) {
this.expiration = expiration;
}
}
如下,升级版本后,postman模拟登录报错
consul打印
io.jsonwebtoken.security.WeakKeyException: The signing key's size is 24 bits which is not secure enough for the HS512 algorithm. The JWT JWA Specification (RFC 7518, Section 3.2) states that keys used with HS512 MUST have a size >= 512 bits (the key size must be greater than or equal to the hash output size). Consider using the io.jsonwebtoken.security.Keys class's 'secretKeyFor(SignatureAlgorithm.HS512)' method to create a key guaranteed to be secure enough for HS512. See https://tools.ietf.org/html/rfc7518#section-3.2 for more information.
at io.jsonwebtoken.SignatureAlgorithm.assertValid(SignatureAlgorithm.java:387)
问题描述
翻译:The signing key's size is 24 bits which is not secure enough for the HS512 algorithm. The JWT JWA Specification (RFC 7518, Section 3.2) states that keys used with HS512 MUST have a size >= 512 bits (the key size must be greater than or equal to the hash output size). Consider using the io.jsonwebtoken.security.Keys class's 'secretKeyFor(SignatureAlgorithm.HS512)' method to create a key guaranteed to be secure enough for HS512. See https://tools.ietf.org/html/rfc7518#section-3.2 for more information.大概意思是说:io.jsonwebtoken.security.WeakKeyException:签名密钥的大小是24位,对于HS512算法来说不够安全。JWT JWA规范(RFC 7518, Section 3.2)规定,与HS512一起使用的密钥的大小必须大于= 512位(密钥大小必须大于或等于哈希输出的大小)。考虑使用io.jsonwebtoken.security.Keys类的'secretKeyFor(signaturealalgorithm .HS512)'方法来创建一个保证对HS512足够安全的密钥。参见https:tools.ietf.orghtmlrfc7518section-3.2获取更多信息
综上:就是说升级后,那个密钥的长度要大于512,就是yml中配置secrect的配置项,太短了
解决方案:
提示:修改yml配置项jwt.secrect的密钥字符串的值为AAAAB3NzaC1yc2EAAAADAQABAAABgQDb73eBHCS6avoS2hnC3Zzf3N1JQax2Wg2Z9uvxIq6MlORGGFnrhV3jlDD2+iXsoM1UUEOIvhGMeeAt9EWJ8MVIbKOfHzChwkHojUlTFd87qWxCEfS9LWcl1d1Hsx9R1R5Uj31xfP76XlQAR+vhOGQtk1RW1RInxWZVo/++Bts9iNCvxMCx5c/v9Dhgrkj+CD2b1YoYILn6xZjjRMOVB33+xV5ERO5mluHQV+8xKght75WQqaOz4lARBsfxgOpMAzVU6IYCz2qGztrYNCjnjgBLee+Kok4belJM7fjW5ntuP7kPGLAnxfkZQWdR/iG71PuZmcNngesxaqIxVD3Hwa6Qfv2cUsS2bJn/L51uT5k07obZF5poxssCx7qzhe+1Aa1sDfct2vfkqzYCikI9ioKqM999dKiqYPP13ThfYfaWRuVKor3/tgZkbKmhf7+BH8pLnS03/ewZZgwxxx=
注意:值可以是任意字符串,非固定。