diff --git a/common/src/main/java/com/bht/saigonparking/common/auth/SaigonParkingAuthentication.java b/common/src/main/java/com/bht/saigonparking/common/auth/SaigonParkingAuthentication.java index 50190efa..046664c3 100644 --- a/common/src/main/java/com/bht/saigonparking/common/auth/SaigonParkingAuthentication.java +++ b/common/src/main/java/com/bht/saigonparking/common/auth/SaigonParkingAuthentication.java @@ -1,5 +1,7 @@ package com.bht.saigonparking.common.auth; +import java.util.UUID; + import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; @@ -17,14 +19,14 @@ public interface SaigonParkingAuthentication { SaigonParkingTokenBody parseJwtToken(@NotEmpty String jsonWebToken); /* 1st: tokenId, 2nd: token */ - Pair generateAccessToken(@NotNull Long userId, @NotEmpty String userRole); + Pair generateAccessToken(@NotNull Long userId, @NotEmpty String userRole); /* 1st: tokenId, 2nd: token */ - Pair generateRefreshToken(@NotNull Long userId, @NotEmpty String userRole); + Pair generateRefreshToken(@NotNull Long userId, @NotEmpty String userRole); /* 1st: tokenId, 2nd: token */ - Pair generateActivateAccountToken(@NotNull Long userId, @NotEmpty String userRole); + Pair generateActivateAccountToken(@NotNull Long userId, @NotEmpty String userRole); /* 1st: tokenId, 2nd: token */ - Pair generateResetPasswordToken(@NotNull Long userId, @NotEmpty String userRole); + Pair generateResetPasswordToken(@NotNull Long userId, @NotEmpty String userRole); } \ No newline at end of file diff --git a/common/src/main/java/com/bht/saigonparking/common/auth/SaigonParkingAuthenticationImpl.java b/common/src/main/java/com/bht/saigonparking/common/auth/SaigonParkingAuthenticationImpl.java index d458e194..bb2eaa36 100644 --- a/common/src/main/java/com/bht/saigonparking/common/auth/SaigonParkingAuthenticationImpl.java +++ b/common/src/main/java/com/bht/saigonparking/common/auth/SaigonParkingAuthenticationImpl.java @@ -12,6 +12,7 @@ import java.util.Objects; import java.util.Properties; import java.util.Random; +import java.util.UUID; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; @@ -84,17 +85,17 @@ private byte[] getSecretKeyByteArray(@NotEmpty String keyPath) throws IOExceptio .getBytes(StandardCharsets.UTF_8)); } - private Pair generateJwtToken(@NotNull SaigonParkingTokenType type, - @NotNull Long userId, - @NotEmpty String userRole, - @NotNull Integer timeAmount, - @NotNull ChronoUnit timeUnit) { + private Pair generateJwtToken(@NotNull SaigonParkingTokenType type, + @NotNull Long userId, + @NotEmpty String userRole, + @NotNull Integer timeAmount, + @NotNull ChronoUnit timeUnit) { Instant now = Instant.now(); Integer factor = new Random().nextInt(MAX_RANDOM_EXCLUSIVE); - String tokenId = UUID_GENERATOR.generate().toString(); + UUID tokenUuid = UUID_GENERATOR.generate(); - return Pair.of(tokenId, Jwts.builder() - .setId(tokenId) + return Pair.of(tokenUuid, Jwts.builder() + .setId(tokenUuid.toString()) .setIssuer(SAIGON_PARKING_ISSUER) .claim(USER_ROLE_KEY_NAME, userRole) .claim(FACTOR_KEY_NAME, factor) @@ -123,22 +124,22 @@ public SaigonParkingTokenBody parseJwtToken(@NotEmpty String jsonWebToken) { } @Override - public Pair generateAccessToken(@NotNull Long userId, @NotEmpty String userRole) { + public Pair generateAccessToken(@NotNull Long userId, @NotEmpty String userRole) { return generateJwtToken(ACCESS_TOKEN, userId, userRole, 30, ChronoUnit.MINUTES); } @Override - public Pair generateRefreshToken(@NotNull Long userId, @NotEmpty String userRole) { + public Pair generateRefreshToken(@NotNull Long userId, @NotEmpty String userRole) { return generateJwtToken(REFRESH_TOKEN, userId, userRole, 30, ChronoUnit.DAYS); } @Override - public Pair generateActivateAccountToken(@NotNull Long userId, @NotEmpty String userRole) { + public Pair generateActivateAccountToken(@NotNull Long userId, @NotEmpty String userRole) { return generateJwtToken(ACTIVATE_TOKEN, userId, userRole, 5, ChronoUnit.MINUTES); } @Override - public Pair generateResetPasswordToken(@NotNull Long userId, @NotEmpty String userRole) { + public Pair generateResetPasswordToken(@NotNull Long userId, @NotEmpty String userRole) { return generateJwtToken(RESET_PW_TOKEN, userId, userRole, 5, ChronoUnit.MINUTES); } } \ No newline at end of file diff --git a/dev/database/backup/auth.bak b/dev/database/backup/auth.bak index 3f38d264..efdb1402 100755 Binary files a/dev/database/backup/auth.bak and b/dev/database/backup/auth.bak differ diff --git a/dev/database/diagram/auth.png b/dev/database/diagram/auth.png index 73399462..34eac490 100755 Binary files a/dev/database/diagram/auth.png and b/dev/database/diagram/auth.png differ diff --git a/dev/database/script/schema/auth_schema.sql b/dev/database/script/schema/auth_schema.sql old mode 100644 new mode 100755 index 780968f6..2f760187 Binary files a/dev/database/script/schema/auth_schema.sql and b/dev/database/script/schema/auth_schema.sql differ diff --git a/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/entity/UserTokenEntity.java b/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/entity/UserTokenEntity.java index 9a2c3656..339d0e30 100644 --- a/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/entity/UserTokenEntity.java +++ b/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/entity/UserTokenEntity.java @@ -1,5 +1,7 @@ package com.bht.saigonparking.service.auth.entity; +import java.util.UUID; + import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; @@ -9,7 +11,7 @@ import org.hibernate.annotations.NaturalId; import org.hibernate.annotations.NaturalIdCache; import org.hibernate.annotations.SelectBeforeUpdate; -import org.hibernate.validator.constraints.Length; +import org.hibernate.annotations.Type; import lombok.AllArgsConstructor; import lombok.Builder; @@ -40,10 +42,10 @@ public final class UserTokenEntity { @Column(name = "[USER_ID]") private Long userId; - @Length(max = 40) + @Type(type = "uuid-char") @NaturalId(mutable = true) - @Column(name = "[TOKEN_ID]", unique = true, nullable = false) - private String tokenId; + @Column(name = "[TOKEN_ID]", unique = true, nullable = false, columnDefinition = "UNIQUEIDENTIFIER") + private UUID tokenId; @Version @EqualsAndHashCode.Exclude diff --git a/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/interceptor/AuthServiceInterceptor.java b/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/interceptor/AuthServiceInterceptor.java index d1d3dd23..6fca16b2 100644 --- a/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/interceptor/AuthServiceInterceptor.java +++ b/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/interceptor/AuthServiceInterceptor.java @@ -6,6 +6,7 @@ import java.util.Date; import java.util.Map; import java.util.Set; +import java.util.UUID; import javax.persistence.EntityNotFoundException; @@ -72,7 +73,7 @@ public final class AuthServiceInterceptor implements ServerInterceptor { @Getter private final Context.Key userRoleContext = Context.key("userRole"); @Getter - private final Context.Key tokenIdContext = Context.key("tokenId"); + private final Context.Key tokenIdContext = Context.key("tokenId"); @Getter private final Context.Key tokenTypeContext = Context.key("tokenType"); @Getter @@ -137,12 +138,11 @@ public ServerCall.Listener interceptCall(ServerCall ServerCall.Listener interceptCall(ServerCall ServerCall.Listener interceptCall(ServerCall { /** - * * self-implement findByTokenId method * in order to prevent N+1 problem * using optional to catch null return @@ -26,5 +26,5 @@ public interface UserTokenRepository extends JpaRepository findByTokenId(@NotEmpty String tokenId); + Optional findByTokenId(@NotEmpty UUID tokenId); } \ No newline at end of file diff --git a/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/service/AuthService.java b/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/service/AuthService.java index a1189e6f..a2d930eb 100644 --- a/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/service/AuthService.java +++ b/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/service/AuthService.java @@ -1,6 +1,7 @@ package com.bht.saigonparking.service.auth.service; import java.util.Date; +import java.util.UUID; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; @@ -54,7 +55,7 @@ Pair validateLogin(@NotEmpty String username, */ Triple generateNewToken(@NotNull Long userId, @NotNull Date currentExp, - @NotEmpty String currentTokenId, + @NotEmpty UUID currentTokenId, boolean currentIsRefreshToken); /** @@ -66,6 +67,6 @@ Triple generateNewToken(@NotNull Long userId, */ Triple activateNewAccount(@NotNull Long userId, @NotNull Date currentExp, - @NotEmpty String currentTokenId, + @NotEmpty UUID currentTokenId, boolean currentIsRefreshToken); } \ No newline at end of file diff --git a/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/service/grpc/AuthServiceGrpcImpl.java b/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/service/grpc/AuthServiceGrpcImpl.java index 4df913d6..86b39921 100644 --- a/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/service/grpc/AuthServiceGrpcImpl.java +++ b/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/service/grpc/AuthServiceGrpcImpl.java @@ -34,6 +34,7 @@ public final class AuthServiceGrpcImpl extends AuthServiceGrpc.AuthServiceImplBa private final AuthService authService; private final AuthServiceInterceptor authServiceInterceptor; + private final UserServiceGrpc.UserServiceBlockingStub userServiceBlockingStub; @Override @@ -132,8 +133,7 @@ public void registerUser(RegisterRequest request, StreamObserver re @Override public void sendResetPasswordEmail(StringValue request, StreamObserver responseObserver) { try { - StringValue email = StringValue.of(authService - .sendResetPasswordEmail(request.getValue())); + StringValue email = StringValue.of(authService.sendResetPasswordEmail(request.getValue())); responseObserver.onNext(email); responseObserver.onCompleted(); @@ -154,8 +154,7 @@ public void sendResetPasswordEmail(StringValue request, StreamObserver responseObserver) { try { - StringValue email = StringValue.of(authService - .sendActivateAccountEmail(request.getValue())); + StringValue email = StringValue.of(authService.sendActivateAccountEmail(request.getValue())); responseObserver.onNext(email); responseObserver.onCompleted(); diff --git a/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/service/impl/AuthServiceHelperImpl.java b/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/service/impl/AuthServiceHelperImpl.java index 77979c20..b42c2a07 100644 --- a/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/service/impl/AuthServiceHelperImpl.java +++ b/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/service/impl/AuthServiceHelperImpl.java @@ -3,6 +3,8 @@ import static com.bht.saigonparking.common.constant.SaigonParkingMessageQueue.MAIL_TOPIC_ROUTING_KEY; import static com.bht.saigonparking.common.constant.SaigonParkingMessageQueue.USER_TOPIC_ROUTING_KEY; +import java.util.UUID; + import javax.persistence.EntityNotFoundException; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; @@ -79,7 +81,7 @@ public void sendMail(@NotNull MailRequestType type, .build()); } - public void saveUserRefreshToken(@NotNull Long userId, @NotEmpty String tokenId) { + public void saveUserRefreshToken(@NotNull Long userId, @NotEmpty UUID tokenId) { try { /* update refresh token id to database if already existed */ UserTokenEntity userTokenEntity = userTokenRepository.findById(userId).orElseThrow(EntityNotFoundException::new); @@ -87,14 +89,12 @@ public void saveUserRefreshToken(@NotNull Long userId, @NotEmpty String tokenId) userTokenRepository.saveAndFlush(userTokenEntity); } catch (EntityNotFoundException entityNotFoundException) { - /* save new refresh token id to database if not existed before */ UserTokenEntity userTokenEntity = UserTokenEntity.builder().userId(userId).tokenId(tokenId).build(); userTokenRepository.saveAndFlush(userTokenEntity); } finally { - LoggingUtil.log(Level.INFO, "SERVICE", "Success", - String.format("saveUserRefreshToken(%d, %s)", userId, tokenId)); + LoggingUtil.log(Level.INFO, "SERVICE", "Success", String.format("saveUserRefreshToken(%d, %s)", userId, tokenId)); } } } \ No newline at end of file diff --git a/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/service/impl/AuthServiceImpl.java b/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/service/impl/AuthServiceImpl.java index 84c71ff5..1b79c10e 100644 --- a/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/service/impl/AuthServiceImpl.java +++ b/service/auth-service/src/main/java/com/bht/saigonparking/service/auth/service/impl/AuthServiceImpl.java @@ -4,6 +4,7 @@ import static com.bht.saigonparking.api.grpc.mail.MailRequestType.RESET_PASSWORD; import java.util.Date; +import java.util.UUID; import javax.persistence.EntityNotFoundException; import javax.validation.constraints.NotEmpty; @@ -65,8 +66,8 @@ public Pair validateLogin(@NotEmpty String username, context.run(() -> authServiceImplHelper.updateUserLastSignIn(user.getId())); /* Generate new access token, new refresh token for user with Id, Role */ - Pair generatedAccessToken = authentication.generateAccessToken(user.getId(), user.getRole().toString()); - Pair generatedRefreshToken = authentication.generateRefreshToken(user.getId(), user.getRole().toString()); + Pair generatedAccessToken = authentication.generateAccessToken(user.getId(), user.getRole().toString()); + Pair generatedRefreshToken = authentication.generateRefreshToken(user.getId(), user.getRole().toString()); /* Asynchronously save user token to the database */ authServiceImplHelper.saveUserRefreshToken(user.getId(), generatedRefreshToken.getFirst()); @@ -136,7 +137,7 @@ public String sendActivateAccountEmail(@NotEmpty String username) { @Override public Triple generateNewToken(@NotNull Long userId, @NotNull Date currentExp, - @NotEmpty String currentTokenId, + @NotEmpty UUID currentTokenId, boolean currentIsRefreshToken) { User user = userServiceBlockingStub.getUserById(Int64Value.of(userId)); @@ -147,7 +148,7 @@ public Triple generateNewToken(@NotNull Long userId, @Override public Triple activateNewAccount(@NotNull Long userId, @NotNull Date currentExp, - @NotEmpty String currentTokenId, + @NotEmpty UUID currentTokenId, boolean currentIsRefreshToken) { User user = userServiceBlockingStub.getUserById(Int64Value.of(userId)); @@ -162,7 +163,7 @@ public Triple activateNewAccount(@NotNull Long userId, @SuppressWarnings("java:S2201") private Triple generateNewToken(@NotNull User user, @NotNull Date currentExp, - @NotEmpty String currentTokenId, + @NotEmpty UUID currentTokenId, boolean currentIsRefreshToken) { try { if (currentIsRefreshToken) { @@ -173,7 +174,7 @@ private Triple generateNewToken(@NotNull User user, throw new InvalidRefreshTokenException(); } - Pair generatedAccessToken = authentication.generateAccessToken(user.getId(), user.getRole().toString()); + Pair generatedAccessToken = authentication.generateAccessToken(user.getId(), user.getRole().toString()); if (((currentExp.getTime() - new Date().getTime()) / 86400000) > 7) { /* Token not nearly expire */ return Triple.of(user.getUsername(), generatedAccessToken.getSecond(), ""); @@ -181,7 +182,7 @@ private Triple generateNewToken(@NotNull User user, } else { /* Token nearly expire */ /* Generate new refresh token for user with Id, Role */ - Pair generatedRefreshToken = authentication.generateRefreshToken(user.getId(), user.getRole().toString()); + Pair generatedRefreshToken = authentication.generateRefreshToken(user.getId(), user.getRole().toString()); authServiceImplHelper.saveUserRefreshToken(user.getId(), generatedRefreshToken.getFirst()); return Triple.of(user.getUsername(), generatedAccessToken.getSecond(), generatedRefreshToken.getSecond());