From ec5b4899d6280fc756197ec158100c1e9d3851e3 Mon Sep 17 00:00:00 2001 From: zhouhao Date: Mon, 4 Aug 2025 10:12:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=B8=8A=E4=B8=8B?= =?UTF-8?q?=E6=96=87=E4=BC=A0=E9=80=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hsweb-authorization-api/pom.xml | 6 +++ .../authorization/AuthenticationHolder.java | 14 ++++++- .../AuthenticationThreadLocalAccessor.java | 33 ++++++++++++++++ .../io.micrometer.context.ThreadLocalAccessor | 1 + ...AuthenticationThreadLocalAccessorTest.java | 38 +++++++++++++++++++ .../basic/web/WebUserTokenInterceptor.java | 37 +++++++++++------- .../web/context/ContextHolder.java | 7 +++- .../hswebframework/web/i18n/LocaleUtils.java | 7 ++-- 8 files changed, 122 insertions(+), 21 deletions(-) create mode 100644 hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/context/AuthenticationThreadLocalAccessor.java create mode 100644 hsweb-authorization/hsweb-authorization-api/src/main/resources/META-INF/services/io.micrometer.context.ThreadLocalAccessor create mode 100644 hsweb-authorization/hsweb-authorization-api/src/test/java/org/hswebframework/web/authorization/context/AuthenticationThreadLocalAccessorTest.java diff --git a/hsweb-authorization/hsweb-authorization-api/pom.xml b/hsweb-authorization/hsweb-authorization-api/pom.xml index 2ed5394e4..2a3622b63 100644 --- a/hsweb-authorization/hsweb-authorization-api/pom.xml +++ b/hsweb-authorization/hsweb-authorization-api/pom.xml @@ -52,5 +52,11 @@ jakarta.servlet-api true + + + io.micrometer + context-propagation + true + \ No newline at end of file diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/AuthenticationHolder.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/AuthenticationHolder.java index a8d9e9d5b..72ca5df55 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/AuthenticationHolder.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/AuthenticationHolder.java @@ -109,6 +109,18 @@ public final class AuthenticationHolder { } } + public static void resetCurrent() { + CURRENT.remove(); + } + + public static void makeCurrent(Authentication authentication) { + if (authentication == null) { + resetCurrent(); + } else { + CURRENT.set(authentication); + } + } + /** * 指定用户权限,执行一个任务。任务执行过程中可通过 {@link Authentication#current()}获取到当前权限. * @@ -119,7 +131,7 @@ public final class AuthenticationHolder { */ @SneakyThrows public static T executeWith(Authentication current, Callable callable) { - Authentication previous = CURRENT.get(); + Authentication previous = CURRENT.getIfExists(); try { CURRENT.set(current); return callable.call(); diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/context/AuthenticationThreadLocalAccessor.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/context/AuthenticationThreadLocalAccessor.java new file mode 100644 index 000000000..b8f4981a8 --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/context/AuthenticationThreadLocalAccessor.java @@ -0,0 +1,33 @@ +package org.hswebframework.web.authorization.context; + +import io.micrometer.context.ThreadLocalAccessor; +import org.hswebframework.web.authorization.Authentication; +import org.hswebframework.web.authorization.AuthenticationHolder; + +import javax.annotation.Nonnull; + +public class AuthenticationThreadLocalAccessor implements ThreadLocalAccessor { + + static final String KEY = "cp.hs.auth"; + + @Override + @Nonnull + public Object key() { + return KEY; + } + + @Override + public Authentication getValue() { + return AuthenticationHolder.get().orElse(null); + } + + @Override + public void setValue() { + AuthenticationHolder.resetCurrent(); + } + + @Override + public void setValue(@Nonnull Authentication value) { + AuthenticationHolder.makeCurrent(value); + } +} diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/resources/META-INF/services/io.micrometer.context.ThreadLocalAccessor b/hsweb-authorization/hsweb-authorization-api/src/main/resources/META-INF/services/io.micrometer.context.ThreadLocalAccessor new file mode 100644 index 000000000..b4c130e3a --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-api/src/main/resources/META-INF/services/io.micrometer.context.ThreadLocalAccessor @@ -0,0 +1 @@ +org.hswebframework.web.authorization.context.AuthenticationThreadLocalAccessor \ No newline at end of file diff --git a/hsweb-authorization/hsweb-authorization-api/src/test/java/org/hswebframework/web/authorization/context/AuthenticationThreadLocalAccessorTest.java b/hsweb-authorization/hsweb-authorization-api/src/test/java/org/hswebframework/web/authorization/context/AuthenticationThreadLocalAccessorTest.java new file mode 100644 index 000000000..62b0e46b6 --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-api/src/test/java/org/hswebframework/web/authorization/context/AuthenticationThreadLocalAccessorTest.java @@ -0,0 +1,38 @@ +package org.hswebframework.web.authorization.context; + +import org.hswebframework.web.authorization.Authentication; +import org.hswebframework.web.authorization.AuthenticationHolder; +import org.hswebframework.web.authorization.simple.SimpleAuthentication; +import org.junit.jupiter.api.Test; +import reactor.core.publisher.Hooks; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; + +import static org.junit.jupiter.api.Assertions.*; + +class AuthenticationThreadLocalAccessorTest { + + + @Test + void testReadFromReactive() { + + Hooks.enableAutomaticContextPropagation(); + + Authentication auth = new SimpleAuthentication(); + + Authentication auth2 = AuthenticationHolder.executeWith( + auth, + () -> Mono + .fromCallable(() -> { + // cross context + return Authentication.current().orElse(null); + }) + .subscribeOn(Schedulers.boundedElastic()) + .contextWrite(c->c) + .block()); + + assertEquals(auth, auth2); + + + } +} \ No newline at end of file diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/WebUserTokenInterceptor.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/WebUserTokenInterceptor.java index bc9b5832b..b1e37eebe 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/WebUserTokenInterceptor.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/WebUserTokenInterceptor.java @@ -39,22 +39,21 @@ public class WebUserTokenInterceptor implements HandlerInterceptor { this.parser = definitionParser; enableBasicAuthorization = userTokenParser - .stream() - .filter(UserTokenForTypeParser.class::isInstance) - .anyMatch(parser -> "basic".equalsIgnoreCase(((UserTokenForTypeParser) parser).getTokenType())); + .stream() + .filter(UserTokenForTypeParser.class::isInstance) + .anyMatch(parser -> "basic".equalsIgnoreCase(((UserTokenForTypeParser) parser).getTokenType())); } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { List tokens = userTokenParser - .stream() - .map(parser -> parser.parseToken(request)) - .filter(Objects::nonNull) - .collect(Collectors.toList()); + .stream() + .map(parser -> parser.parseToken(request)) + .filter(Objects::nonNull) + .toList(); if (tokens.isEmpty()) { - if (enableBasicAuthorization && handler instanceof HandlerMethod) { - HandlerMethod method = ((HandlerMethod) handler); + if (enableBasicAuthorization && handler instanceof HandlerMethod method) { AuthorizeDefinition definition = parser.parse(method.getBeanType(), method.getMethod()); if (null != definition) { response.addHeader("WWW-Authenticate", " Basic realm=\"\""); @@ -69,12 +68,18 @@ public class WebUserTokenInterceptor implements HandlerInterceptor { userToken = userTokenManager.getByToken(token).blockOptional().orElse(null); } if ((userToken == null || userToken.isExpired()) && parsedToken instanceof AuthorizedToken) { - //先踢出旧token - userTokenManager.signOutByToken(token).subscribe(); + userToken = + userTokenManager + .signOutByToken(token) + .then( + userTokenManager + .signIn(parsedToken.getToken(), + parsedToken.getType(), + ((AuthorizedToken) parsedToken).getUserId(), + ((AuthorizedToken) parsedToken) + .getMaxInactiveInterval()) + ) - userToken = userTokenManager - .signIn(parsedToken.getToken(), parsedToken.getType(), ((AuthorizedToken) parsedToken).getUserId(), ((AuthorizedToken) parsedToken) - .getMaxInactiveInterval()) .block(); } if (null != userToken) { @@ -85,4 +90,8 @@ public class WebUserTokenInterceptor implements HandlerInterceptor { return true; } + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { + UserTokenHolder.setCurrent(null); + } } diff --git a/hsweb-core/src/main/java/org/hswebframework/web/context/ContextHolder.java b/hsweb-core/src/main/java/org/hswebframework/web/context/ContextHolder.java index 8f8a01099..9b993a28b 100644 --- a/hsweb-core/src/main/java/org/hswebframework/web/context/ContextHolder.java +++ b/hsweb-core/src/main/java/org/hswebframework/web/context/ContextHolder.java @@ -48,8 +48,11 @@ public class ContextHolder { } } - public static Mono currentReactive(Function> handler) { - return Mono.deferContextual(ctx -> handler.apply(current().putAll(ctx))); + public static Mono wrap(Function> handler) { + return Mono.deferContextual(ctx -> { + Context context = current().putAll(ctx); + return handler.apply(context); + }); } public static Context current() { diff --git a/hsweb-core/src/main/java/org/hswebframework/web/i18n/LocaleUtils.java b/hsweb-core/src/main/java/org/hswebframework/web/i18n/LocaleUtils.java index 09f583b98..0477dfa5a 100644 --- a/hsweb-core/src/main/java/org/hswebframework/web/i18n/LocaleUtils.java +++ b/hsweb-core/src/main/java/org/hswebframework/web/i18n/LocaleUtils.java @@ -7,6 +7,7 @@ import org.hswebframework.web.exception.I18nSupportException; import org.reactivestreams.Publisher; import org.reactivestreams.Subscription; import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; import reactor.core.CoreSubscriber; import reactor.core.publisher.*; import reactor.util.context.Context; @@ -93,10 +94,8 @@ public final class LocaleUtils { */ public static Locale current() { Locale locale = CONTEXT_THREAD_LOCAL.get(); - if (locale == null) { - locale = DEFAULT_LOCALE; - } - return locale; + // fallback to spring + return Objects.requireNonNullElseGet(locale, LocaleContextHolder::getLocale); } /**