Jwt Decoder Local Key Configuration
Adds support for configuring Resource Server DSL with a local public key. Fixes: gh-5131
This commit is contained in:
parent
75a2c2b729
commit
1ea73e7d8e
|
@ -20,10 +20,14 @@ import java.io.BufferedReader;
|
|||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Base64;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
@ -110,6 +114,7 @@ import static org.mockito.Mockito.mock;
|
|||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.security.oauth2.jwt.JwtProcessors.withJwkSetUri;
|
||||
import static org.springframework.security.oauth2.jwt.JwtProcessors.withPublicKey;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
|
@ -947,6 +952,44 @@ public class OAuth2ResourceServerConfigurerTests {
|
|||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
// -- single key
|
||||
|
||||
@Test
|
||||
public void requestWhenUsingPublicKeyAndValidTokenThenAuthenticates()
|
||||
throws Exception {
|
||||
|
||||
this.spring.register(SingleKeyConfig.class, BasicController.class).autowire();
|
||||
String token = this.token("ValidNoScopes");
|
||||
|
||||
this.mvc.perform(get("/")
|
||||
.with(bearerToken(token)))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenUsingPublicKeyAndSignatureFailsThenReturnsInvalidToken()
|
||||
throws Exception {
|
||||
|
||||
this.spring.register(SingleKeyConfig.class).autowire();
|
||||
String token = this.token("WrongSignature");
|
||||
|
||||
this.mvc.perform(get("/")
|
||||
.with(bearerToken(token)))
|
||||
.andExpect(invalidTokenHeader("signature"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenUsingPublicKeyAlgorithmDoesNotMatchThenReturnsInvalidToken()
|
||||
throws Exception {
|
||||
|
||||
this.spring.register(SingleKeyConfig.class).autowire();
|
||||
String token = this.token("WrongAlgorithm");
|
||||
|
||||
this.mvc.perform(get("/")
|
||||
.with(bearerToken(token)))
|
||||
.andExpect(invalidTokenHeader("algorithm"));
|
||||
}
|
||||
|
||||
// -- In combination with other authentication providers
|
||||
|
||||
@Test
|
||||
|
@ -1519,8 +1562,38 @@ public class OAuth2ResourceServerConfigurerTests {
|
|||
.oauth2ResourceServer()
|
||||
.jwt()
|
||||
.decoder(jwtDecoder);
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class SingleKeyConfig extends WebSecurityConfigurerAdapter {
|
||||
byte[] spec = Base64.getDecoder().decode(
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoXJ8OyOv/eRnce4akdan" +
|
||||
"R4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGmuLvolxsDncc2" +
|
||||
"UrhyMBY6DVQVgMSVYaPCTgW76iYEKGgzTEw5IBRQL9w3SRJWd3VJTZZQjkXef48O" +
|
||||
"cz06PGF3lhbz4t5UEZtdF4rIe7u+977QwHuh7yRPBQ3sII+cVoOUMgaXB9SHcGF2" +
|
||||
"iZCtPzL/IffDUcfhLQteGebhW8A6eUHgpD5A1PQ+JCw/G7UOzZAjjDjtNM2eqm8j" +
|
||||
"+Ms/gqnm4MiCZ4E+9pDN77CAAPVN7kuX6ejs9KBXpk01z48i9fORYk9u7rAkh1Hu" +
|
||||
"QwIDAQAB");
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.oauth2ResourceServer()
|
||||
.jwt();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Bean
|
||||
JwtDecoder decoder() throws Exception {
|
||||
RSAPublicKey publicKey = (RSAPublicKey)
|
||||
KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(this.spec));
|
||||
return new NimbusJwtDecoder(withPublicKey(publicKey).build());
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
eyJhbGciOiJSUzUxMiJ9.eyJleHAiOjQ2ODczNDQ2NzN9.hvVUW_xwUXd7nGm27E5tLTZ21x64YjP0o-TMW6t_bOkfG1Vp1AMEX8fXvSqeG0vK8TWiB2_keOGtH-eFmAGBEYXq1o1zj1BgMHeaZAVio9n-77DkTzQ7CiOF5M1M7B_Ng4K8ra4DpieZZXVjHTWsuOiU1hWoI1tIna8VucAxZln-oh7PkrYmgwFTlsL2Z9aZZYN_X7ECyRQDf3lRrLwr4Go_XpJ5i9F-GT5LvUYa42uggGjvq_frfb0t5wcmPgjtqiE6l2mnrYFjjKTq1nQRYrJ5wFWOHUTRxNsGS8PwrNxzh6JW1ZZTS0n_JIOvSh__w0WAB241QLoKBx4AETMLQA
|
|
@ -0,0 +1 @@
|
|||
eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjQ2ODczNDQ0MTd9.jfqDyHvpRXWF6KaRQS3cGT0HUSix09xwTPvUCtg9UJ2QR1Rx4MclGCli3yIHNm0vsRed4s-gZduVGfbj7enyKnpXCZE7dNxZENfm7P54OfJmlyJY3DvhzlyH_rtuOD4c_Q88J9uELd_pghikLlMtu8090UzTtwRfdo_JsDfMRAcDeYq7TTaL60w3AVarStwZAAy_dpi6bTEanm5hwkz4-deA4Bz4KentpvlcwB01IXw9DVYrW1lpzLgycwk_VbCK_LA1hjFnnjc3OnQaxvqydrBAlFD3ziklVAxGnKnrYzppixdwwztuga4XS36OhicIGXEkMf3oT3nzgcR309DP_A
|
|
@ -18,9 +18,13 @@ package org.springframework.security.oauth2.jwt;
|
|||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.nimbusds.jose.JWSAlgorithm;
|
||||
import com.nimbusds.jose.jwk.JWKSet;
|
||||
import com.nimbusds.jose.jwk.RSAKey;
|
||||
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
|
||||
import com.nimbusds.jose.jwk.source.JWKSource;
|
||||
import com.nimbusds.jose.jwk.source.RemoteJWKSet;
|
||||
import com.nimbusds.jose.proc.JWSKeySelector;
|
||||
|
@ -37,6 +41,7 @@ import org.springframework.http.HttpMethod;
|
|||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.client.RestOperations;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
@ -61,6 +66,16 @@ public final class JwtProcessors {
|
|||
return new JwkSetUriJwtProcessorBuilder(jwkSetUri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the given public key to validate JWTs
|
||||
*
|
||||
* @param key the public key to use
|
||||
* @return a {@link PublicKeyJwtProcessorBuilder} for further configurations
|
||||
*/
|
||||
public static PublicKeyJwtProcessorBuilder withPublicKey(RSAPublicKey key) {
|
||||
return new PublicKeyJwtProcessorBuilder(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for creating Nimbus {@link JWTProcessor} instances based on a
|
||||
* <a target="_blank" href="https://tools.ietf.org/html/rfc7517#section-5">JWK Set</a> uri.
|
||||
|
@ -159,4 +174,65 @@ public final class JwtProcessors {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for creating Nimbus {@link JWTProcessor} instances based on a
|
||||
* public key.
|
||||
*/
|
||||
public static final class PublicKeyJwtProcessorBuilder {
|
||||
private JWSAlgorithm jwsAlgorithm;
|
||||
private RSAKey key;
|
||||
|
||||
private PublicKeyJwtProcessorBuilder(RSAPublicKey key) {
|
||||
Assert.notNull(key, "key cannot be null");
|
||||
this.jwsAlgorithm = JWSAlgorithm.parse(JwsAlgorithms.RS256);
|
||||
this.key = rsaKey(key);
|
||||
}
|
||||
|
||||
private static RSAKey rsaKey(RSAPublicKey publicKey) {
|
||||
return new RSAKey.Builder(publicKey)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the given signing
|
||||
* <a href="https://tools.ietf.org/html/rfc7515#section-4.1.1" target="_blank">algorithm</a>.
|
||||
*
|
||||
* The value should be one of
|
||||
* <a href="https://tools.ietf.org/html/rfc7518#section-3.3" target="_blank">RS256, RS384, or RS512</a>.
|
||||
*
|
||||
* @param jwsAlgorithm the algorithm to use
|
||||
* @return a {@link JwtProcessors} for further configurations
|
||||
*/
|
||||
public PublicKeyJwtProcessorBuilder jwsAlgorithm(String jwsAlgorithm) {
|
||||
Assert.hasText(jwsAlgorithm, "jwsAlgorithm cannot be empty");
|
||||
this.jwsAlgorithm = JWSAlgorithm.parse(jwsAlgorithm);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the configured {@link JWTProcessor}.
|
||||
*mzRC
|
||||
* @return the configured {@link JWTProcessor}
|
||||
*/
|
||||
public JWTProcessor<SecurityContext> build() {
|
||||
if (!JWSAlgorithm.Family.RSA.contains(this.jwsAlgorithm)) {
|
||||
throw new IllegalStateException("The provided key is of type RSA; " +
|
||||
"however the signature algorithm is of some other type: " +
|
||||
this.jwsAlgorithm + ". Please indicate one of RS256, RS384, or RS512.");
|
||||
}
|
||||
|
||||
JWKSet jwkSet = new JWKSet(this.key);
|
||||
JWKSource<SecurityContext> jwkSource = new ImmutableJWKSet<>(jwkSet);
|
||||
JWSKeySelector<SecurityContext> jwsKeySelector =
|
||||
new JWSVerificationKeySelector<>(this.jwsAlgorithm, jwkSource);
|
||||
DefaultJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
|
||||
jwtProcessor.setJWSKeySelector(jwsKeySelector);
|
||||
|
||||
// Spring Security validates the claim set independent from Nimbus
|
||||
jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> { });
|
||||
|
||||
return jwtProcessor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,18 +16,30 @@
|
|||
|
||||
package org.springframework.security.oauth2.jwt;
|
||||
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.EncodedKeySpec;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Base64;
|
||||
|
||||
import com.nimbusds.jose.proc.BadJOSEException;
|
||||
import com.nimbusds.jose.proc.SecurityContext;
|
||||
import com.nimbusds.jwt.JWTClaimsSet;
|
||||
import com.nimbusds.jwt.proc.JWTProcessor;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
|
||||
import org.springframework.web.client.RestOperations;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
@ -39,9 +51,18 @@ import static org.springframework.security.oauth2.jwt.JwtProcessors.withJwkSetUr
|
|||
* Tests for {@link JwtProcessors}
|
||||
*/
|
||||
public class JwtProcessorsTest {
|
||||
private static final String JWK_SET = "{\"keys\":[{\"p\":\"49neceJFs8R6n7WamRGy45F5Tv0YM-R2ODK3eSBUSLOSH2tAqjEVKOkLE5fiNA3ygqq15NcKRadB2pTVf-Yb5ZIBuKzko8bzYIkIqYhSh_FAdEEr0vHF5fq_yWSvc6swsOJGqvBEtuqtJY027u-G2gAQasCQdhyejer68zsTn8M\",\"kty\":\"RSA\",\"q\":\"tWR-ysspjZ73B6p2vVRVyHwP3KQWL5KEQcdgcmMOE_P_cPs98vZJfLhxobXVmvzuEWBpRSiqiuyKlQnpstKt94Cy77iO8m8ISfF3C9VyLWXi9HUGAJb99irWABFl3sNDff5K2ODQ8CmuXLYM25OwN3ikbrhEJozlXg_NJFSGD4E\",\"d\":\"FkZHYZlw5KSoqQ1i2RA2kCUygSUOf1OqMt3uomtXuUmqKBm_bY7PCOhmwbvbn4xZYEeHuTR8Xix-0KpHe3NKyWrtRjkq1T_un49_1LLVUhJ0dL-9_x0xRquVjhl_XrsRXaGMEHs8G9pLTvXQ1uST585gxIfmCe0sxPZLvwoic-bXf64UZ9BGRV3lFexWJQqCZp2S21HfoU7wiz6kfLRNi-K4xiVNB1gswm_8o5lRuY7zB9bRARQ3TS2G4eW7p5sxT3CgsGiQD3_wPugU8iDplqAjgJ5ofNJXZezoj0t6JMB_qOpbrmAM1EnomIPebSLW7Ky9SugEd6KMdL5lW6AuAQ\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"qi\":\"wdkFu_tV2V1l_PWUUimG516Zvhqk2SWDw1F7uNDD-Lvrv_WNRIJVzuffZ8WYiPy8VvYQPJUrT2EXL8P0ocqwlaSTuXctrORcbjwgxDQDLsiZE0C23HYzgi0cofbScsJdhcBg7d07LAf7cdJWG0YVl1FkMCsxUlZ2wTwHfKWf-v4\",\"dp\":\"uwnPxqC-IxG4r33-SIT02kZC1IqC4aY7PWq0nePiDEQMQWpjjNH50rlq9EyLzbtdRdIouo-jyQXB01K15-XXJJ60dwrGLYNVqfsTd0eGqD1scYJGHUWG9IDgCsxyEnuG3s0AwbW2UolWVSsU2xMZGb9PurIUZECeD1XDZwMp2s0\",\"dq\":\"hra786AunB8TF35h8PpROzPoE9VJJMuLrc6Esm8eZXMwopf0yhxfN2FEAvUoTpLJu93-UH6DKenCgi16gnQ0_zt1qNNIVoRfg4rw_rjmsxCYHTVL3-RDeC8X_7TsEySxW0EgFTHh-nr6I6CQrAJjPM88T35KHtdFATZ7BCBB8AE\",\"n\":\"oXJ8OyOv_eRnce4akdanR4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGmuLvolxsDncc2UrhyMBY6DVQVgMSVYaPCTgW76iYEKGgzTEw5IBRQL9w3SRJWd3VJTZZQjkXef48Ocz06PGF3lhbz4t5UEZtdF4rIe7u-977QwHuh7yRPBQ3sII-cVoOUMgaXB9SHcGF2iZCtPzL_IffDUcfhLQteGebhW8A6eUHgpD5A1PQ-JCw_G7UOzZAjjDjtNM2eqm8j-Ms_gqnm4MiCZ4E-9pDN77CAAPVN7kuX6ejs9KBXpk01z48i9fORYk9u7rAkh1HuQw\"}]}";
|
||||
private static final String SIGNED_JWT = "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ0ZXN0LXN1YmplY3QiLCJzY3AiOlsibWVzc2FnZTpyZWFkIl0sImV4cCI6NDY4Mzg5Nzc3Nn0.LtMVtIiRIwSyc3aX35Zl0JVwLTcQZAB3dyBOMHNaHCKUljwMrf20a_gT79LfhjDzE_fUVUmFiAO32W1vFnYpZSVaMDUgeIOIOpxfoe9shj_uYenAwIS-_UxqGVIJiJoXNZh_MK80ShNpvsQwamxWEEOAMBtpWNiVYNDMdfgho9n3o5_Z7Gjy8RLBo1tbDREbO9kTFwGIxm_EYpezmRCRq4w1DdS6UDW321hkwMxPnCMSWOvp-hRpmgY2yjzLgPJ6Aucmg9TJ8jloAP1DjJoF1gRR7NTAk8LOGkSjTzVYDYMbCF51YdpojhItSk80YzXiEsv1mTz4oMM49jXBmfXFMA";
|
||||
private static final String JWK_SET = "{\"keys\":[{\"p\":\"5VhDhCcOp9D3krJi2W2uF-LPovIKqtU59Jdt_gF6iwPw_0Bgo8UlbFRv3HdnVYUExLdkvHOoA9bmbkV0w_6TUwLP65PKPu6P8JfeWsIuGi3wY91_CsfahxGmVZxlZookBUIMMnylvM9fGR6-daNgkl31CKqDpkJt9XF35yjVpbM\",\"kty\":\"RSA\",\"q\":\"v3yrgOhimojmLVsLBdynmw_pfAlZPw2eXVzoJ514xP94UZJQRY_NOUjYV0O9Vqict_Qv42sUa-uurY8n_0Btslt--iJsMyTHYMIKjbyeFAqAGFuXYbQPnorEOkuZhT1NIBZhlLLuKSD8DVCtsEv2EVgBTwyzFJ6QbXLqVpNUvZs\",\"d\":\"ZyjCopsw7TszSV4qMIyunb0PaGfHbQ_0LJcAxhNwQsf3MYR6j0J9k1GVxq2SjpRylgKJg8CKjySaU4frewH7MEaNLNdfR2_XMFSKW3KFggdNRtW1TFwjcHfpBLTvB3MEaTx56Sohn0eXqd_Wa2EAfRiLjllwOeqwXqgdSXdvKfkkmV2DBZ2h100wLJB87Y5kvlGvbNDs0KgTFaPWRZkCQz3CGhGPDyTJVwzgJvIxbHgzl9DnW6FlgrP_DZmyfGbJ833FZSiBczTQGDWT7euR3h491fKPCHTXjdULtU1578NldRAo8SOXH4ThXXA_kwKafIGlKx5LZPNwMWgNuVvE6Q\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"qi\":\"eaGTNLhJf1K82YqB6VKrYWz1hxsKnjRBg-V-kuWJXvW7HQsLFKx56kXy_ximz_IQDZOO3F-rW_7Saz3RvWuFt_Yq7sRcLCMtpiDRbZ-nGDgHxQHedtLoalLLPmJkMMsZwZzXf9l6LO6a8r30lrC_C-kPY5K7lz97ZToKeper7c8\",\"dp\":\"QQJ4-O_dTqKEWvfn3zwg2jJ3qvezIGOarwNxsUuYAenXGXOVMTcD-aYhozvRdcNj66MUkfqyyIvU-7MCe0AhYKluaJeW_6m98XQLGmzqho85EgXKKjMmdZ0CKkhP0fYcacUkEfeVP2UEzukREeWCzVqGx7MV6D3yT12foE3J6dM\",\"dq\":\"PsH2V5ZSEsHBZqYLE83ApMJvTHan6FFnUMQNVkZ2-WGdJmbkphe-NAMa3GbYHBnA201NkKRcmg4xPrLHchHEogr4r7QucAiiy6Rs3w0tZfYXC2ShVaU05Uoni8-RLijsKRMMwjZudc5YrWh-tGQA7qhALY9E9gIN5cEe6mb5A_c\",\"n\":\"q4yKxb6SNePdDmQi9xFCrP6QvHosErQzryknQTTTffs0t3cy3Er3lIceuhZ7yQNSCDfPFqG8GoyoKhuChRiA5D-J2ab7bqTa1QJKfnCyERoscftgN2fXPHjHoiKbpGV2tMVw8mXl__tePOAiKbMJaBUnlAvJgkk1rVm08dSwpLC1sr2M19euf9jwnRGkMRZuhp9iCPgECRke5T8Ixpv0uQjSmGHnWUKTFlbj8sM83suROR1Ue64JSGScANc5vk3huJ_J97qTC-K2oKj6L8d9O8dpc4obijEOJwpydNvTYDgbiivYeSB00KS9jlBkQ5B2QqLvLVEygDl3dp59nGx6YQ\"}]}";
|
||||
private static final String JWK_SET_URI = "http://issuer/.well-known/jwks.json";
|
||||
private static final String RS512_SIGNED_JWT = "eyJhbGciOiJSUzUxMiJ9.eyJzdWIiOiJ0ZXN0LXN1YmplY3QiLCJleHAiOjE5NzQzMjYxMTl9.LKAx-60EBfD7jC1jb1eKcjO4uLvf3ssISV-8tN-qp7gAjSvKvj4YA9-V2mIb6jcS1X_xGmNy6EIimZXpWaBR3nJmeu-jpe85u4WaW2Ztr8ecAi-dTO7ZozwdtljKuBKKvj4u1nF70zyCNl15AozSG0W1ASrjUuWrJtfyDG6WoZ8VfNMuhtU-xUYUFvscmeZKUYQcJ1KS-oV5tHeF8aNiwQoiPC_9KXCOZtNEJFdq6-uzFdHxvOP2yex5Gbmg5hXonauIFXG2ZPPGdXzm-5xkhBpgM8U7A_6wb3So8wBvLYYm2245QUump63AJRAy8tQpwt4n9MvQxQgS3z9R-NK92A";
|
||||
private static final String RS256_SIGNED_JWT = "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ0ZXN0LXN1YmplY3QiLCJleHAiOjE5NzQzMjYzMzl9.CT-H2OWEqmSs1NWmnta5ealLFvM8OlbQTjGhfRcKLNxrTrzsOkqBJl-AN3k16BQU7mS32o744TiiZ29NcDlxPsr1MqTlN86-dobPiuNIDLp3A1bOVdXMcVFuMYkrNv0yW0tGS9OjEqsCCuZDkZ1by6AhsHLbGwRY-6AQdcRouZygGpOQu1hNun5j8q5DpSTY4AXKARIFlF-O3OpVbPJ0ebr3Ki-i3U9p_55H0e4-wx2bqcApWlqgofl1I8NKWacbhZgn81iibup2W7E0CzCzh71u1Mcy3xk1sYePx-dwcxJnHmxJReBBWjJZEAeCrkbnn_OCuo2fA-EQyNJtlN5F2w";
|
||||
private static final String VERIFY_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq4yKxb6SNePdDmQi9xFCrP6QvHosErQzryknQTTTffs0t3cy3Er3lIceuhZ7yQNSCDfPFqG8GoyoKhuChRiA5D+J2ab7bqTa1QJKfnCyERoscftgN2fXPHjHoiKbpGV2tMVw8mXl//tePOAiKbMJaBUnlAvJgkk1rVm08dSwpLC1sr2M19euf9jwnRGkMRZuhp9iCPgECRke5T8Ixpv0uQjSmGHnWUKTFlbj8sM83suROR1Ue64JSGScANc5vk3huJ/J97qTC+K2oKj6L8d9O8dpc4obijEOJwpydNvTYDgbiivYeSB00KS9jlBkQ5B2QqLvLVEygDl3dp59nGx6YQIDAQAB";
|
||||
|
||||
private static KeyFactory kf;
|
||||
|
||||
@BeforeClass
|
||||
public static void keyFactory() throws NoSuchAlgorithmException {
|
||||
kf = KeyFactory.getInstance("RSA");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withJwkSetUriWhenNullOrEmptyThenThrowsException() {
|
||||
|
@ -68,12 +89,57 @@ public class JwtProcessorsTest {
|
|||
RestOperations restOperations = mockJwkSetResponse(JWK_SET);
|
||||
JWTProcessor<SecurityContext> processor =
|
||||
withJwkSetUri(JWK_SET_URI).restOperations(restOperations).build();
|
||||
assertThat(processor.process(SIGNED_JWT, null))
|
||||
assertThat(processor.process(RS256_SIGNED_JWT, null))
|
||||
.extracting(JWTClaimsSet::getExpirationTime)
|
||||
.isNotNull();
|
||||
verify(restOperations).exchange(any(RequestEntity.class), eq(String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withPublicKeyWhenNullThenThrowsException() {
|
||||
assertThatThrownBy(() -> JwtProcessors.withPublicKey(null))
|
||||
.isInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildWhenSignatureAlgorithmMismatchesKeyTypeThenThrowsException() {
|
||||
assertThatCode(() -> JwtProcessors.withPublicKey(key())
|
||||
.jwsAlgorithm(JwsAlgorithms.ES256)
|
||||
.build())
|
||||
.isInstanceOf(IllegalStateException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processWhenUsingPublicKeyThenSuccessfullyDecodes() throws Exception {
|
||||
JWTProcessor<SecurityContext> processor = JwtProcessors.withPublicKey(key()).build();
|
||||
assertThat(processor.process(RS256_SIGNED_JWT, null))
|
||||
.extracting(JWTClaimsSet::getSubject)
|
||||
.isEqualTo("test-subject");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processWhenUsingPublicKeyWithRs512ThenSuccessfullyDecodes() throws Exception {
|
||||
JWTProcessor<SecurityContext> processor = JwtProcessors
|
||||
.withPublicKey(key()).jwsAlgorithm(JwsAlgorithms.RS512).build();
|
||||
assertThat(processor.process(RS512_SIGNED_JWT, null))
|
||||
.extracting(JWTClaimsSet::getSubject)
|
||||
.isEqualTo("test-subject");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processWhenSignatureMismatchesAlgorithmThenThrowsException() throws Exception {
|
||||
JWTProcessor<SecurityContext> processor = JwtProcessors
|
||||
.withPublicKey(key()).jwsAlgorithm(JwsAlgorithms.RS512).build();
|
||||
assertThatCode(() -> processor.process(RS256_SIGNED_JWT, null))
|
||||
.isInstanceOf(BadJOSEException.class);
|
||||
}
|
||||
|
||||
private RSAPublicKey key() throws InvalidKeySpecException {
|
||||
byte[] decoded = Base64.getDecoder().decode(VERIFY_KEY.getBytes());
|
||||
EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
|
||||
return (RSAPublicKey) kf.generatePublic(spec);
|
||||
}
|
||||
|
||||
private static RestOperations mockJwkSetResponse(String response) {
|
||||
RestOperations restOperations = mock(RestOperations.class);
|
||||
when(restOperations.exchange(any(RequestEntity.class), eq(String.class)))
|
||||
|
|
Loading…
Reference in New Issue