
fi.evolver.basics.spring.auth.JwtAuthorization Maven / Gradle / Ivy
package fi.evolver.basics.spring.auth;
import java.time.Instant;
import java.util.Date;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import fi.evolver.basics.spring.auth.entity.JwtToken;
import fi.evolver.utils.collection.BoundedLinkedHashMap;
import fi.evolver.utils.timing.TimingUtils;
@Component
@ConditionalOnProperty(JwtAuthorization.ENV_AUTHORIZATION_SECRET)
public class JwtAuthorization {
private static final Logger LOG = LoggerFactory.getLogger(JwtAuthorization.class);
public static final String ENV_AUTHORIZATION_SECRET = "AUTHORIZATION_SECRET";
private static final Pattern HEADER_REGEX = Pattern.compile("^Bearer (?.*)$");
private static final Map cache = new BoundedLinkedHashMap<>(50, BoundedLinkedHashMap.Order.ACCESS);
private final Algorithm jwtAlgorithm;
private final JWTVerifier jwtVerifier;
private final JwtTokenRepository jwtTokenRepository;
private final String applicationName;
@Autowired
public JwtAuthorization(JwtTokenRepository jwtTokenRepository, Environment environment) {
this.jwtTokenRepository = jwtTokenRepository;
this.applicationName = environment.getRequiredProperty("application.name.pretty").replaceAll("\\W", "");
this.jwtAlgorithm = Algorithm.HMAC256(getAuthorizationSecret());
this.jwtVerifier = JWT.require(jwtAlgorithm)
.withIssuer(applicationName)
.build();
LOG.warn("{}", getAuthorizationSecret());
}
public JwtToken authorizeToken(String authorization) {
CachedToken cached = cache.get(authorization);
if (cached != null && cached.isValid())
return cached.getToken();
Optional jwtToken = parseAuthorization(authorization);
String jwtId = JwtToken.DEFAULT_JWT_ID;
if (jwtToken.isPresent()) {
try {
jwtId = verifyJwtToken(jwtToken.get()).getId();
}
catch (RuntimeException e) {
LOG.warn("Invalid JWT token", e);
return JwtToken.INVALID;
}
}
JwtToken result = fetchToken(jwtId);
cache.put(authorization, new CachedToken(result));
return result;
}
private JwtToken fetchToken(String jwtId) {
return jwtTokenRepository.findByJwtId(jwtId)
.stream()
.findFirst()
.orElse(JwtToken.UNKNOWN);
}
private static Optional parseAuthorization(String authorization) {
if (authorization == null)
return Optional.empty();
Matcher matcher = HEADER_REGEX.matcher(authorization);
if (!matcher.find())
throw new IllegalArgumentException("Unsupported authorization");
return Optional.of(matcher.group("data"));
}
public String generateJwtToken(String jwtId) {
try (TimingUtils.AutoCloser ignored = TimingUtils.begin("JWT", "Create")) {
return JWT.create()
.withIssuer(applicationName)
.withIssuedAt(new Date())
.withJWTId(jwtId)
.sign(jwtAlgorithm);
}
}
private DecodedJWT verifyJwtToken(String token) {
try (TimingUtils.AutoCloser ignored = TimingUtils.begin("JWT", "Verify")) {
return jwtVerifier.verify(token);
}
}
public static String getAuthorizationSecret() {
return System.getenv(ENV_AUTHORIZATION_SECRET);
}
private static class CachedToken {
private final Instant validUntil = Instant.now().plusSeconds(60);
private final JwtToken token;
public CachedToken(JwtToken token) {
this.token = token;
}
public boolean isValid() {
return Instant.now().isBefore(validUntil);
}
public JwtToken getToken() {
return token;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy