Spring Security
提供了对 OAuth2
的支持,该包下提供 4
个模块:
oauth2-core
,基础核心模块oauth2-jose
,JOSE:Javascript Object Signing and Encryption
,对应 JWS
JWE
的实现oauth2-client
,oauth2 client
组件的实现oauth2-resource-server
,oauth2 resource server
组件的实现本文是基于 oauth2-jose
模块的一些 demo
展示,对应依赖 spring-securty-oauth2-jose
Nimbus
应该是目前最好用的 JWT API
了,spring-security-oauth2-jose
也是基于 Nimbus API
的封装
@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
的 加签
和 验签
@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
类似 @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);
}
JWK
,Json Web Key
,比如 RSAKey
JWKSet
,JWK
的集合封装JWKMatcher
则是基于 keyID
keyOperation
等属性从 JWKSet
中匹配对应的 JWK
集合spring-security-oauth2-jose
底层依赖 Nimbus
,主要封装了两个大的 API
:
JwtEncoder
,负责对 JWT
编码(JWS
或 JWE
)JwtDecoder
,负责 JWT
的解码 @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
过期时间的校验,对应 JwtClaimNames
的 expireTime
属性,比如示例中的 JWT
有效期为 5s
这些 API
一般可能不会直接使用,是 Spring Security
对 OAuth2 JWT
的核心支持