diff --git a/pom.xml b/pom.xml index fde6856..d27284f 100644 --- a/pom.xml +++ b/pom.xml @@ -28,18 +28,71 @@ GitHub Actions https://github.com/ilyalisov/jwt/actions - scm:git:git://github.com/ilyalisov/jwt.git scm:git:ssh://github.com:ilyalisov/jwt.git https://github.com/ilyalisov/jwt/tree/main - 17 17 17 UTF-8 + 0.12.2 + 1.18.30 + 3.2.2 - + + + org.projectlombok + lombok + true + ${lombok.version} + + + io.jsonwebtoken + jjwt-api + ${jjwt.version} + + + io.jsonwebtoken + jjwt-impl + ${jjwt.version} + runtime + + + io.jsonwebtoken + jjwt-jackson + ${jjwt.version} + runtime + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${checkstyle.version} + + sun_checks.xml + true + true + false + + + + validate + validate + + check + + + suppressions.xml + suppressions.xml + + + + + + diff --git a/src/main/java/TokenParameters.java b/src/main/java/TokenParameters.java new file mode 100644 index 0000000..5934e55 --- /dev/null +++ b/src/main/java/TokenParameters.java @@ -0,0 +1,145 @@ +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@Builder( + builderMethodName = "hiddenBuilder", + access = AccessLevel.PRIVATE +) +@Getter +public class TokenParameters { + + /** + * A map of claims to be put in JWT token. + */ + private Map claims; + + /** + * The "sub" of JWT token. + */ + private String subject; + + /** + * Date when JWT token was issued. + */ + private Date issuedAt; + + /** + * Date when JWT token will be expired. + */ + private Date expiredAt; + + /** + * Creates a builder for TokenParameters. + * + * @param subject sub of JWT token + * @param duration duration between token issuing and expiration date + * @return TokenParametersBuilder + */ + public static TokenParametersBuilder builder( + final String subject, + final Duration duration + ) { + Date issuedAt = new Date(); + return hiddenBuilder() + .claims(new HashMap<>()) + .issuedAt(issuedAt) + .subject(subject) + .expiredAt(new Date( + issuedAt.getTime() + + 1000 * duration.get(ChronoUnit.SECONDS) + )); + } + + public static class TokenParametersBuilder { + + /** + * Add claims to parameters. + * + * @param key the key of claim + * @param value the value of claim + * @return TokenParametersBuilder + */ + public TokenParametersBuilder claim( + final String key, + final Object value + ) { + this.claims.put(key, value); + return this; + } + + /** + * Adds claims to parameters. + * + * @param claims a map of claims + * @return TokenParametersBuilder + */ + public TokenParametersBuilder claims( + final Map claims + ) { + this.claims.putAll(claims); + return this; + } + + /** + * Sets issued date for JWT token. + * + * @param issuedAt date of issuing + * @return TokenParametersBuilder + */ + public TokenParametersBuilder issuedAt( + final Date issuedAt + ) { + this.issuedAt = issuedAt; + return this; + } + + /** + * Sets expiration date for JWT token. + * + * @param expiredAt date of expiration + * @return TokenParametersBuilder + */ + public TokenParametersBuilder expiredAt( + final Date expiredAt + ) { + this.expiredAt = expiredAt; + return this; + } + + /** + * Sets subject to parameters. + * + * @param subject subject of JWT token + * @return TokenParametersBuilder + */ + public TokenParametersBuilder subject( + final String subject + ) { + this.subject = subject; + return this; + } + + /** + * Builds final object. + * + * @return TokenParameters object + */ + public TokenParameters build() { + return new TokenParameters( + claims, + subject, + issuedAt, + expiredAt + ); + } + + } + +} diff --git a/src/main/java/TokenService.java b/src/main/java/TokenService.java new file mode 100644 index 0000000..395d7c3 --- /dev/null +++ b/src/main/java/TokenService.java @@ -0,0 +1,60 @@ +import java.util.Map; + +public interface TokenService { + + /** + * Creates JWT token by provided parameters. + * + * @param params parameters for JWT token + * @return JWT token + */ + String create( + TokenParameters params + ); + + /** + * Checks whether JWT token is expired by current time. + * + * @param token JWT token to be checked + * @return true - if JWT token expired, false - otherwise + */ + boolean isExpired( + String token + ); + + /** + * Checks whether JWT token has a key-value pair in payload. + * + * @param token JWT token + * @param key key of payload + * @param value value of payload + * @return true - if JWT token has a provided key-value pair in payload, + * false - otherwise + */ + boolean has( + String token, + String key, + Object value + ); + + /** + * Returns "sub" of JWT token. + * + * @param token JWT token + * @return "sub" of JWT token + */ + String getSubject( + String token + ); + + /** + * Returns payload of JWT token as a Map. + * + * @param token JWT token + * @return a map of key-value pairs from payload + */ + Map claims( + String token + ); + +} diff --git a/src/main/java/TokenServiceImpl.java b/src/main/java/TokenServiceImpl.java new file mode 100644 index 0000000..766709e --- /dev/null +++ b/src/main/java/TokenServiceImpl.java @@ -0,0 +1,97 @@ +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; + +import javax.crypto.SecretKey; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +public class TokenServiceImpl implements TokenService { + + private final SecretKey key; + + /** + * Creates TokenServiceImpl object. + * + * @param secret secret of key for JWT token generation + */ + public TokenServiceImpl( + final String secret + ) { + this.key = Keys.hmacShaKeyFor(secret.getBytes()); + } + + @Override + public String create( + final TokenParameters params + ) { + Claims claims = Jwts.claims() + .subject(params.getSubject()) + .add(params.getClaims()) + .build(); + return Jwts.builder() + .claims(claims) + .issuedAt(params.getIssuedAt()) + .expiration(params.getExpiredAt()) + .signWith(key) + .compact(); + } + + @Override + public boolean isExpired( + final String token + ) { + Jws claims = Jwts + .parser() + .verifyWith(key) + .build() + .parseSignedClaims(token); + return claims.getPayload() + .getExpiration() + .before(new Date()); + } + + @Override + public boolean has( + final String token, + final String key, + final Object value + ) { + Jws claims = Jwts + .parser() + .verifyWith(this.key) + .build() + .parseSignedClaims(token); + return claims.getPayload() + .get(key) + .equals(value); + } + + @Override + public String getSubject( + final String token + ) { + return Jwts + .parser() + .verifyWith(key) + .build() + .parseSignedClaims(token) + .getPayload() + .getSubject(); + } + + @Override + public Map claims( + final String token + ) { + Jws claims = Jwts + .parser() + .verifyWith(key) + .build() + .parseSignedClaims(token); + return new HashMap<>(claims.getPayload()); + } + +} diff --git a/suppressions.xml b/suppressions.xml new file mode 100644 index 0000000..53fa695 --- /dev/null +++ b/suppressions.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file