Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions framework/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@
<artifactId>spring-web</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package io.theurl.framework.application;

import com.neroyun.mediator.Mediator;
import io.theurl.framework.security.CredentialExpiredException;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.web.context.annotation.RequestScope;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import jakarta.servlet.http.HttpServletRequest;

import java.security.Principal;
import java.util.concurrent.CompletionException;
import java.util.function.Consumer;

Expand All @@ -28,4 +33,24 @@ protected void handleException(Throwable throwable, Consumer<Throwable> consumer
consumer.accept(throwable);
}
}

protected HttpServletRequest getRequest() {
return ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
}

protected Principal currentUser() {
var request = getRequest();
if (request == null) {
return null;
}
return request.getUserPrincipal();
}

protected Long currentUserId() {
var principal = currentUser();
if (principal == null || principal.getName() == null || principal.getName().isBlank()) {
throw new CredentialExpiredException(null, "Unauthenticated request.");
}
return Long.parseLong(principal.getName());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.theurl.framework.configure;

import org.springframework.core.task.TaskDecorator;
import org.springframework.web.context.request.RequestContextHolder;

@SuppressWarnings("NullableProblems")
public class ContextCopyingDecorator implements TaskDecorator {

@Override
public Runnable decorate(Runnable runnable) {
var attributes = RequestContextHolder.getRequestAttributes();

return () -> {
try {
RequestContextHolder.setRequestAttributes(attributes);
runnable.run();
} finally {
RequestContextHolder.resetRequestAttributes();
}
};
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.theurl.framework.configure;

import com.neroyun.mediator.*;
import org.slf4j.MDC;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
Expand All @@ -12,7 +13,9 @@
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.RequestContextListener;

import java.util.Map;
import java.util.concurrent.CompletableFuture;

@SuppressWarnings("rawtypes")
Expand All @@ -34,31 +37,16 @@ public ThreadPoolTaskExecutor taskExecutor() {
executor.setCorePoolSize(8);
executor.setMaxPoolSize(32);
executor.setQueueCapacity(200);
executor.setTaskDecorator(copyRequestContextDecorator());
executor.setTaskDecorator(new ContextCopyingDecorator());
executor.initialize();
return executor;
}

private TaskDecorator copyRequestContextDecorator() {
return runnable -> {
RequestAttributes context = RequestContextHolder.getRequestAttributes();
return () -> {
try {
if (context != null) {
RequestContextHolder.setRequestAttributes(context);
}
runnable.run();
} finally {
RequestContextHolder.resetRequestAttributes();
}
};
};
}

@Bean(name = "applicationEventMulticaster")
public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
multicaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
multicaster.setTaskExecutor(taskExecutor());
//multicaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
return multicaster;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.theurl.framework.configure;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.context.request.RequestContextListener;
import org.springframework.web.filter.RequestContextFilter;

@Configuration
public class WebConfiguration {
@Bean
public ServletListenerRegistrationBean<RequestContextListener> requestContextListenerRegistration() {
ServletListenerRegistrationBean<RequestContextListener> registrationBean = new ServletListenerRegistrationBean<>();
registrationBean.setListener(new RequestContextListener());
return registrationBean;
}

@Bean
public FilterRegistrationBean<RequestContextFilter> requestContextFilterRegistration() {
RequestContextFilter filter = new RequestContextFilter();
// Force the request context to pass down to asynchronous child threads
filter.setThreadContextInheritable(true);

FilterRegistrationBean<RequestContextFilter> registration = new FilterRegistrationBean<>(filter);
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registration;
}
}
43 changes: 43 additions & 0 deletions framework/src/main/java/io/theurl/framework/core/BeanScope.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,54 @@
package io.theurl.framework.core;

/**
* Defines the scopes of a bean in the Spring IoC container.
* <p>>
* The scopes are defined as constants in this class, and can be used to specify the scope of a bean when defining it in the Spring configuration.
* <p>>
* The scopes defined in this class are:
* <ul>
* <li>{@link #APPLICATION}: Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.</li>
* <li>{@link #PROTOTYPE}: Scopes a single bean definition to any number of object instances.</li>
* <li>{@link #REQUEST}: Scopes a single bean definition to the lifecycle of a single HTTP request. Only valid in the context of a web-aware Spring ApplicationContext.</li>
* <li>{@link #SESSION}: Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.</li>
* <li>{@link #SINGLETON}: Scopes a single bean definition to a single object instance for each Spring IoC container.</li>
* <li>{@link #WEB_SOCKET}: Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.</li>
* </ul>
*/
public class BeanScope {
/**
* Scopes a single bean definition to the lifecycle of a ServletContext.
* Only valid in the context of a web-aware Spring ApplicationContext.
*/
public final static String APPLICATION = "application";

/**
* Scopes a single bean definition to any number of object instances.
*/
public final static String PROTOTYPE = "prototype";

/**
* Scopes a single bean definition to the lifecycle of a single HTTP request.
* That is, each HTTP request has its own instance of a bean created off the back of a single bean definition.
* Only valid in the context of a web-aware Spring ApplicationContext.
*/
public final static String REQUEST = "request";

/**
* Scopes a single bean definition to the lifecycle of an HTTP Session.
* Only valid in the context of a web-aware Spring ApplicationContext.
*/
public final static String SESSION = "session";

/**
* Scopes a single bean definition to a single object instance for each Spring IoC container.
*/
public final static String SINGLETON = "singleton";

/**
* Scopes a single bean definition to the lifecycle of a WebSocket.
* Only valid in the context of a web-aware Spring ApplicationContext.
*/
public final static String WEB_SOCKET = "websocket";
// APPLICATION("application"),
// PROTOTYPE("prototype"),
Expand Down
9 changes: 9 additions & 0 deletions identity/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
Expand Down Expand Up @@ -95,6 +99,11 @@
<artifactId>spring-boot-starter-data-jpa-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-data-redis-test</artifactId>-->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.theurl.identity.application.command;

import com.neroyun.mediator.Command;
import lombok.Data;

import java.time.LocalDateTime;

@Data
public final class AuthlogCreateCommand implements Command {
private Long userId;
private String username;
private String grantType;
private String requestId;
private String ipAddress;
private String userAgent;
private String referrer;
private String appName;
private String appVersion;
private String osPlatform;
private String source;
private boolean success;
private String remark;
private LocalDateTime timestamp;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
package io.theurl.identity.application.dto;

import lombok.Data;

import java.util.Collection;

@Data
public class UserProfileResponseDto {
private Long id;
private String username;
private String nickname;
private String email;
private String phone;
private Collection<String> roles;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.theurl.identity.application.handler;

import com.neroyun.mediator.Handler;
import io.theurl.framework.core.BeanScope;
import io.theurl.identity.application.command.AuthlogCreateCommand;
import io.theurl.identity.domain.repository.AuthlogRepository;
import io.theurl.identity.domain.aggregate.Authlog;
import org.modelmapper.ModelMapper;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;

import java.util.concurrent.CompletableFuture;

@Component
@Scope(value = BeanScope.REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class AuthlogCreateCommandHandler implements Handler<AuthlogCreateCommand, Void> {

private final AuthlogRepository repository;
private final ModelMapper mapper;

public AuthlogCreateCommandHandler(AuthlogRepository repository, ModelMapper mapper) {
this.repository = repository;
this.mapper = mapper;
}

@Override
public CompletableFuture<Void> handleAsync(AuthlogCreateCommand message) {

var authlog = Authlog.create(message.getRequestId(), message.getUsername(), message.isSuccess());
mapper.map(message, authlog);
repository.save(authlog);
return CompletableFuture.completedFuture(null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
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 java.util.concurrent.CompletableFuture;

@Component
@Scope(BeanScope.PROTOTYPE)
//@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
@Scope(value = BeanScope.REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class TokenCreateCommandHandler implements Handler<TokenCreateCommand, Void> {

@Resource
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.theurl.identity.application.handler;

import com.neroyun.mediator.Handler;
import io.theurl.framework.core.BeanScope;
import io.theurl.identity.application.command.UserAccessFailureCountCommand;
import io.theurl.identity.domain.repository.UserRepository;
import org.springframework.context.annotation.Scope;
Expand All @@ -11,7 +12,7 @@
import java.util.concurrent.CompletableFuture;

@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
@Scope(value = BeanScope.REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserAccessFailureCountCommandHandler implements Handler<UserAccessFailureCountCommand, Void> {

private final UserRepository repository;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.theurl.identity.application.handler;

import com.neroyun.mediator.Handler;
import io.theurl.framework.core.BeanScope;
import io.theurl.identity.application.command.UserCreateCommand;
import io.theurl.identity.domain.aggregate.User;
import io.theurl.identity.domain.repository.UserRepository;
Expand All @@ -14,7 +15,7 @@
import java.util.concurrent.CompletableFuture;

@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
@Scope(value = BeanScope.REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserCreateCommandHandler implements Handler<UserCreateCommand, Void> {
private final UserRepository repository;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.theurl.identity.application.handler;

import com.neroyun.mediator.Handler;
import io.theurl.framework.core.BeanScope;
import io.theurl.identity.application.command.UserPasswordChangeCommand;
import io.theurl.identity.domain.repository.UserRepository;
import org.springframework.context.annotation.Scope;
Expand All @@ -11,7 +12,7 @@
import java.util.concurrent.CompletableFuture;

@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
@Scope(value = BeanScope.REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserPasswordChangeCommandHandler implements Handler<UserPasswordChangeCommand, Void> {

private final UserRepository repository;
Expand Down
Loading
Loading