diff --git a/.gitignore b/.gitignore index fb8d881..e331b61 100644 --- a/.gitignore +++ b/.gitignore @@ -72,3 +72,7 @@ build/ .vscode/ .env + +application-*.properties +application-*.yml +application-*.yaml diff --git a/framework/src/main/java/io/theurl/framework/configure/GlobalExceptionHandler.java b/framework/src/main/java/io/theurl/framework/configure/GlobalExceptionHandler.java index 95a3525..47db6e6 100644 --- a/framework/src/main/java/io/theurl/framework/configure/GlobalExceptionHandler.java +++ b/framework/src/main/java/io/theurl/framework/configure/GlobalExceptionHandler.java @@ -66,7 +66,10 @@ public ResponseEntity> handleEntityNotFoundException(EntityN @ExceptionHandler(AggregateException.class) public ResponseEntity> handleAggregateException(AggregateException exception) { - LOGGER.error(exception.getMessage(), exception); + for (var ex : exception.getExceptions()) { + LOGGER.error(ex.getLocalizedMessage(), ex); + } + Throwable cause = exception.getExceptions().getFirst(); LOGGER.debug("AggregateException contains {} exceptions, handling the first one: {}", exception.getExceptions().size(), cause.getMessage()); return handleGeneralException(cause); diff --git a/framework/src/main/java/io/theurl/framework/core/BeanScope.java b/framework/src/main/java/io/theurl/framework/core/BeanScope.java new file mode 100644 index 0000000..04ce303 --- /dev/null +++ b/framework/src/main/java/io/theurl/framework/core/BeanScope.java @@ -0,0 +1,27 @@ +package io.theurl.framework.core; + +public class BeanScope { + public final static String APPLICATION = "application"; + public final static String PROTOTYPE = "prototype"; + public final static String REQUEST = "request"; + public final static String SESSION = "session"; + public final static String SINGLETON = "singleton"; + public final static String WEB_SOCKET = "websocket"; +// APPLICATION("application"), +// PROTOTYPE("prototype"), +// REQUEST("request"), +// SESSION("session"), +// SINGLETON("singleton"), +// WEB_SOCKET("websocket"); +// +// private final String name; +// +// BeanScope(String name) { +// this.name = name; +// } +// +// @Override +// public String toString() { +// return name; +// } +} diff --git a/framework/src/main/java/io/theurl/framework/core/ObjectId.java b/framework/src/main/java/io/theurl/framework/core/ObjectId.java index af07dd9..a44ebf5 100644 --- a/framework/src/main/java/io/theurl/framework/core/ObjectId.java +++ b/framework/src/main/java/io/theurl/framework/core/ObjectId.java @@ -55,4 +55,9 @@ public boolean equals(Object obj) { ObjectId objectId = (ObjectId) obj; return value.equals(objectId.value); } + + @Override + public String toString() { + return value.toString(); + } } diff --git a/identity/pom.xml b/identity/pom.xml index 929e189..dd8eb5c 100644 --- a/identity/pom.xml +++ b/identity/pom.xml @@ -33,6 +33,17 @@ jjwt-api 0.13.0 + + io.jsonwebtoken + jjwt-impl + 0.13.0 + + + io.jsonwebtoken + jjwt-jackson + 0.13.0 + runtime + diff --git a/identity/src/main/java/io/theurl/identity/application/event/UserAuthSuccessEvent.java b/identity/src/main/java/io/theurl/identity/application/event/UserAuthSuccessEvent.java index 2b5c928..2aeb0df 100644 --- a/identity/src/main/java/io/theurl/identity/application/event/UserAuthSuccessEvent.java +++ b/identity/src/main/java/io/theurl/identity/application/event/UserAuthSuccessEvent.java @@ -4,8 +4,10 @@ import io.theurl.framework.domain.ApplicationEvent; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.Getter; import java.time.LocalDateTime; +import java.util.HashMap; import java.util.Map; @EqualsAndHashCode(callSuper = true) @@ -14,7 +16,6 @@ public final class UserAuthSuccessEvent extends ApplicationEvent implements Even private final String grantType; private final Long userId; - public UserAuthSuccessEvent(String grantType, Long userId) { this.grantType = grantType; this.userId = userId; @@ -22,5 +23,7 @@ public UserAuthSuccessEvent(String grantType, Long userId) { private String username; private LocalDateTime grantTime; - private Map data; + + @Getter + private Map data = new HashMap<>(); } diff --git a/identity/src/main/java/io/theurl/identity/application/handler/TokenCreateCommandHandler.java b/identity/src/main/java/io/theurl/identity/application/handler/TokenCreateCommandHandler.java index 37f27c1..f21688d 100644 --- a/identity/src/main/java/io/theurl/identity/application/handler/TokenCreateCommandHandler.java +++ b/identity/src/main/java/io/theurl/identity/application/handler/TokenCreateCommandHandler.java @@ -1,19 +1,19 @@ package io.theurl.identity.application.handler; import com.neroyun.mediator.Handler; +import io.theurl.framework.core.BeanScope; import io.theurl.identity.application.command.TokenCreateCommand; import io.theurl.identity.domain.aggregate.Token; import io.theurl.identity.domain.repository.TokenRepository; import jakarta.annotation.Resource; import org.springframework.context.annotation.Scope; -import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.stereotype.Component; -import org.springframework.web.context.WebApplicationContext; import java.util.concurrent.CompletableFuture; @Component -@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) +@Scope(BeanScope.PROTOTYPE) +//@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) public class TokenCreateCommandHandler implements Handler { @Resource diff --git a/identity/src/main/java/io/theurl/identity/application/implement/AuthApplicationServiceImpl.java b/identity/src/main/java/io/theurl/identity/application/implement/AuthApplicationServiceImpl.java index d76d893..ecc742f 100644 --- a/identity/src/main/java/io/theurl/identity/application/implement/AuthApplicationServiceImpl.java +++ b/identity/src/main/java/io/theurl/identity/application/implement/AuthApplicationServiceImpl.java @@ -81,7 +81,7 @@ public CompletableFuture grant(TokenGrantRequestDto reque throw new CredentialNotFoundException(request.username(), "Invalid username or password."); } - if (userInfo.getLockedUntil().isAfter(LocalDateTime.now())) { + if (userInfo.getLockedUntil() != null && userInfo.getLockedUntil().isAfter(LocalDateTime.now())) { throw new AccountLockedException(request.username(), "Account is locked until " + userInfo.getLockedUntil()); } @@ -92,13 +92,17 @@ public CompletableFuture grant(TokenGrantRequestDto reque } } - events.add(new UserAuthSuccessEvent(request.grantType(), userInfo.getId())); - var jwtId = ObjectId.guid().toString(); var iat = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC); var exp = LocalDateTime.now().plusHours(24).toEpochSecond(ZoneOffset.UTC); var accessToken = generateToken(jwtId, userInfo, iat, exp); + var event = new UserAuthSuccessEvent(request.grantType(), userInfo.getId()); + event.setGrantTime(LocalDateTime.now()); + event.getData().put("jti", jwtId); + event.getData().put("jwt", accessToken); + events.add(event); + return new TokenGrantResponseDto(accessToken, jwtId, "Bearer", 3600 * 24, iat, userInfo.getUsername(), userInfo.getId()); } catch (Exception e) { diff --git a/identity/src/main/java/io/theurl/identity/application/subscriber/TokenEventSubscriber.java b/identity/src/main/java/io/theurl/identity/application/subscriber/TokenEventSubscriber.java index 0c34575..de939ed 100644 --- a/identity/src/main/java/io/theurl/identity/application/subscriber/TokenEventSubscriber.java +++ b/identity/src/main/java/io/theurl/identity/application/subscriber/TokenEventSubscriber.java @@ -1,13 +1,16 @@ package io.theurl.identity.application.subscriber; import com.neroyun.mediator.Mediator; +import io.theurl.framework.core.BeanScope; import io.theurl.identity.application.command.TokenCreateCommand; import io.theurl.identity.application.event.UserAuthSuccessEvent; +import org.springframework.context.annotation.Scope; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; @Component +@Scope(BeanScope.PROTOTYPE) public class TokenEventSubscriber { private final Mediator mediator; @@ -20,7 +23,7 @@ public TokenEventSubscriber(Mediator mediator) { public void handleUserAuthSucceedEvent(UserAuthSuccessEvent event) { var command = new TokenCreateCommand() {{ setJti(event.getData().get("jti")); - setContent(event.getData().get("content")); + setContent(event.getData().get("jwt")); setSubject(event.getUserId()); }}; diff --git a/identity/src/main/java/io/theurl/identity/domain/aggregate/Token.java b/identity/src/main/java/io/theurl/identity/domain/aggregate/Token.java index 4ff26d4..44605bd 100644 --- a/identity/src/main/java/io/theurl/identity/domain/aggregate/Token.java +++ b/identity/src/main/java/io/theurl/identity/domain/aggregate/Token.java @@ -7,9 +7,9 @@ @SuppressWarnings({"LombokGetterMayBeUsed"}) public class Token extends AggregateRoot { - private String jti; - private String content; - private Long subject; + private final String jti; + private final String content; + private final Long subject; private LocalDateTime issuedAt; private LocalDateTime expiresAt; @@ -20,6 +20,9 @@ public class Token extends AggregateRoot { */ public Token(Long id, String jti, String content, Long subject) { super(id); + this.jti = jti; + this.content = content; + this.subject = subject; } public LocalDateTime getIssuedAt() { @@ -30,15 +33,27 @@ public LocalDateTime getExpiresAt() { return expiresAt; } + public String getContent() { + return content; + } + + public String getJti() { + return jti; + } + + public Long getSubject() { + return subject; + } + public void setIssuedAt(LocalDateTime issuedAt) { - if (issuedAt.isBefore(LocalDateTime.now())) { + if (issuedAt != null && issuedAt.isBefore(LocalDateTime.now())) { throw new IllegalArgumentException("issuedAt must be in the future"); } this.issuedAt = issuedAt; } public void setExpiresAt(LocalDateTime expiresAt) { - if (expiresAt.isBefore(LocalDateTime.now())) { + if (expiresAt != null && expiresAt.isBefore(LocalDateTime.now())) { throw new IllegalArgumentException("expiresAt must be in the future"); } this.expiresAt = expiresAt; diff --git a/identity/src/main/resources/application-dev.yaml b/identity/src/main/resources/application-dev.yaml deleted file mode 100644 index 26e8472..0000000 --- a/identity/src/main/resources/application-dev.yaml +++ /dev/null @@ -1,5 +0,0 @@ -spring: - datasource: - url: ${DB_URL:jdbc:postgresql://localhost:5432/linkyou?currentSchema=public} - username: ${DB_USERNAME:postgres} - password: ${DB_PASSWORD:nerosoft.8888} diff --git a/message/src/main/resources/application-dev.yaml b/message/src/main/resources/application-dev.yaml deleted file mode 100644 index 26e8472..0000000 --- a/message/src/main/resources/application-dev.yaml +++ /dev/null @@ -1,5 +0,0 @@ -spring: - datasource: - url: ${DB_URL:jdbc:postgresql://localhost:5432/linkyou?currentSchema=public} - username: ${DB_USERNAME:postgres} - password: ${DB_PASSWORD:nerosoft.8888}