diff --git a/pom.xml b/pom.xml index 0eb1b58..1104515 100644 --- a/pom.xml +++ b/pom.xml @@ -62,6 +62,12 @@ 5.8.1 test + + com.auth0 + java-jwt + 3.18.2 + true + diff --git a/src/main/java/org/javawebstack/webutils/security/AbstractAuthSystem.java b/src/main/java/org/javawebstack/webutils/security/AbstractAuthSystem.java new file mode 100644 index 0000000..f8bd09c --- /dev/null +++ b/src/main/java/org/javawebstack/webutils/security/AbstractAuthSystem.java @@ -0,0 +1,90 @@ +package org.javawebstack.webutils.security; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.interfaces.DecodedJWT; +import org.javawebstack.abstractdata.AbstractObject; +import org.javawebstack.httpserver.Exchange; + +import java.sql.Date; +import java.time.Instant; +import java.util.Optional; + +public abstract class AbstractAuthSystem { + private Algorithm algorithm; + private int tokenExpire = 3600; + + public AbstractAuthSystem(String secret) { + this(Algorithm.HMAC256(secret)); + } + + public AbstractAuthSystem(Algorithm algorithm) { + this.algorithm = algorithm; + } + + protected abstract Optional getUserByUsername(String username); + protected AbstractObject successMessage(String token) { + return new AbstractObject() + .set("success", true) + .set("token", token); + } + + public void setTokenExpire(int tokenExpire) { + this.tokenExpire = tokenExpire; + } + + public int getTokenExpire() { + return tokenExpire; + } + + public AbstractObject loginHandler(Exchange exchange) { + AbstractObject body = exchange.body(AbstractObject.class); + Optional oUser = getUserByUsername(body.string("username")); + if (!oUser.isPresent()) + throw new AuthException("Wrong credentials"); + IUser user = oUser.get(); + if (!user.checkPassword(body.string("password"))) + throw new AuthException("Wrong credentials"); + return successMessage(signToken(user)); + } + + public boolean tokenHandler (Exchange exchange) { + String bearer = exchange.bearerAuth(); + if (bearer != null) { + DecodedJWT jwt = JWT.require(algorithm).acceptExpiresAt(System.currentTimeMillis()).build().verify(bearer); + if (jwt != null) + exchange.attrib("jwt", jwt); + } + return false; + } + + public Object authMiddleware (Exchange exchange) { + if (exchange.attrib("jwt") == null) { + exchange.status(401); + throw new AuthException("Authentication required"); + } + return null; + } + + public IUser currentUser () { + Exchange exchange = Exchange.current(); + if (exchange == null) + throw new RuntimeException("Exchange not available in current Thread!"); + IUser user = exchange.attrib("user"); + if (user == null) { + DecodedJWT jwt = exchange.attrib("jwt"); + if (jwt == null) + throw new RuntimeException("JWT not found! Is the tokenHandler in place?"); + user = getUserByUsername(jwt.getSubject()).get(); + exchange.attrib("user", user); + } + return user; + } + + protected String signToken(IUser user) { + return JWT.create() + .withSubject(user.getUsername()) + .withExpiresAt(Date.from(Instant.now().plusSeconds(tokenExpire))) + .sign(algorithm); + } +} diff --git a/src/main/java/org/javawebstack/webutils/security/AuthException.java b/src/main/java/org/javawebstack/webutils/security/AuthException.java new file mode 100644 index 0000000..6758b09 --- /dev/null +++ b/src/main/java/org/javawebstack/webutils/security/AuthException.java @@ -0,0 +1,7 @@ +package org.javawebstack.webutils.security; + +public class AuthException extends RuntimeException { + public AuthException(String message) { + super(message); + } +} diff --git a/src/main/java/org/javawebstack/webutils/security/IUser.java b/src/main/java/org/javawebstack/webutils/security/IUser.java new file mode 100644 index 0000000..1452393 --- /dev/null +++ b/src/main/java/org/javawebstack/webutils/security/IUser.java @@ -0,0 +1,14 @@ +package org.javawebstack.webutils.security; + +import org.javawebstack.httpserver.Exchange; +import org.javawebstack.webutils.crypt.BCrypt; + +public interface IUser { + String getUsername(); + + String getPassword(); + + default boolean checkPassword (String password) { + return BCrypt.check(getPassword(), password); + } +} diff --git a/src/main/java/org/javawebstack/webutils/security/SimpleAuthSystem.java b/src/main/java/org/javawebstack/webutils/security/SimpleAuthSystem.java new file mode 100644 index 0000000..44f1650 --- /dev/null +++ b/src/main/java/org/javawebstack/webutils/security/SimpleAuthSystem.java @@ -0,0 +1,22 @@ +package org.javawebstack.webutils.security; + +import org.javawebstack.orm.ORM; +import org.javawebstack.orm.exception.ORMConfigurationException; +import org.javawebstack.orm.wrapper.SQL; + +import java.util.Optional; + +public class SimpleAuthSystem extends AbstractAuthSystem { + public SimpleAuthSystem(String secret, SQL sql) { + super(secret); + try { + ORM.register(SimpleUser.class, sql); + } catch (ORMConfigurationException e) { + e.printStackTrace(); + } + } + + protected Optional getUserByUsername(String username) { + return Optional.ofNullable(ORM.repo(SimpleUser.class).where("username", username).first()); + } +} diff --git a/src/main/java/org/javawebstack/webutils/security/SimpleUser.java b/src/main/java/org/javawebstack/webutils/security/SimpleUser.java new file mode 100644 index 0000000..70f9f6b --- /dev/null +++ b/src/main/java/org/javawebstack/webutils/security/SimpleUser.java @@ -0,0 +1,51 @@ +package org.javawebstack.webutils.security; + +import com.auth0.jwt.algorithms.Algorithm; +import org.javawebstack.orm.Model; +import org.javawebstack.orm.annotation.Column; +import org.javawebstack.orm.util.KeyType; + + +public class SimpleUser extends Model implements IUser { + + @Column(id = true, ai = true, key = KeyType.PRIMARY) + private long id; + @Column + private String username; + @Column + private String password; + + public SimpleUser() {} + + public SimpleUser(long id, String username, String password) { + this.id = id; + this.username = username; + this.password = password; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + @Override + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + @Override + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +}