spring-security-oauth2-jose 模块的一些 DEMO

长孙弘盛
2023-12-01

前言

Spring Security 提供了对 OAuth2 的支持,该包下提供 4 个模块:

  • oauth2-core,基础核心模块
  • oauth2-joseJOSE:Javascript Object Signing and Encryption,对应 JWS JWE 的实现
  • oauth2-clientoauth2 client 组件的实现
  • oauth2-resource-serveroauth2 resource server 组件的实现

本文是基于 oauth2-jose 模块的一些 demo 展示,对应依赖 spring-securty-oauth2-jose

Nimbus

Nimbus 应该是目前最好用的 JWT API 了,spring-security-oauth2-jose 也是基于 Nimbus API 的封装

demo

RSA256

	@Test
    public void rs256() throws JOSEException, ParseException, InterruptedException {

        // 生成 REA256 密钥对
        RSAKey rsaKey = new RSAKeyGenerator(2048)
                .generate();
        RSAKey publicJWK = rsaKey.toPublicJWK();
        String publicJWKJson = publicJWK.toJSONString();

        // JWT 加签
        JWSSigner signer = new RSASSASigner(rsaKey);
        /**
         * JWTClaimsSet:JWT 携带信息的封装,有一些既定的属性比如
         *      issuer
         *      subject
         *      ...
          */
        JWTClaimsSet jwtClaimsSet = new JWTClaimsSet.Builder()
                .expirationTime(new Date(new Date().getTime() + 5 * 1000))
                .audience("xsn")
                .build();
        SignedJWT signedJWT = new SignedJWT(
                new JWSHeader(JWSAlgorithm.RS256)
                , jwtClaimsSet
        );
        signedJWT.sign(signer);
        String jwt = signedJWT.serialize();

        // TimeUnit.SECONDS.sleep(6);

        // 用公钥验签 JWT
        SignedJWT parse = SignedJWT.parse(jwt);
        JWSVerifier verifier = new RSASSAVerifier(
                RSAKey.parse(publicJWKJson)
        );
        boolean verify = parse.verify(verifier);

        if (verify) {
            System.out.println(parse.getJWTClaimsSet().getAudience());
        }
    }
  • RSA256 应该是最常用的 JWS 算法,一般地:认证中心基于该算法加签,客户端则基于对应的 公钥 验签
  • Nimbus 提供开箱即用的 API 生成 RSAKey
  • 可以基于 JWTClaimsSet Builder API 轻松的构造 JWT 的信息,它还定义了一些默认的属性比如 issuer subject expireTime
  • 上述 demo 包含基于 RSA256加签验签

HS256

	@Test
    public void hs256() throws JOSEException, ParseException {
        // HS256 对称密钥加密
        SecureRandom random = new SecureRandom();
        byte[] bytes = new byte[32];
        random.nextBytes(bytes);

        JWSSigner signer = new MACSigner(bytes);
        JWSObject jwt = new JWSObject(
                new JWSHeader(JWSAlgorithm.HS256)
                , new Payload("hello jwt")
        );
        jwt.sign(signer);

        String serialize = jwt.serialize();

        JWSObject parse = JWSObject.parse(serialize);
        JWSVerifier verifier = new MACVerifier(bytes);
        boolean verify = parse.verify(verifier);
        if (verify) {
            System.out.println(parse.getPayload());
        }
    }
  • 对称密钥算法 HS256
  • 大致过程与 RSA256 类似

JWKMatcher

	@Test
    public void jwkMatcher() throws JOSEException {

        // JWK: Json Web Key
        JWK jwk1 = new RSAKey.Builder(new RSAKeyGenerator(2048).generate())
                .keyID("id1")
                .keyOperations(new HashSet<KeyOperation>() {{ add(KeyOperation.SIGN); }})
                .build();
        JWK jwk2 = new RSAKey.Builder(new RSAKeyGenerator(2048).generate())
                .keyID("id1")
                .keyOperations(new HashSet<KeyOperation>() {{ add(KeyOperation.ENCRYPT); }})
                .build();
        JWK jwk3 = new RSAKey.Builder(new RSAKeyGenerator(2048).generate())
                .keyID("id3")
                .keyOperations(new HashSet<KeyOperation>() {{ add(KeyOperation.ENCRYPT); }})
                .build();

        // JWK 集合
        JWKSet jwkSet = new JWKSet(new ArrayList<JWK>() {{
            add(jwk1);
            add(jwk2);
            add(jwk3);
        }});

        /**
         * JWKMatcher 可以基于 keyId keyOperation JWT Head
         *      等属性从 JWKSet 中匹配对应的 JWK
         */
        JWKMatcher matcher1 = new JWKMatcher.Builder()
                .keyID("id1")
                .build();
        JWKMatcher matcher2 = new JWKMatcher.Builder()
                .keyOperations(KeyOperation.SIGN)
                .build();

        List<JWK> select1 = new JWKSelector(matcher1).select(jwkSet);
        List<JWK> select2 = new JWKSelector(matcher2).select(jwkSet);
        select1.forEach(System.out::println);
        System.out.println("----");
        select2.forEach(System.out::println);
    }
  • JWKJson Web Key,比如 RSAKey
  • JWKSetJWK 的集合封装
  • JWKMatcher 则是基于 keyID keyOperation 等属性从 JWKSet 中匹配对应的 JWK 集合

spring-security-oauth2-jose

spring-security-oauth2-jose 底层依赖 Nimbus,主要封装了两个大的 API

  • JwtEncoder,负责对 JWT 编码(JWSJWE
  • JwtDecoder,负责 JWT 的解码

demo

	@Test
    public void rs256() throws JOSEException, InterruptedException {
        // JwtEncoder:spring security jose 基于 Nimbus 的 JWT 加密类
        RSAKey rsaKey = new RSAKeyGenerator(2048).generate();
        JwtEncoder encoder = new NimbusJwtEncoder(
                new ImmutableJWKSet<>(
                        new JWKSet(rsaKey)
                )
        );
        Jwt jwt = encoder.encode(JwtEncoderParameters.from(
                        JwsHeader.with(SignatureAlgorithm.RS256)
                                // .jwk(new HashMap<>() {{ put("publicKey", rsaKey.toPublicKey()); }})
                                .build()
                        , JwtClaimsSet.builder()
                                .expiresAt(Instant.now().plusSeconds(5))
                                .subject("xsn")
                                .claim("content", "hello spring jwt")
                                .build()
                )
        );
        String tokenValue = jwt.getTokenValue();

        // TimeUnit.SECONDS.sleep(6);

        // DefaultJWTProcessor 自带对 JWT 过期的校验
        DefaultJWTProcessor<SecurityContext> processor = new DefaultJWTProcessor<>();
        processor.setJWSKeySelector(new JWSVerificationKeySelector<SecurityContext>(
                JWSAlgorithm.RS256
                , new ImmutableJWKSet<>(new JWKSet(rsaKey.toPublicJWK()))
        ));
        JwtDecoder decoder = new NimbusJwtDecoder(processor);
        Jwt decode = decoder.decode(tokenValue);
        System.out.println(decode.getClaim(JwtClaimNames.SUB).toString());
        System.out.println(decode.getClaim("content").toString());
    }
  • 示例中是基于 RSA256 算法的 加签 验签 过程
  • DefaultJWTProcessor 包含对 JWT 过期时间的校验,对应 JwtClaimNamesexpireTime 属性,比如示例中的 JWT 有效期为 5s

总结

这些 API 一般可能不会直接使用,是 Spring SecurityOAuth2 JWT 的核心支持

完整 demo 示例

 类似资料: