All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.rocketbase.commons.security.JwtTokenService Maven / Gradle / Ivy

There is a newer version: 4.4.1
Show newest version
package io.rocketbase.commons.security;

import io.jsonwebtoken.*;
import io.rocketbase.commons.config.JwtProperties;
import io.rocketbase.commons.converter.AppUserConverter;
import io.rocketbase.commons.dto.authentication.JwtTokenBundle;
import io.rocketbase.commons.model.AppUserEntity;
import io.rocketbase.commons.model.AppUserToken;
import io.rocketbase.commons.model.SimpleAppUserToken;
import io.rocketbase.commons.util.RolesAuthoritiesConverter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

import java.io.Serializable;
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.*;
import java.util.function.Function;

@Slf4j
@RequiredArgsConstructor
public class JwtTokenService implements Serializable {

    public static final String REFRESH_TOKEN = "REFRESH_TOKEN";
    public static final String ROLES_KEY = "scopes";
    public static final String USER_ID_KEY = "user_id";
    public static final String FIRST_NAME_KEY = "given_name";
    public static final String LAST_NAME_KEY = "family_name";
    public static final String EMAIL_KEY = "email";
    public static final String AVATAR_KEY = "picture";
    public static final String KEY_VALUE_PREFIX = "kv_";

    final JwtProperties jwtProperties;
    final CustomAuthoritiesProvider customAuthoritiesProvider;

    public String getUsernameFromToken(String token) {
        return getClaimFromToken(token, Claims::getSubject);
    }

    public Collection getAuthoritiesFromToken(String token) {
        Claims claims = getAllClaimsFromToken(token);
        List roles = (List) claims.getOrDefault(ROLES_KEY, Collections.emptyList());
        List result = new ArrayList<>();
        for (Object r : roles) {
            result.add(new SimpleGrantedAuthority(String.valueOf(r)));
        }
        return result;
    }

    public Instant getIssuedAtDateFromToken(String token) {
        Date issuedAt = getClaimFromToken(token, Claims::getIssuedAt);
        return Instant.ofEpochMilli(issuedAt.getTime());
    }

    public Instant getExpirationDateFromToken(String token) {
        Date expiration = getClaimFromToken(token, Claims::getExpiration);
        return Instant.ofEpochMilli(expiration.getTime());
    }

    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(jwtProperties.getSecret())
                .parseClaimsJws(token)
                .getBody();
    }

    public AppUserToken parseToken(String token) {
        Claims claims = getAllClaimsFromToken(token);
        Map keyValues = null;
        for (String key : claims.keySet()) {
            if (key.startsWith(KEY_VALUE_PREFIX)) {
                if (keyValues == null) {
                    keyValues = new HashMap<>();
                }
                keyValues.put(key.replaceAll("^" + KEY_VALUE_PREFIX, ""), claims.get(key, String.class));
            }
        }

        return SimpleAppUserToken.builder()
                .id(claims.get(USER_ID_KEY, String.class))
                .username(claims.getSubject())
                .firstName(claims.get(FIRST_NAME_KEY, String.class))
                .lastName(claims.get(LAST_NAME_KEY, String.class))
                .email(claims.get(EMAIL_KEY, String.class))
                .avatar(claims.get(AVATAR_KEY, String.class))
                .roles((List) claims.getOrDefault(ROLES_KEY, Collections.emptyList()))
                .keyValueMap(keyValues)
                .build();
    }


    public JwtTokenBundle generateTokenBundle(AppUserToken appUserToken) {
        Instant now = Instant.now();
        return new JwtTokenBundle(generateAccessToken(now, appUserToken),
                prepareBuilder(now, jwtProperties.getRefreshTokenExpiration(), appUserToken.getUsername())
                        .claim(USER_ID_KEY, appUserToken.getId())
                        .claim(ROLES_KEY, Arrays.asList(REFRESH_TOKEN))
                        .compact());
    }

    public String generateAccessToken(AppUserToken appUserToken) {
        return generateAccessToken(Instant.now(), appUserToken);
    }

    protected String generateAccessToken(Instant ldt, AppUserToken appUserToken) {
        List scopes = new ArrayList<>();
        scopes.addAll(RolesAuthoritiesConverter.convert(appUserToken.getRoles()));
        scopes.addAll(customAuthoritiesProvider.getExtraTokenAuthorities(appUserToken));

        JwtBuilder jwtBuilder = prepareBuilder(ldt, jwtProperties.getAccessTokenExpiration(), appUserToken.getUsername())
                .claim(ROLES_KEY, RolesAuthoritiesConverter.convertToDtos(scopes))
                .claim(USER_ID_KEY, appUserToken.getId())
                .claim(FIRST_NAME_KEY, appUserToken.getFirstName())
                .claim(LAST_NAME_KEY, appUserToken.getLastName())
                .claim(EMAIL_KEY, appUserToken.getEmail())
                .claim(AVATAR_KEY, appUserToken.getAvatar());

        Map keyValues = AppUserConverter.filterInvisibleKeys(appUserToken.getKeyValues());
        if (keyValues != null) {
            for (Map.Entry entry : keyValues.entrySet()) {
                if (!entry.getKey().startsWith("#")) {
                    jwtBuilder.claim(KEY_VALUE_PREFIX + entry.getKey(), entry.getValue());
                }
            }
        }
        return jwtBuilder.compact();
    }

    private JwtBuilder prepareBuilder(Instant ldt, long expirationMinutes, String username) {
        return Jwts.builder()
                .setIssuedAt(convert(ldt))
                .setExpiration(convert(ldt.plusSeconds(expirationMinutes*60)))
                .signWith(SignatureAlgorithm.HS512, jwtProperties.getSecret())
                .setSubject(username);
    }

    private Date convert(Instant ldt) {
        return Date.from(ldt.atZone(ZoneOffset.UTC)
                .toInstant());
    }

    /**
     * check if token string is valid and fit expected username
     *
     * @param token
     * @param username
     * @param lastTokenInvalidation
     * @return true in case of valid
     */
    public Boolean validateToken(String token, String username, Instant lastTokenInvalidation) {
        try {
            getAllClaimsFromToken(token);
        } catch (JwtException e) {
            // should show catch expiration etc. exceptions
            if (log.isTraceEnabled()) {
                log.trace("token is invalid", e);
            }
            return false;
        }
        if (!getUsernameFromToken(token).equals(username)) {
            // username not fitting
            if (log.isTraceEnabled()) {
                log.trace("token username differs");
            }
            return false;
        }
        if (lastTokenInvalidation == null) {
            return true;
        } else {
            // check if token creation is newer then last token invalidation
            boolean validIssued = lastTokenInvalidation.isBefore(getIssuedAtDateFromToken(token));
            if (log.isTraceEnabled() && !validIssued) {
                log.trace("token is issued {} before lastTokenInvalidation {}", getIssuedAtDateFromToken(token), lastTokenInvalidation);
            }
            return validIssued;
        }
    }

    public Boolean validateToken(String token, AppUserEntity user) {
        return validateToken(token, user.getUsername(), user.getLastTokenInvalidation());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy