Browse Source

refactored creationing Jwt SecretKey and JwtParcer

pull/10671/head
YevhenBondarenko 2 years ago
parent
commit
1f648806f3
  1. 5
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java
  2. 7
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java
  3. 4
      application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java
  4. 5
      application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/DefaultJwtSettingsService.java
  5. 3
      application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/DefaultJwtSettingsValidator.java
  6. 43
      application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java

5
application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java

@ -94,6 +94,7 @@ import org.thingsboard.server.service.queue.processing.IdMsgPair;
import org.thingsboard.server.service.resource.TbImageService;
import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
import org.thingsboard.server.service.security.auth.jwt.settings.JwtSettingsService;
import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
import org.thingsboard.server.service.state.DeviceStateService;
import org.thingsboard.server.service.subscription.SubscriptionManagerService;
import org.thingsboard.server.service.subscription.TbLocalSubscriptionService;
@ -172,10 +173,12 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
PartitionService partitionService,
ApplicationEventPublisher eventPublisher,
JwtSettingsService jwtSettingsService,
JwtTokenFactory jwtTokenFactory,
NotificationSchedulerService notificationSchedulerService,
NotificationRuleProcessor notificationRuleProcessor,
TbImageService imageService) {
super(actorContext, tenantProfileCache, deviceProfileCache, assetProfileCache, apiUsageStateService, partitionService, eventPublisher, tbCoreQueueFactory.createToCoreNotificationsMsgConsumer(), jwtSettingsService);
super(actorContext, tenantProfileCache, deviceProfileCache, assetProfileCache, apiUsageStateService, partitionService,
eventPublisher, tbCoreQueueFactory.createToCoreNotificationsMsgConsumer(), jwtSettingsService, jwtTokenFactory);
this.mainConsumer = tbCoreQueueFactory.createToCoreMsgConsumer();
this.usageStatsConsumer = tbCoreQueueFactory.createToUsageStatsServiceMsgConsumer();
this.firmwareStatesConsumer = tbCoreQueueFactory.createToOtaPackageStateServiceMsgConsumer();

7
application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java

@ -56,6 +56,8 @@ import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService;
import org.thingsboard.server.service.security.auth.jwt.settings.JwtSettingsService;
import jakarta.annotation.PostConstruct;
import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@ -86,9 +88,10 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
TbApiUsageStateService apiUsageStateService,
PartitionService partitionService,
ApplicationEventPublisher eventPublisher,
JwtSettingsService jwtSettingsService) {
JwtSettingsService jwtSettingsService,
JwtTokenFactory jwtTokenFactory) {
super(actorContext, tenantProfileCache, deviceProfileCache, assetProfileCache, apiUsageStateService, partitionService,
eventPublisher, tbRuleEngineQueueFactory.createToRuleEngineNotificationsMsgConsumer(), jwtSettingsService);
eventPublisher, tbRuleEngineQueueFactory.createToRuleEngineNotificationsMsgConsumer(), jwtSettingsService, jwtTokenFactory);
this.ctx = ctx;
this.tbDeviceRpcService = tbDeviceRpcService;
this.queueService = queueService;

4
application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java

@ -48,6 +48,8 @@ import org.thingsboard.server.service.queue.TbPackProcessingContext;
import org.thingsboard.server.service.security.auth.jwt.settings.JwtSettingsService;
import jakarta.annotation.PreDestroy;
import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@ -75,6 +77,7 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene
protected final TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer;
protected final JwtSettingsService jwtSettingsService;
protected final JwtTokenFactory jwtTokenFactory;
public void init(String nfConsumerThreadName) {
this.notificationsConsumerExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName(nfConsumerThreadName));
@ -163,6 +166,7 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene
} else if (EntityType.TENANT.equals(componentLifecycleMsg.getEntityId().getEntityType())) {
if (TenantId.SYS_TENANT_ID.equals(tenantId)) {
jwtSettingsService.reloadJwtSettings();
jwtTokenFactory.reload();
return;
} else {
tenantProfileCache.evict(tenantId);

5
application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/DefaultJwtSettingsService.java

@ -15,7 +15,6 @@
*/
package org.thingsboard.server.service.security.auth.jwt.settings;
import io.jsonwebtoken.Jwts;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@ -31,6 +30,8 @@ import java.util.Base64;
import java.util.Objects;
import java.util.Optional;
import static org.thingsboard.server.service.security.model.token.JwtTokenFactory.KEY_LENGTH;
@Service
@RequiredArgsConstructor
@Slf4j
@ -105,7 +106,7 @@ public class DefaultJwtSettingsService implements JwtSettingsService {
}
public static boolean validateTokenSigningKeyLength(JwtSettings settings) {
return Base64.getDecoder().decode(settings.getTokenSigningKey()).length * Byte.SIZE >= Jwts.SIG.HS512.getKeyBitLength();
return Base64.getDecoder().decode(settings.getTokenSigningKey()).length * Byte.SIZE >= KEY_LENGTH;
}
}

3
application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/DefaultJwtSettingsValidator.java

@ -28,6 +28,7 @@ import java.util.Optional;
import java.util.concurrent.TimeUnit;
import static org.thingsboard.server.service.security.auth.jwt.settings.DefaultJwtSettingsService.isSigningKeyDefault;
import static org.thingsboard.server.service.security.model.token.JwtTokenFactory.KEY_LENGTH;
@Component
@RequiredArgsConstructor
@ -61,7 +62,7 @@ public class DefaultJwtSettingsValidator implements JwtSettingsValidator {
if (Arrays.isNullOrEmpty(decodedKey)) {
throw new DataValidationException("JWT token signing key should be non-empty after Base64 decoding!");
}
if (decodedKey.length * Byte.SIZE < 512 && !isSigningKeyDefault(jwtSettings)) {
if (decodedKey.length * Byte.SIZE < KEY_LENGTH && !isSigningKeyDefault(jwtSettings)) {
throw new DataValidationException("JWT token signing key should be a Base64 encoded string representing at least 512 bits of data!");
}

43
application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java

@ -20,6 +20,7 @@ import io.jsonwebtoken.ClaimsBuilder;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
@ -57,6 +58,8 @@ import java.util.stream.Collectors;
@Slf4j
public class JwtTokenFactory {
public static int KEY_LENGTH = Jwts.SIG.HS512.getKeyBitLength();
private static final String SCOPES = "scopes";
private static final String USER_ID = "userId";
private static final String FIRST_NAME = "firstName";
@ -69,6 +72,9 @@ public class JwtTokenFactory {
private final JwtSettingsService jwtSettingsService;
private volatile JwtParser jwtParser;
private volatile SecretKey secretKey;
/**
* Factory method for issuing new JWT Tokens.
*/
@ -180,6 +186,11 @@ public class JwtTokenFactory {
return new AccessJwtToken(jwtBuilder.compact());
}
public void reload() {
getSecretKey(true);
getJwtParser(true);
}
private JwtBuilder setUpToken(SecurityUser securityUser, List<String> scopes, long expirationTime) {
if (StringUtils.isBlank(securityUser.getEmail())) {
throw new IllegalArgumentException("Cannot create JWT Token without username/email");
@ -202,15 +213,12 @@ public class JwtTokenFactory {
.issuer(jwtSettingsService.getJwtSettings().getTokenIssuer())
.issuedAt(Date.from(currentTime.toInstant()))
.expiration(Date.from(currentTime.plusSeconds(expirationTime).toInstant()))
.signWith(toSecretKey(jwtSettingsService.getJwtSettings().getTokenSigningKey()), Jwts.SIG.HS512);
.signWith(getSecretKey(false), Jwts.SIG.HS512);
}
public Jws<Claims> parseTokenClaims(String token) {
try {
return Jwts.parser()
.verifyWith(Keys.hmacShaKeyFor(Base64.getDecoder().decode(jwtSettingsService.getJwtSettings().getTokenSigningKey())))
.build()
.parseSignedClaims(token);
return getJwtParser(false).parseSignedClaims(token);
} catch (UnsupportedJwtException | MalformedJwtException | IllegalArgumentException ex) {
log.debug("Invalid JWT Token", ex);
throw new BadCredentialsException("Invalid JWT token: ", ex);
@ -226,9 +234,28 @@ public class JwtTokenFactory {
return new JwtPair(accessToken.getToken(), refreshToken.getToken());
}
private SecretKey toSecretKey(String base64Key) {
byte[] decodedToken = Base64.getDecoder().decode(base64Key);
return new SecretKeySpec(decodedToken, "HmacSHA512");
private SecretKey getSecretKey(boolean forceReload) {
if (secretKey == null || forceReload) {
synchronized (this) {
if (secretKey == null || forceReload) {
byte[] decodedToken = Base64.getDecoder().decode(jwtSettingsService.getJwtSettings().getTokenSigningKey());
secretKey = new SecretKeySpec(decodedToken, "HmacSHA512");
}
}
}
return secretKey;
}
private JwtParser getJwtParser(boolean forceReload) {
if (jwtParser == null || forceReload) {
synchronized (this) {
if (jwtParser == null || forceReload) {
jwtParser = Jwts.parser()
.verifyWith(Keys.hmacShaKeyFor(Base64.getDecoder().decode(jwtSettingsService.getJwtSettings().getTokenSigningKey())))
.build();
}
}
}
return jwtParser;
}
}

Loading…
Cancel
Save