package com.sandu.common.security.token; import com.sandu.common.enums.AdminStatusStatus; import com.sandu.common.execption.BusinessException; import com.sandu.common.redis.RedisService; import com.sandu.common.security.LoginUserInfo; import com.sandu.common.security.config.SecurityProperties; import com.sandu.common.util.SpringContextHolder; import io.jsonwebtoken.*; import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.io.DecodingException; import io.jsonwebtoken.security.Keys; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.InitializingBean; import org.springframework.boot.autoconfigure.cache.CacheProperties; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.security.core.GrantedAuthority; import org.springframework.stereotype.Component; import java.security.Key; import java.util.Date; import java.util.stream.Collectors; /** * @author chenjiantian * @date 2022/1/8 18:12 */ @Component @Slf4j @ConditionalOnProperty(prefix = "sandu.jwt", name = "cache-online", havingValue = "false") public class JwtTokenProvider implements TokenProvider, InitializingBean { private static final String AUTHORITIES_KEY = "auth"; private static final String CREDENTIALS_KEY = "cred"; private static final String ADMINISTRATOR_KEY = "admin"; private final SecurityProperties properties; private Key key; public JwtTokenProvider(SecurityProperties properties) { this.properties = properties; } @Override public void afterPropertiesSet() { byte[] keyBytes = Decoders.BASE64.decode(properties.getBase64Secret()); this.key = Keys.hmacShaKeyFor(keyBytes); } @Override public String createToken(LoginUserInfo loginUserInfo) { if (loginUserInfo.getUserId() == null) { throw new IllegalArgumentException("用户id不能为空"); } RedisService redisService = SpringContextHolder.getBean(RedisService.class); String authorities = loginUserInfo.getAuthorities().stream() .map(GrantedAuthority::getAuthority) .collect(Collectors.joining(",")); long now = (new Date()).getTime(); Date expiration = new Date(now + properties.getTokenValidityInSeconds()); String token = Jwts.builder() .setSubject(loginUserInfo.getUserId().toString()) .claim(AUTHORITIES_KEY, authorities) .claim(CREDENTIALS_KEY, loginUserInfo.getAccount()) .claim(ADMINISTRATOR_KEY, loginUserInfo.getAdministratorType()) .setExpiration(expiration) .signWith(key, SignatureAlgorithm.HS512) .compact(); String key = String.format("%d_%d", loginUserInfo.getUserId(), loginUserInfo.getAdministratorType()); redisService.set(key, token, 2592000); return token; } @Override public LoginUserInfo validateToken(String token) { try { RedisService redisService = SpringContextHolder.getBean(RedisService.class); Claims claims = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody(); LoginUserInfo loginUserInfo = new LoginUserInfo(); loginUserInfo.setUserId(Long.valueOf(claims.getSubject())); loginUserInfo.setAccount(String.valueOf(claims.get(CREDENTIALS_KEY))); loginUserInfo.setPermission(claims.get(AUTHORITIES_KEY).toString()); loginUserInfo.setAdministratorType(Integer.parseInt(claims.get(ADMINISTRATOR_KEY).toString())); loginUserInfo.setToken(token); loginUserInfo.setStatus(AdminStatusStatus.NORMAL.getCode()); String key = String.format("%d_%d", loginUserInfo.getUserId(), loginUserInfo.getAdministratorType()); String redisToken = String.valueOf(redisService.get(key)); if (redisToken == null || !token.equals(redisToken)) { throw new BusinessException("token无效"); } return loginUserInfo; } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) { log.info("Invalid JWT signature."); } catch (ExpiredJwtException e) { log.info("Expired JWT token."); } catch (UnsupportedJwtException | DecodingException e) { log.info("Unsupported JWT token."); } catch (IllegalArgumentException e) { log.info("JWT token compact of handler are invalid."); } return null; } }