Skip to content

Commit

Permalink
remove unneccessary guava dependency
Browse files Browse the repository at this point in the history
  • Loading branch information
wuan authored and [email protected] committed May 27, 2020
1 parent 01b557f commit 438aadf
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 93 deletions.
5 changes: 0 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -364,11 +364,6 @@
<artifactId>slf4j-api</artifactId>
<version>${slf4j-api.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.1-jre</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ default int getTokenLeeway() {

/**
* @return {@link JWTVerifier} for given {@link JWTKeyset} to be used for token
* verification
* verification
*/
@Value.Derived
default Option<JWTVerifier> jwtVerifier() {
return jwtKeyset().map(jwks -> new JWTVerifierFactory(jwks, this)).map(JWTVerifierFactory::create);
return jwtKeyset() .map(jwks -> new JWTVerifierFactory(jwks, this)).map(JWTVerifierFactory::create);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright © 2017 Mercateo AG (http://www.mercateo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mercateo.spring.security.jwt.token.verifier;

import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.interfaces.RSAKeyProvider;

class AlgorithmFactory {
private final RSAKeyProvider rsaKeyProvider;

public AlgorithmFactory(RSAKeyProvider rsaKeyProvider) {
this.rsaKeyProvider = rsaKeyProvider;
}

public Algorithm createByName(String algorithmName) throws AlgorithmMismatchException {
switch (algorithmName.toLowerCase()) {
case "rs256":
return Algorithm.RSA256(rsaKeyProvider);
case "rs384":
return Algorithm.RSA384(rsaKeyProvider);
case "rs512":
return Algorithm.RSA512(rsaKeyProvider);
default:
throw new AlgorithmMismatchException(
"The provided Algorithm has to be RSA.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import com.auth0.jwt.interfaces.Clock;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.RSAKeyProvider;
import com.google.common.annotations.VisibleForTesting;

import lombok.val;

Expand All @@ -47,11 +46,11 @@
@SuppressWarnings("WeakerAccess")
public final class JWTVerifier {
private final Map<String, Object> claims;
private final RSAKeyProvider rsaKeyProvider;
private final AlgorithmFactory algorithmFactory;
private final Clock clock;

JWTVerifier(RSAKeyProvider rsaKeyProvider, Map<String, Object> claims, Clock clock) {
this.rsaKeyProvider = rsaKeyProvider;
JWTVerifier(AlgorithmFactory algorithmFactory, Map<String, Object> claims, Clock clock) {
this.algorithmFactory = algorithmFactory;
this.claims = Collections.unmodifiableMap(claims);
this.clock = clock;
}
Expand All @@ -64,7 +63,7 @@ public final class JWTVerifier {
* @throws IllegalArgumentException if the provided algorithm is null.
*/
public static BaseVerification init(RSAKeyProvider rsaKeyProvider) throws IllegalArgumentException {
return new BaseVerification(rsaKeyProvider);
return new BaseVerification(new AlgorithmFactory(rsaKeyProvider));
}

/**
Expand All @@ -86,25 +85,11 @@ public static BaseVerification init(RSAKeyProvider rsaKeyProvider) throws Illega
*/
public DecodedJWT verify(String token) throws JWTVerificationException {
DecodedJWT jwt = JWT.decode(token);
Algorithm algorithm = getAlgorithm(jwt);
Algorithm algorithm = algorithmFactory.createByName(jwt.getAlgorithm());
algorithm.verify(jwt);
verifyClaims(jwt, claims);
return jwt;
}
@VisibleForTesting
Algorithm getAlgorithm(DecodedJWT jwt) throws AlgorithmMismatchException {
switch (jwt.getAlgorithm().toLowerCase()) {
case "rs256":
return Algorithm.RSA256(rsaKeyProvider);
case "rs384":
return Algorithm.RSA384(rsaKeyProvider);
case "rs512":
return Algorithm.RSA512(rsaKeyProvider);
default:
throw new AlgorithmMismatchException(
"The provided Algorithm has to be RSA.");
}
}

private void verifyClaims(DecodedJWT jwt, Map<String, Object> claims) throws TokenExpiredException,
InvalidClaimException {
Expand Down Expand Up @@ -199,18 +184,18 @@ public Date getToday() {
* The Verification class holds the Claims required by a JWT to be valid.
*/
public static class BaseVerification {
private final RSAKeyProvider rsaKeyProvider;
private final AlgorithmFactory algorithmFactory;

private final Map<String, Object> claims;

private long defaultLeeway;

BaseVerification(RSAKeyProvider rsaKeyProvider) throws IllegalArgumentException {
if (rsaKeyProvider == null) {
BaseVerification(AlgorithmFactory algorithmFactory) throws IllegalArgumentException {
if (algorithmFactory == null) {
throw new IllegalArgumentException("The rsaKeyprovider cannot be null.");
}

this.rsaKeyProvider = rsaKeyProvider;
this.algorithmFactory = algorithmFactory;
this.claims = new HashMap<>();
this.defaultLeeway = 0;
}
Expand Down Expand Up @@ -283,7 +268,7 @@ public JWTVerifier build() {
*/
public JWTVerifier build(Clock clock) {
addLeewayToDateClaims();
return new JWTVerifier(rsaKeyProvider, claims, clock);
return new JWTVerifier(algorithmFactory, claims, clock);
}

private void assertPositive(long leeway) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,60 +15,24 @@
*/
package com.mercateo.spring.security.jwt.token.verifier;

import java.security.Key;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;

import com.auth0.jwk.Jwk;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.RSAKeyProvider;
import com.mercateo.spring.security.jwt.token.config.JWTConfig;
import com.mercateo.spring.security.jwt.token.keyset.JWTKeyset;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;

@AllArgsConstructor
@Slf4j
public class JWTVerifierFactory {
private final JWTKeyset jwtKeyset;
private final RSAKeyProviderFactory keyProviderFactory;

private final JWTConfig jwtConfig;

private static IllegalStateException map(Throwable cause) {
return new IllegalStateException(cause);
public JWTVerifierFactory(JWTKeyset jwtKeyset, JWTConfig jwtConfig) {
this.keyProviderFactory = new RSAKeyProviderFactory(jwtKeyset);
this.jwtConfig = jwtConfig;
}

public JWTVerifier create() {
final RSAKeyProvider rsaKeyProvider = new RSAKeyProvider() {
@Override
public RSAPublicKey getPublicKeyById(String keyId) {
return jwtKeyset
.getKeysetForId(keyId)
.mapTry(Jwk::getPublicKey)
.map(Key::getEncoded)
.mapTry(JWTVerifierFactory::createKey)
.onFailure(e -> log.error("Error getting public key for id " + keyId, e))
.getOrElseThrow(JWTVerifierFactory::map);
}

@Override
public RSAPrivateKey getPrivateKey() {
return null;
}

@Override
public String getPrivateKeyId() {
return null;
}
};

val verification = JWTVerifier.init(rsaKeyProvider);
val verification = JWTVerifier.init(keyProviderFactory.create());

final int tokenLeeway = jwtConfig.getTokenLeeway();
verification.acceptLeeway(tokenLeeway);
Expand All @@ -80,9 +44,4 @@ public String getPrivateKeyId() {

return verification.build();
}

private static RSAPublicKey createKey(byte[] bytes ) throws NoSuchAlgorithmException, InvalidKeySpecException {
return (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(
new X509EncodedKeySpec(bytes));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright © 2020 Mercateo AG (http://www.mercateo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mercateo.spring.security.jwt.token.verifier;

import java.security.Key;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;

import com.auth0.jwk.Jwk;
import com.auth0.jwt.interfaces.RSAKeyProvider;
import com.mercateo.spring.security.jwt.token.keyset.JWTKeyset;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@AllArgsConstructor
class RSAKeyProviderFactory {

private final JWTKeyset jwtKeyset;

private static IllegalStateException map(Throwable cause) {
return new IllegalStateException(cause);
}

private static RSAPublicKey createKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
return (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(
new X509EncodedKeySpec(bytes));
}

public RSAKeyProvider create() {
return new RSAKeyProvider() {
@Override
public RSAPublicKey getPublicKeyById(String keyId) {
return jwtKeyset
.getKeysetForId(keyId)
.mapTry(Jwk::getPublicKey)
.map(Key::getEncoded)
.mapTry(RSAKeyProviderFactory::createKey)
.onFailure(e -> log.error("Error getting public key for id " + keyId, e))
.getOrElseThrow(RSAKeyProviderFactory::map);
}

@Override
public RSAPrivateKey getPrivateKey() {
return null;
}

@Override
public String getPrivateKeyId() {
return null;
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright © 2017 Mercateo AG (http://www.mercateo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mercateo.spring.security.jwt.token.verifier;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.junit.Before;
import org.junit.Test;

import com.auth0.jwk.Jwk;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.mercateo.spring.security.jwt.JWKProvider;
import com.mercateo.spring.security.jwt.token.keyset.JWTKeyset;

import io.vavr.control.Try;

public class AlgorithmFactoryTest {

private AlgorithmFactory uut;

@Before
public void setUp() {
final JWKProvider jwkProvider = new JWKProvider();
String keyId = "4711";
final Jwk jwk = jwkProvider.create(keyId);
JWTKeyset jwks = mock(JWTKeyset.class);
when(jwks.getKeysetForId(keyId)).thenReturn(Try.success(jwk));
assertThat(jwks.getKeysetForId(keyId)).isNotNull();
RSAKeyProviderFactory keyProviderFactory = new RSAKeyProviderFactory(jwks);
uut = new AlgorithmFactory(keyProviderFactory.create());
}

@Test
public void createsRSA256Algorithm() {
Algorithm algo1 = uut.createByName("RS256");
assertEquals("RS256", algo1.getName());
}

@Test
public void createRSA384Algorithm() {
Algorithm algo2 = uut.createByName("RS384");
assertEquals("RS384", algo2.getName());
}

@Test
public void createRSA512Algorithm() {
Algorithm algo3 = uut.createByName("RS512");
assertEquals("RS512", algo3.getName());
}

@Test
public void failsCreatingUnknownAlgorithm() {
assertThatThrownBy(() -> uut.createByName("foo")).isInstanceOf(AlgorithmMismatchException.class);
}
}
Loading

0 comments on commit 438aadf

Please sign in to comment.