关于SpringBoot集成SpringSecurity+jjwt生成token,(DecodingException: Illegal base64 character: ‘-‘)

文章讲述了从jjwt0.9.1版本升级到0.11.2版本时,JWT生成和解析方法的变化,包括移除旧版本的直接依赖,转为使用API、实施SM4解密和使用自定义密钥生成过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、问题描述

1.背景阐述

在jjwt老版本0.9.1之前,只需要引入jjwt即可生成认证token,至于maven仓库显示为什么在0.9.1版本后jjwt就移动到了jjwt-api,官网并没有详细阐述

在这里插入图片描述
jjwt官网阐述了使用方法如下:
在这里插入图片描述

2.使用0.9.1及之前的jjwt版本

<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

此时只需要编写工具类

package com.juhe.digital.util;

import com.alibaba.fastjson.JSONObject;

import com.juhe.digital.config.JwtConfig;
import com.juhe.digital.pojo.bo.UserBO;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * @author kiki
 * @date 2023/5/5
 * @description jwt工具类
 */
@Component
@ConfigurationProperties(prefix = "jwt")
@Slf4j
@Data
public class JwtTokenUtil {

    private static final String CLAIM_KEY_USERNAME = "sub";
    private static final String CLAIM_KEY_CREATED = "created";

    private String secret;

    private Integer expiration;

    private String tokenHeader;

    private Integer expireVerifyCode;

    private Integer loginFailRange;

    private Integer loginFailMaxNum;

    /**
     * 从token中获取JWT中的负载
     */
    public Claims getClaimsFromToken(String token, String secret) {
        Claims claims = Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
        return claims;
    }

    /**
     * 根据负责生成JWT的token
     */
    private String generateToken(Map<String, Object> claims) {
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(generateExpirationDate())
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    /**
     * 从token中获取JWT中的负载
     */
    public Claims getClaimsFromToken(String token) {
        Claims claims = null;
        try {
            claims = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            log.info("JWT格式验证失败:{}", token);
        }
        return claims;
    }

    /**
     * 生成token的过期时间
     */
    private Date generateExpirationDate() {
        return new Date(System.currentTimeMillis() + expiration * 1000);
    }

    /**
     * 从token中获取登录用户名
     */
    public String getUserNameFromToken(String token) {
        String username;
        try {
            Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }

    /**
     * 验证token是否还有效
     *
     * @param token       客户端传入的token
     * @param userDetails 从数据库中查询出来的用户信息
     */
    public boolean validateToken(String token, UserDetails userDetails) {
        String username = getUserNameFromToken(token);
        return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
    }

    /**
     * 判断token是否已经失效
     */
    private boolean isTokenExpired(String token) {
        Date expiredDate = getExpiredDateFromToken(token);
        return expiredDate.before(new Date());
    }

    /**
     * 从token中获取过期时间
     */
    public Date getExpiredDateFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.getExpiration();
    }

    /**
     * 根据用户信息生成token
     */
    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);
    }

    /**
     * 判断token是否可以被刷新
     */
    public boolean canRefresh(String token) {
        return !isTokenExpired(token);
    }

    /**
     * 刷新token
     */
    public String refreshToken(String token) {
        Claims claims = getClaimsFromToken(token);
        claims.put(CLAIM_KEY_CREATED, new Date());
        return generateToken(claims);
    }

    /**
     * 刷新token
     */
    public String refreshToken(Claims claims) {
        claims.put(CLAIM_KEY_CREATED, new Date());
        return generateToken(claims);
    }

    public static String getToken() {
        String token = "";
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String th = request.getHeader(JwtConfig.tokenHeader);
        if (null != JwtConfig.tokenHeader && JwtConfig.tokenHeader.startsWith(JwtConfig.tokenPrefix)) {
            // 截取JWT前缀
            token = th.replace(JwtConfig.tokenPrefix, "");
        }
        return token;
    }

}

是这样生成和解析token的
在这里插入图片描述
如此便可直接使用。

1.2 项目升级

近期由于项目在对接第三方平台获取互认信息的时候,出现了问题,原因是我们的项目采用的旧版的jjwt,也就是0.9.1之前的版本。在对接第三方进行加解密认证的时候,引入了如下依赖

		<dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.2</version>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.2</version>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.11.2</version>
        </dependency>

大概业务流程是,对方提供了sm4加密的token,首先通过sm4解密获取初始的token信息,再通过jjwt提供的方法来进行解析

 Jwt jwt = Jwts.parserBuilder().setSigningKey(key).build().parse(split[1]);
 DefaultClaims obj = (DefaultClaims) jwt.getBody();
 String token = obj.getSubject();

如此获取到加密的信息。
但是如上述说的,jjwt 0.9.1不是这种写法,在内部实现里边高版本已经摒弃了0.9.1的做法。采用如下方式

	public Key generateKey(){
        String encode = Base64.encode(secret);
        byte[] keyBytes = Decoders.BASE64.decode(encode);
        Key key = Keys.hmacShaKeyFor(keyBytes);
        return key;
    }

    /**
     * 根据负责生成JWT的token
     */
    private String generateToken(Map<String, Object> claims) {
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(generateExpirationDate())
                .signWith(generateKey())
                .compact();
    }

    /**
     * 从token中获取JWT中的负载
     */
    public Claims getClaimsFromToken(String token) {
        Claims claims = null;
        try {
            claims = Jwts.parserBuilder().setSigningKey(generateKey()).build().parseClaimsJws(token).getBody();
        } catch (Exception e) {
            log.info("JWT格式验证失败:{}", token);
        }
        return claims;
    }

如此完成版本迭代。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

慕木兮人可

感谢支持,勿忘初心

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值