在 Spring boot 资源服务器中使用自定义 JWT 解码器

Sha*_*.es 4 java spring oauth-2.0 jwt nimbus-jose-jwt

我正在使用 Spring boot 资源服务器。身份验证服务器发出 JWT。该 JWT 使用密钥重新编码(使用 AES),并且在资源服务器中,我应该在将 JWT 发送到 JwtAuthenticator 之前对其进行解码(从 AES)。
现在,我有了安全配置。

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(new KeycloakRoleConverter());

        http
                .authorizeRequests()
                .antMatchers(HttpMethod.GET, "/users/status/check")
                .hasRole("developer")
                .anyRequest().authenticated()
                .and()
                .oauth2ResourceServer()
                .jwt()
                .decoder(new JWTDecoder())
                .jwtAuthenticationConverter(jwtAuthenticationConverter);
    }
Run Code Online (Sandbox Code Playgroud)

和 JWT 解码器

import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTParser;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtException;

import java.text.ParseException;

public class JWTDecoder implements JwtDecoder {
    @Override
    public Jwt decode(String token) throws JwtException {


        //decrypt from AES here


        JWT jwt = null;
        try {
            jwt = JWTParser.parse(token);
        } catch (ParseException e) {
            e.printStackTrace();
        }


        return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

那我该怎么办?该函数应该返回org.springframework.security.oauth2.jwt.Jwt. 如何转换String token为 Jwt?
我尝试了以下操作,但出现了问题。

    private Jwt createJwt(String token, JWT parsedJwt) {
        try {
            Map<String, Object> headers = new LinkedHashMap<>(parsedJwt.getHeader().toJSONObject());
            Map<String, Object> claims = parsedJwt.getJWTClaimsSet().getClaims();
            return Jwt.withTokenValue(token)
                    .headers(h -> h.putAll(headers))
                    .claims(c -> c.putAll(claims))
                    .build();
        } catch (Exception ex) {
            if (ex.getCause() instanceof ParseException) {
                throw new JwtException("There is a problem parsing the JWT.");
            } else {
                throw new JwtException("There is a problem decoding the JWT.");
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

我收到的错误:

java.lang.IllegalArgumentException:时间戳必须是 Instant 类型:java.lang.Long



我正在使用 Keycloak 生成 JWT。因此,jwt.io 中令牌的 exp 字段是"exp": 1657363340,。但是在我的代码中解析 JWT 后,它更改为日期格式。所以,我将 exp 更改为Instant,我的最终方法如下:
    private Jwt createJwt(String token, JWT parsedJwt) {
        try {
            Map<String, Object> headers = new LinkedHashMap<>(parsedJwt.getHeader().toJSONObject());
            Map<String, Object> claims = parsedJwt.getJWTClaimsSet().getClaims();
            Jwt.Builder finalJwt = Jwt.withTokenValue(token)
                    .headers(h -> h.putAll(headers))
                    .claims(c -> c.putAll(claims));
            finalJwt.expiresAt(((Date) claims.get("exp")).toInstant());
            return finalJwt.build();
        } catch (Exception ex) {
            if (ex.getCause() instanceof ParseException) {
                throw new JwtException("There is a problem parsing the JWT: " + ex.getMessage());
            } else {
                throw new JwtException("There is a problem decoding the JWT: " + ex.getMessage());
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

但问题仍然存在。

Sha*_*.es 6

Instant正如@Jose 告诉我的,我使用时间戳类型设置了过期时间的值。然后,我将其设置为JWT 的exp和字段。iat我的最终功能如下:

Map<String, Object> headers = new LinkedHashMap<>(parsedJwt.getHeader().toJSONObject());
Map<String, Object> claims = new HashMap<>();
for (String key : parsedJwt.getJWTClaimsSet().getClaims().keySet()) {
    Object value = parsedJwt.getJWTClaimsSet().getClaims().get(key);
    if (key.equals("exp") || key.equals("iat")) {
        value = ((Date) value).toInstant();
    }
    claims.put(key, value);
}
return Jwt.withTokenValue(token)
        .headers(h -> h.putAll(headers))
        .claims(c -> c.putAll(claims))
        .build();
Run Code Online (Sandbox Code Playgroud)