From 8a6b744519cd2ebe603427f983e40686f263b6b8 Mon Sep 17 00:00:00 2001 From: jeffcheasey88 <66554203+jeffcheasey88@users.noreply.github.com> Date: Mon, 14 Oct 2024 16:10:45 +0200 Subject: [PATCH] =?UTF-8?q?Move=20JWT=20system=20out=20of=20router=20&=20a?= =?UTF-8?q?dd=20possibilit=C3=A9=20to=20custom=20internal=20Authenticator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dev/peerat/framework/Client.java | 10 +-- src/dev/peerat/framework/Router.java | 79 ++++-------------- .../peerat/framework/auth/AuthException.java | 17 ++++ .../peerat/framework/auth/Authenticator.java | 15 ++++ .../framework/auth/JwtAuthenticator.java | 81 +++++++++++++++++++ 5 files changed, 134 insertions(+), 68 deletions(-) create mode 100644 src/dev/peerat/framework/auth/AuthException.java create mode 100644 src/dev/peerat/framework/auth/Authenticator.java create mode 100644 src/dev/peerat/framework/auth/JwtAuthenticator.java diff --git a/src/dev/peerat/framework/Client.java b/src/dev/peerat/framework/Client.java index 26ece7e..c705c24 100644 --- a/src/dev/peerat/framework/Client.java +++ b/src/dev/peerat/framework/Client.java @@ -2,7 +2,7 @@ package dev.peerat.framework; import java.net.Socket; -import org.jose4j.jwt.consumer.InvalidJwtException; +import dev.peerat.framework.auth.AuthException; public class Client extends Thread{ @@ -28,7 +28,7 @@ public class Client extends Thread{ router.exec(context, reader, writer); writer.flush(); writer.close(); - }catch(InvalidJwtException e){ + }catch(AuthException e){ this.router.getExceptionLogger().setValue(e); }catch(Throwable e){ this.router.getExceptionLogger().setValue(e); @@ -45,13 +45,13 @@ public class Client extends Thread{ if(context != null) router.getLogger().setValue(context); } - private User isLogin(RequestType type, HttpReader reader) throws InvalidJwtException{ + private User isLogin(RequestType type, HttpReader reader) throws AuthException{ String auth = reader.getHeader("Authorization"); if(auth == null) return null; auth = auth.substring(7); try{ return this.router.getUser(auth); - }catch(InvalidJwtException e){ + }catch(Exception e){ try{ writer.response(401, this.router.getDefaultHeaders(type)); writer.flush(); @@ -59,7 +59,7 @@ public class Client extends Thread{ }catch(Exception ex){ this.router.getExceptionLogger().setValue(ex); } - throw e; + throw new AuthException(e); } } } \ No newline at end of file diff --git a/src/dev/peerat/framework/Router.java b/src/dev/peerat/framework/Router.java index 243c843..cda30a9 100644 --- a/src/dev/peerat/framework/Router.java +++ b/src/dev/peerat/framework/Router.java @@ -7,27 +7,13 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.ServerSocket; import java.net.Socket; -import java.security.Key; import java.util.ArrayList; import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; -import org.jose4j.jwa.AlgorithmConstraints.ConstraintType; -import org.jose4j.jwk.JsonWebKey.OutputControlLevel; -import org.jose4j.jwk.RsaJsonWebKey; -import org.jose4j.jwk.RsaJwkGenerator; -import org.jose4j.jws.AlgorithmIdentifiers; -import org.jose4j.jws.JsonWebSignature; -import org.jose4j.jwt.JwtClaims; -import org.jose4j.jwt.consumer.InvalidJwtException; -import org.jose4j.jwt.consumer.JwtConsumer; -import org.jose4j.jwt.consumer.JwtConsumerBuilder; -import org.jose4j.lang.JoseException; +import dev.peerat.framework.auth.Authenticator; public class Router{ @@ -40,10 +26,7 @@ public class Router{ private RouteMapper[] mappers; private List interceptors; private Response noFileFound; - private RsaJsonWebKey rsaJsonWebKey; - private JwtConsumer jwtConsumer; - private Consumer claims; - private Function userCreator; + private Authenticator auth; private String[][] headers; private ServerSocket serverSocket; @@ -57,36 +40,11 @@ public class Router{ this.headers = new String[types][0]; } - public Router configureJwt(Consumer consumer, Consumer claims, Function userCreator) throws Exception{ - this.rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048); - configureJwtWithKey(consumer, claims, userCreator, this.rsaJsonWebKey.getKey()); + public Router setAuthenticator(Authenticator auth){ + this.auth = auth; return this; } - public Router configureJwt(Consumer consumer, Consumer claims, Function userCreator, Map keyParams) throws Exception{ - this.rsaJsonWebKey = new RsaJsonWebKey(keyParams); - configureJwtWithKey(consumer, claims, userCreator, this.rsaJsonWebKey.getKey()); - return this; - } - - private void configureJwtWithKey(Consumer consumer, Consumer claims, Function userCreator, Key key) throws Exception{ - JwtConsumerBuilder builder = new JwtConsumerBuilder() - .setRequireExpirationTime() - .setAllowedClockSkewInSeconds(30) - .setVerificationKey(key) - .setJwsAlgorithmConstraints(ConstraintType.PERMIT, AlgorithmIdentifiers.RSA_USING_SHA256); - - consumer.accept(builder); - - this.jwtConsumer = builder.build(); - this.claims = claims; - this.userCreator = userCreator; - } - - public Map exportJwtKey(){ - return this.rsaJsonWebKey.toParams(OutputControlLevel.INCLUDE_PRIVATE); - } - public Router addDefaultHeaders(RequestType type, String... headers){ String[] origin = this.headers[type.ordinal()]; String[] copy = new String[origin.length+headers.length]; @@ -187,6 +145,10 @@ public class Router{ String[] getDefaultHeaders(RequestType type){ return this.headers[type.ordinal()]; } + + public Authenticator getAuthenticator(){ + return this.auth; + } void exec(Context context, HttpReader reader, HttpWriter writer) throws Exception{ if(this.mappers[context.getType().ordinal()].exec(context, reader, writer, this.interceptors)) return; @@ -199,25 +161,16 @@ public class Router{ return this; } - public U getUser(String token) throws InvalidJwtException{ - return this.userCreator.apply(this.jwtConsumer.processToClaims(token)); + public U getUser(String token) throws Exception{ + return this.auth != null ? this.auth.getUser(token) : null; } - public String createAuthUser(U user) throws JoseException{ - JwtClaims claims = new JwtClaims(); - claims.setGeneratedJwtId(); // a unique identifier for the token - claims.setIssuedAtToNow(); // when the token was issued/created (now) - claims.setNotBeforeMinutesInThePast(2); // time before which the token is not yet valid (2 minutes ago) - this.claims.accept(claims); - - user.write(claims); - - JsonWebSignature jws = new JsonWebSignature(); - jws.setPayload(claims.toJson()); - jws.setKey(rsaJsonWebKey.getPrivateKey()); - jws.setKeyIdHeaderValue(rsaJsonWebKey.getKeyId()); - jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256); - return jws.getCompactSerialization(); + public String createAuthUser(U user) throws Exception{ + return this.auth.createAuthUser(user); + } + + public User isLogin(RequestType type, HttpReader reader) throws Exception{ + return this.auth.isLogin(type, reader); } public Locker getLogger(){ diff --git a/src/dev/peerat/framework/auth/AuthException.java b/src/dev/peerat/framework/auth/AuthException.java new file mode 100644 index 0000000..ddacb40 --- /dev/null +++ b/src/dev/peerat/framework/auth/AuthException.java @@ -0,0 +1,17 @@ +package dev.peerat.framework.auth; + +public class AuthException extends Exception{ + + public AuthException(String message){ + super(message); + } + + public AuthException(Throwable throwable){ + super(throwable); + } + + public AuthException(String message, Throwable throwable){ + super(message, throwable); + } + +} diff --git a/src/dev/peerat/framework/auth/Authenticator.java b/src/dev/peerat/framework/auth/Authenticator.java new file mode 100644 index 0000000..8709de0 --- /dev/null +++ b/src/dev/peerat/framework/auth/Authenticator.java @@ -0,0 +1,15 @@ +package dev.peerat.framework.auth; + +import dev.peerat.framework.HttpReader; +import dev.peerat.framework.RequestType; +import dev.peerat.framework.User; + +public interface Authenticator{ + + U getUser(String token) throws Exception; + + String createAuthUser(U user) throws Exception; + + User isLogin(RequestType type, HttpReader reader) throws Exception; + +} diff --git a/src/dev/peerat/framework/auth/JwtAuthenticator.java b/src/dev/peerat/framework/auth/JwtAuthenticator.java new file mode 100644 index 0000000..c2a2211 --- /dev/null +++ b/src/dev/peerat/framework/auth/JwtAuthenticator.java @@ -0,0 +1,81 @@ +package dev.peerat.framework.auth; + +import java.security.Key; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; + +import org.jose4j.jwa.AlgorithmConstraints.ConstraintType; +import org.jose4j.jwk.RsaJsonWebKey; +import org.jose4j.jwk.RsaJwkGenerator; +import org.jose4j.jwk.JsonWebKey.OutputControlLevel; +import org.jose4j.jws.AlgorithmIdentifiers; +import org.jose4j.jws.JsonWebSignature; +import org.jose4j.jwt.JwtClaims; +import org.jose4j.jwt.consumer.JwtConsumer; +import org.jose4j.jwt.consumer.JwtConsumerBuilder; + +import dev.peerat.framework.User; + +public class JwtAuthenticator{ + + private RsaJsonWebKey rsaJsonWebKey; + private JwtConsumer jwtConsumer; + private Consumer claims; + private Function userCreator; + + public JwtAuthenticator(){} + + public JwtAuthenticator configure(Consumer consumer, Consumer claims, Function userCreator) throws Exception{ + this.rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048); + configureWithKey(consumer, claims, userCreator, this.rsaJsonWebKey.getKey()); + return this; + } + + public JwtAuthenticator configure(Consumer consumer, Consumer claims, Function userCreator, Map keyParams) throws Exception{ + this.rsaJsonWebKey = new RsaJsonWebKey(keyParams); + configureWithKey(consumer, claims, userCreator, this.rsaJsonWebKey.getKey()); + return this; + } + + private JwtAuthenticator configureWithKey(Consumer consumer, Consumer claims, Function userCreator, Key key) throws Exception{ + JwtConsumerBuilder builder = new JwtConsumerBuilder() + .setRequireExpirationTime() + .setAllowedClockSkewInSeconds(30) + .setVerificationKey(key) + .setJwsAlgorithmConstraints(ConstraintType.PERMIT, AlgorithmIdentifiers.RSA_USING_SHA256); + + consumer.accept(builder); + + this.jwtConsumer = builder.build(); + this.claims = claims; + this.userCreator = userCreator; + + return this; + } + + public Map exportKey(){ + return this.rsaJsonWebKey.toParams(OutputControlLevel.INCLUDE_PRIVATE); + } + + public U getUser(String token) throws Exception{ + return this.userCreator.apply(this.jwtConsumer.processToClaims(token)); + } + + public String createAuthUser(U user) throws Exception{ + JwtClaims claims = new JwtClaims(); + claims.setGeneratedJwtId(); // a unique identifier for the token + claims.setIssuedAtToNow(); // when the token was issued/created (now) + claims.setNotBeforeMinutesInThePast(2); // time before which the token is not yet valid (2 minutes ago) + this.claims.accept(claims); + + user.write(claims); + + JsonWebSignature jws = new JsonWebSignature(); + jws.setPayload(claims.toJson()); + jws.setKey(rsaJsonWebKey.getPrivateKey()); + jws.setKeyIdHeaderValue(rsaJsonWebKey.getKeyId()); + jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256); + return jws.getCompactSerialization(); + } +}