package com.izouma.awesomeAdmin.security; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Clock; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.impl.DefaultClock; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Component; import java.io.Serializable; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.function.Function; @Component public class JwtTokenUtil implements Serializable { private static final long serialVersionUID = -3301605591108950415L; static final String CLAIM_KEY_USERNAME = "sub"; static final String CLAIM_KEY_CREATED = "iat"; @SuppressFBWarnings(value = "SE_BAD_FIELD", justification = "It's okay here") private Clock clock = DefaultClock.INSTANCE; private JwtConfig jwtConfig; public JwtTokenUtil(JwtConfig jwtConfig) { this.jwtConfig = jwtConfig; } public String getUsernameFromToken(String token) { return getClaimFromToken(token, Claims::getSubject); } public Date getIssuedAtDateFromToken(String token) { return getClaimFromToken(token, Claims::getIssuedAt); } public Date getExpirationDateFromToken(String token) { return getClaimFromToken(token, Claims::getExpiration); } public T getClaimFromToken(String token, Function claimsResolver) { final Claims claims = getAllClaimsFromToken(token); return claimsResolver.apply(claims); } private Claims getAllClaimsFromToken(String token) { return Jwts.parser() .setSigningKey(jwtConfig.getSecret()) .parseClaimsJws(token) .getBody(); } private Boolean isTokenExpired(String token) { final Date expiration = getExpirationDateFromToken(token); return expiration.before(clock.now()); } private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) { return (lastPasswordReset != null && created.before(lastPasswordReset)); } private Boolean ignoreTokenExpiration(String token) { // here you specify tokens, for that the expiration is ignored return false; } public String generateToken(UserDetails userDetails) { Map claims = new HashMap<>(); return doGenerateToken(claims, userDetails.getUsername()); } private String doGenerateToken(Map claims, String subject) { final Date createdDate = clock.now(); final Date expirationDate = calculateExpirationDate(createdDate); return Jwts.builder() .setClaims(claims) .setSubject(subject) .setIssuedAt(createdDate) .setExpiration(expirationDate) .signWith(SignatureAlgorithm.HS512, jwtConfig.getSecret()) .compact(); } public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) { final Date created = getIssuedAtDateFromToken(token); return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset) && (!isTokenExpired(token) || ignoreTokenExpiration(token)); } public String refreshToken(String token) { final Date createdDate = clock.now(); final Date expirationDate = calculateExpirationDate(createdDate); final Claims claims = getAllClaimsFromToken(token); claims.setIssuedAt(createdDate); claims.setExpiration(expirationDate); return Jwts.builder() .setClaims(claims) .signWith(SignatureAlgorithm.HS512, jwtConfig.getSecret()) .compact(); } public Boolean validateToken(String token, UserDetails userDetails) { JwtUser user = (JwtUser) userDetails; final String username = getUsernameFromToken(token); final Date created = getIssuedAtDateFromToken(token); //final Date expiration = getExpirationDateFromToken(token); return ( username.equals(user.getUsername()) && !isTokenExpired(token) && !isCreatedBeforeLastPasswordReset(created, user.getLastPasswordResetDate()) ); } private Date calculateExpirationDate(Date createdDate) { return new Date(createdDate.getTime() + jwtConfig.getExpiration() * 1000); } }