You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JWT解码报错:Illegal base64url character: ' ' 问题求助

问题:JWT解码触发"Illegal base64url character: ' '"错误,无法提取Claims信息

登录生成JWT Token后,在其他服务验证用户身份时,解码Token提取用户信息并匹配数据库时触发错误:Illegal base64url character: ' ',且执行System.out.println(claims)无输出。

相关代码

package com.example.RentCar.Services;

import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

import com.example.RentCar.DTOS.UserRequestDTO;
import com.example.RentCar.Models.User;
import com.example.RentCar.Repository.UserRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;


import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;

@Service
public class JwtServicesImpl implements JwtServices {
    private UserRequestDTO userRequestDTO;
    //private final UserRepository userRepository;
   // @Value("${token.signing.key}")
    private String jwtSigningKey = "413F4428472B4B6250655368566D5970337336763979244226452948404D6351";


    @Override
    public String extractUserName(String token) {
        return extractClaim(token, Claims::getSubject);
    }
    @Override
    public String generateToken(User user) {
        return generateToken(new HashMap<>(), user);
    }



    @Override
    public boolean isTokenValid(String token, UserServicesImpl userServices) {
        final String email = extractUserName(token);
        UserDetails user = (userServices.loadUserByUsername(email));
        String email2 = user.getUsername();
        return (email.equals(email2) && !isTokenExpired(token));
    }
    private <T> T extractClaim(String token, Function<Claims, T> claimsResolvers) {
        final Claims claims = extractAllClaims(token);
        System.out.println(claims); //not printing
        return claimsResolvers.apply(claims);
    }

    private String generateToken(Map<String, Object> extraClaims, User user) {

        return Jwts.builder().setClaims(extraClaims).setSubject(user.getUserEmail())
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 24))
                .signWith(getSigningKey(), SignatureAlgorithm.HS256).compact();
    }
    private boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    private Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    private Claims extractAllClaims(String token) {
        return Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token)
                .getBody();
    }

    private Key getSigningKey() {
       // System.out.println(jwtSigningKey);
        byte[] keyBytes = Decoders.BASE64.decode(jwtSigningKey);
        return Keys.hmacShaKeyFor(keyBytes);
    }
}

错误栈

io.jsonwebtoken.io.DecodingException: Illegal base64url character: ' '
    at io.jsonwebtoken.io.Base64.ctoi(Base64.java:221)
    at io.jsonwebtoken.io.Base64.decodeFast(Base64.java:270)
    at io.jsonwebtoken.io.Base64Decoder.decode(Base64Decoder.java:36)
    at io.jsonwebtoken.io.Base64Decoder.decode(Base64Decoder.java:23)
    at io.jsonwebtoken.io.ExceptionPropagatingDecoder.decode(ExceptionPropagatingDecoder.java:36)
    at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:288)
    at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:529)
    at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:589)
    at io.jsonwebtoken.impl.ImmutableJwtParser.parseClaimsJws(ImmutableJwtParser.java:173)
    at com.example.RentCar.Services.JwtServicesImpl.extractAllClaims(JwtServicesImpl.java:76)
    at com.example.RentCar.Services.JwtServicesImpl.extractClaim(JwtServicesImpl.java:55)
    at com.example.RentCar.Services.JwtServicesImpl.extractUserName(JwtServicesImpl.java:34)
    at com.example.RentCar.Controllers.UserController.ModifyAddress(UserController.java:36)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
    at java.base/java.lang.reflect.Method.invoke(Method.java:578)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1081)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:974)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1011)

解决方案

1. 清理Token中的空格字符

错误根源是传入的Token包含空格,直接导致Base64解码失败。修改extractAllClaims方法,先移除Token中的所有空格:

private Claims extractAllClaims(String token) {
    // 移除Token中的所有空格字符
    String cleanToken = token.replaceAll("\\s+", "");
    return Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(cleanToken)
            .getBody();
}

2. 规范Token的获取逻辑

如果Token是从HTTP请求的Authorization头获取,注意标准格式是Bearer <token>,需要先截取Bearer 前缀后的内容,再清理空格:

// 在Controller或过滤器中处理Token
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
    // 截取前缀并清理空格
    String token = authHeader.substring(7).replaceAll("\\s+", "");
    // 传入JwtServices进行验证
}

3. 修复签名密钥的解码方式

你的签名密钥413F4428472B4B6250655368566D5970337336763979244226452948404D6351是十六进制字符串,但当前代码用Decoders.BASE64.decode解码,这是错误的,应该改用十六进制解码:

private Key getSigningKey() {
    // 十六进制字符串转字节数组
    byte[] keyBytes = Decoders.HEX.decode(jwtSigningKey);
    return Keys.hmacShaKeyFor(keyBytes);
}

注:错误的解码方式会导致签名验证失败,即使解决了空格问题,后续仍可能出现Token无效的情况。

4. 验证生成的Token格式

可以在generateToken方法中打印生成的Token,确认没有包含空格或异常字符:

private String generateToken(Map<String, Object> extraClaims, User user) {
    String token = Jwts.builder().setClaims(extraClaims).setSubject(user.getUserEmail())
            .setIssuedAt(new Date(System.currentTimeMillis()))
            .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 24))
            .signWith(getSigningKey(), SignatureAlgorithm.HS256).compact();
    // 打印生成的Token,检查格式是否正常
    System.out.println("Generated Token: " + token);
    return token;
}

内容的提问来源于stack exchange,提问作者Max

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 10:18:11