diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/events/AuthorizationExitEvent.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/events/AuthorizationExitEvent.java index d4515b227..f96287802 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/events/AuthorizationExitEvent.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/events/AuthorizationExitEvent.java @@ -19,6 +19,7 @@ package org.hswebframework.web.authorization.events; import org.hswebframework.web.authorization.Authentication; +import org.hswebframework.web.event.DefaultAsyncEvent; import org.springframework.context.ApplicationEvent; /** @@ -26,14 +27,13 @@ import org.springframework.context.ApplicationEvent; * * @author zhouhao */ -public class AuthorizationExitEvent extends ApplicationEvent implements AuthorizationEvent { +public class AuthorizationExitEvent extends DefaultAsyncEvent implements AuthorizationEvent { private static final long serialVersionUID = -4590245933665047280L; - private Authentication authentication; + private final Authentication authentication; public AuthorizationExitEvent(Authentication authentication) { - super(authentication); this.authentication = authentication; } diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/ReactiveTokenAuthenticationSupplier.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/ReactiveTokenAuthenticationSupplier.java index bd7b59516..97efdb45a 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/ReactiveTokenAuthenticationSupplier.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/ReactiveTokenAuthenticationSupplier.java @@ -20,13 +20,15 @@ public class ReactiveTokenAuthenticationSupplier implements ReactiveAuthenticati @Override public Mono get() { - return ContextUtils.reactiveContext() - .flatMap(context -> - context.get(ContextKey.of(ParsedToken.class)) - .map(t -> tokenManager.getByToken(t.getToken())) - .orElseGet(Mono::empty)) - .flatMap(auth -> ReactiveLogger.mdc("userId", auth.getUser().getId()) - .then(ReactiveLogger.mdc("username", auth.getUser().getName())) + return ContextUtils + .reactiveContext() + .flatMap(context -> context + .get(ContextKey.of(ParsedToken.class)) + .map(t -> tokenManager.getByToken(t.getToken())) + .orElseGet(Mono::empty)) + .flatMap(auth -> ReactiveLogger + .mdc("userId", auth.getUser().getId(), + "username", auth.getUser().getName()) .thenReturn(auth)); } } diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenManager.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenManager.java index 897bc3d43..7781683f8 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenManager.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenManager.java @@ -97,7 +97,7 @@ public interface UserTokenManager { * @param userId userId * @param state 状态 * @see org.hswebframework.web.authorization.token.event.UserTokenChangedEvent - * @see this#changeTokenState + * @see UserTokenManager#changeTokenState */ Mono changeUserState(String userId, TokenState state); diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenReactiveAuthenticationSupplier.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenReactiveAuthenticationSupplier.java index f43423ad1..ae8b3d853 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenReactiveAuthenticationSupplier.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenReactiveAuthenticationSupplier.java @@ -68,20 +68,18 @@ public class UserTokenReactiveAuthenticationSupplier implements ReactiveAuthenti @Override public Mono get() { - return ContextUtils.reactiveContext() - .flatMap(context -> - context.get(ContextKey.of(ParsedToken.class)) - .map(t -> userTokenManager - .getByToken(t.getToken()) - .filter(UserToken::validate)) - .map(tokenMono -> tokenMono - .flatMap(token -> userTokenManager - .touch(token.getToken()) - .thenReturn(token)) - .flatMap(token -> get(thirdPartAuthenticationManager.get(token.getType()), token.getUserId()))) - .orElseGet(Mono::empty)) - .flatMap(auth -> ReactiveLogger.mdc("userId", auth.getUser().getId()) - .then(ReactiveLogger.mdc("username", auth.getUser().getName())) + return ContextUtils + .reactiveContext() + .flatMap(context -> context + .get(ContextKey.of(ParsedToken.class)) + .map(t -> userTokenManager.getByToken(t.getToken()).filter(UserToken::validate)) + .map(tokenMono -> tokenMono + .flatMap(token -> userTokenManager.touch(token.getToken()).thenReturn(token)) + .flatMap(token -> get(thirdPartAuthenticationManager.get(token.getType()), token.getUserId()))) + .orElseGet(Mono::empty)) + .flatMap(auth -> ReactiveLogger + .mdc("userId", auth.getUser().getId(), + "username", auth.getUser().getName()) .thenReturn(auth)) ; diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/configuration/AuthorizingHandlerAutoConfiguration.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/configuration/AuthorizingHandlerAutoConfiguration.java index 9f909b20c..50e4510a9 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/configuration/AuthorizingHandlerAutoConfiguration.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/configuration/AuthorizingHandlerAutoConfiguration.java @@ -27,6 +27,7 @@ import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import javax.annotation.Nonnull; import java.util.List; /** @@ -85,6 +86,11 @@ public class AuthorizingHandlerAutoConfiguration { return new UserOnSignOut(userTokenManager); } + @SuppressWarnings("all") + @ConfigurationProperties(prefix = "hsweb.authorize.token.default") + public ServletUserTokenGenPar servletUserTokenGenPar(){ + return new ServletUserTokenGenPar(); + } @Bean @ConditionalOnMissingBean(UserTokenParser.class) @@ -101,11 +107,10 @@ public class AuthorizingHandlerAutoConfiguration { @ConditionalOnProperty(prefix = "hsweb.authorize.two-factor", name = "enable", havingValue = "true") @Order(100) public WebMvcConfigurer twoFactorHandlerConfigurer(TwoFactorValidatorManager manager) { - return new WebMvcConfigurerAdapter() { + return new WebMvcConfigurer() { @Override - public void addInterceptors(InterceptorRegistry registry) { + public void addInterceptors(@Nonnull InterceptorRegistry registry) { registry.addInterceptor(new TwoFactorHandlerInterceptorAdapter(manager)); - super.addInterceptors(registry); } }; } diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/AuthorizedToken.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/AuthorizedToken.java index e4502a610..f53a74868 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/AuthorizedToken.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/AuthorizedToken.java @@ -3,7 +3,7 @@ package org.hswebframework.web.authorization.basic.web; import org.hswebframework.web.authorization.token.ParsedToken; /** - * 已完成认证的令牌,如果返回此令牌,将直接使用{@link this#getUserId()}来绑定用户信息 + * 已完成认证的令牌,如果返回此令牌,将直接使用{@link AuthorizedToken#getUserId()}来绑定用户信息 * * @author zhouhao */ diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/ServletUserTokenGenPar.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/ServletUserTokenGenPar.java new file mode 100644 index 000000000..597c359ca --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/ServletUserTokenGenPar.java @@ -0,0 +1,66 @@ +package org.hswebframework.web.authorization.basic.web; + +import lombok.Getter; +import lombok.Setter; +import org.hswebframework.web.authorization.Authentication; +import org.hswebframework.web.authorization.token.ParsedToken; +import org.hswebframework.web.id.IDGenerator; +import org.springframework.util.StringUtils; + +import javax.servlet.http.HttpServletRequest; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +@Getter +@Setter +public class ServletUserTokenGenPar implements UserTokenParser, UserTokenGenerator { + private long timeout = TimeUnit.MINUTES.toMillis(30); + + private String headerName = "X-Access-Token"; + + @Override + public String getSupportTokenType() { + return "default"; + } + + + @Override + public GeneratedToken generate(Authentication authentication) { + String token = IDGenerator.MD5.generate(); + + return new GeneratedToken() { + @Override + public Map getResponse() { + return Collections.singletonMap("expires", timeout); + } + + @Override + public String getToken() { + return token; + } + + @Override + public String getType() { + return getSupportTokenType(); + } + + @Override + public long getTimeout() { + return timeout; + } + }; + } + + @Override + public ParsedToken parseToken(HttpServletRequest request) { + String token = Optional + .ofNullable(request.getHeader(headerName)) + .orElseGet(() -> request.getParameter(":X_Access_Token")); + if (StringUtils.hasText(token)) { + return ParsedToken.of(getSupportTokenType(), token); + } + return null; + } +} diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserOnSignOut.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserOnSignOut.java index 5f186f827..51eaed077 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserOnSignOut.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserOnSignOut.java @@ -5,12 +5,13 @@ import org.hswebframework.web.authorization.token.UserToken; import org.hswebframework.web.authorization.token.UserTokenHolder; import org.hswebframework.web.authorization.token.UserTokenManager; import org.springframework.context.ApplicationListener; +import org.springframework.context.event.EventListener; /** * @author zhouhao */ -public class UserOnSignOut implements ApplicationListener { - private UserTokenManager userTokenManager; +public class UserOnSignOut { + private final UserTokenManager userTokenManager; public UserOnSignOut(UserTokenManager userTokenManager) { this.userTokenManager = userTokenManager; @@ -21,8 +22,8 @@ public class UserOnSignOut implements ApplicationListener filter(@NonNull ServerWebExchange exchange, WebFilterChain chain) { - return chain.filter(exchange) - .subscriberContext(ContextUtils.acceptContext(ctx -> - Flux.fromIterable(parsers) - .flatMap(parser -> parser.parseToken(exchange)) - .subscribe(token -> ctx.put(ParsedToken.class, token)))) - .subscriberContext(ReactiveLogger.start("requestId", exchange.getRequest().getId())) - ; + return Flux + .fromIterable(parsers) + .flatMap(parser -> parser.parseToken(exchange)) + .next() + .map(token -> chain + .filter(exchange) + .subscriberContext( + ContextUtils.acceptContext( + context -> context.put(ParsedToken.class, token) + ) + )) + .defaultIfEmpty(chain.filter(exchange)) + .flatMap(Function.identity()) + .subscriberContext(ReactiveLogger.start("requestId", exchange.getRequest().getId())); + +// return chain.filter(exchange) +// .subscriberContext(ContextUtils.acceptContext(ctx -> +// Flux.fromIterable(parsers) +// .flatMap(parser -> parser.parseToken(exchange)) +// .subscribe(token -> ctx.put(ParsedToken.class, token))) +// ) +// .subscriberContext(ReactiveLogger.start("requestId", exchange.getRequest().getId())) } @EventListener @@ -60,14 +76,17 @@ public class UserTokenWebFilter implements WebFilter, BeanPostProcessor { if (StringUtils.hasText(token.getToken())) { event.getResult().put("token", token.getToken()); long expires = event.getParameter("expires") - .map(String::valueOf) - .map(Long::parseLong) - .orElse(token.getTimeout()); + .map(String::valueOf) + .map(Long::parseLong) + .orElse(token.getTimeout()); event.getResult().put("expires", expires); event.async(userTokenManager - .signIn(token.getToken(), token.getType(), event.getAuthentication().getUser().getId(), expires) - .doOnNext(t -> log.debug("user [{}] sign in", t.getUserId())) - .then()); + .signIn(token.getToken(), token.getType(), event + .getAuthentication() + .getUser() + .getId(), expires) + .doOnNext(t -> log.debug("user [{}] sign in", t.getUserId())) + .then()); } } 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 1ccb862a1..dc882e7e1 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 @@ -22,27 +22,31 @@ import java.util.stream.Collectors; */ public class WebUserTokenInterceptor extends HandlerInterceptorAdapter { - private UserTokenManager userTokenManager; + private final UserTokenManager userTokenManager; - private List userTokenParser; + private final List userTokenParser; - private AopMethodAuthorizeDefinitionParser parser; + private final AopMethodAuthorizeDefinitionParser parser; - private boolean enableBasicAuthorization = false; + private final boolean enableBasicAuthorization; - public WebUserTokenInterceptor(UserTokenManager userTokenManager, List userTokenParser, AopMethodAuthorizeDefinitionParser definitionParser) { + public WebUserTokenInterceptor(UserTokenManager userTokenManager, + List userTokenParser, + AopMethodAuthorizeDefinitionParser definitionParser) { this.userTokenManager = userTokenManager; this.userTokenParser = userTokenParser; this.parser = definitionParser; - enableBasicAuthorization = userTokenParser.stream() + enableBasicAuthorization = userTokenParser + .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() + List tokens = userTokenParser + .stream() .map(parser -> parser.parseToken(request)) .filter(Objects::nonNull) .collect(Collectors.toList()); @@ -68,7 +72,8 @@ public class WebUserTokenInterceptor extends HandlerInterceptorAdapter { userTokenManager.signOutByToken(token).subscribe(); userToken = userTokenManager - .signIn(parsedToken.getToken(), parsedToken.getType(), ((AuthorizedToken) parsedToken).getUserId(), ((AuthorizedToken) parsedToken).getMaxInactiveInterval()) + .signIn(parsedToken.getToken(), parsedToken.getType(), ((AuthorizedToken) parsedToken).getUserId(), ((AuthorizedToken) parsedToken) + .getMaxInactiveInterval()) .block(); } if (null != userToken) { diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/AccessTokenManager.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/AccessTokenManager.java index 1645357d3..e90793c6c 100644 --- a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/AccessTokenManager.java +++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/AccessTokenManager.java @@ -3,14 +3,41 @@ package org.hswebframework.web.oauth2.server; import org.hswebframework.web.authorization.Authentication; import reactor.core.publisher.Mono; +/** + * OAuth2 AccessToken管理器,用于创建,刷新token以及根据token获取权限信息 + * + * @author zhouhao + * @since 4.0.7 + */ public interface AccessTokenManager { + /** + * 根据token获取权限信息 + * + * @param accessToken accessToken + * @return 权限信息 + */ Mono getAuthenticationByToken(String accessToken); + /** + * 根据ClientId以及权限信息创建token + * + * @param clientId clientId {@link OAuth2Client#getClientId()} + * @param authentication 权限信息 + * @param singleton 是否单例,如果为true,重复创建token将返回首次创建的token + * @return + */ Mono createAccessToken(String clientId, Authentication authentication, boolean singleton); + /** + * 刷新token + * + * @param clientId clientId {@link OAuth2Client#getClientId()} + * @param refreshToken refreshToken + * @return 新的token + */ Mono refreshAccessToken(String clientId, String refreshToken); } diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/auth/ReactiveOAuth2AccessTokenParser.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/auth/ReactiveOAuth2AccessTokenParser.java index 7f1ab25a8..dedcfe752 100644 --- a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/auth/ReactiveOAuth2AccessTokenParser.java +++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/auth/ReactiveOAuth2AccessTokenParser.java @@ -47,15 +47,16 @@ public class ReactiveOAuth2AccessTokenParser implements ReactiveUserTokenParser, @Override public Mono get() { - return ContextUtils.reactiveContext() - .flatMap(context -> - context.get(ContextKey.of(ParsedToken.class)) - .filter(token -> "oauth2".equals(token.getType())) - .map(t -> accessTokenManager - .getAuthenticationByToken(t.getToken())) - .orElse(Mono.empty())) - .flatMap(auth -> ReactiveLogger.mdc("userId", auth.getUser().getId()) - .then(ReactiveLogger.mdc("username", auth.getUser().getName())) + return ContextUtils + .reactiveContext() + .flatMap(context -> context + .get(ContextKey.of(ParsedToken.class)) + .filter(token -> "oauth2".equals(token.getType())) + .map(t -> accessTokenManager.getAuthenticationByToken(t.getToken())) + .orElse(Mono.empty())) + .flatMap(auth -> ReactiveLogger + .mdc("userId", auth.getUser().getId(), + "username", auth.getUser().getName()) .thenReturn(auth)); } } diff --git a/hsweb-core/src/main/java/org/hswebframework/web/context/ContextUtils.java b/hsweb-core/src/main/java/org/hswebframework/web/context/ContextUtils.java index 6640aa5f9..256ee95ee 100644 --- a/hsweb-core/src/main/java/org/hswebframework/web/context/ContextUtils.java +++ b/hsweb-core/src/main/java/org/hswebframework/web/context/ContextUtils.java @@ -18,11 +18,12 @@ public class ContextUtils { } public static Mono reactiveContext() { - return Mono.subscriberContext() + return Mono + .subscriberContext() .handle((context, sink) -> { if (context.hasKey(Context.class)) { sink.next(context.get(Context.class)); - }else { + } else { sink.complete(); } }) diff --git a/hsweb-core/src/main/java/org/hswebframework/web/logger/ReactiveLogger.java b/hsweb-core/src/main/java/org/hswebframework/web/logger/ReactiveLogger.java index 64361ae80..572adc5e8 100644 --- a/hsweb-core/src/main/java/org/hswebframework/web/logger/ReactiveLogger.java +++ b/hsweb-core/src/main/java/org/hswebframework/web/logger/ReactiveLogger.java @@ -13,17 +13,32 @@ import java.util.function.Function; @Slf4j public class ReactiveLogger { - private static String CONTEXT_KEY = ReactiveLogger.class.getName(); + private static final String CONTEXT_KEY = ReactiveLogger.class.getName(); public static Function start(String key, String value) { return start(Collections.singletonMap(key, value)); } + public static Function start(String... keyAndValue) { + Map map = new HashMap<>(); + for (int i = 0, len = keyAndValue.length / 2; i < len; i++) { + map.put(keyAndValue[i * 2], keyAndValue[i * 2 + 1]); + } + return start(map); + } + public static Mono mdc(String key, String value) { - return Mono.empty() + return Mono + .empty() .subscriberContext(start(key, value)); } + public static Mono mdc(String... keyAndValue) { + return Mono + .empty() + .subscriberContext(start(keyAndValue)); + } + public static Function start(Map context) { return ctx -> { Optional> maybeContextMap = ctx.getOrEmpty(CONTEXT_KEY); @@ -74,7 +89,8 @@ public class ReactiveLogger { } public static Mono mdc(Consumer> consumer) { - return Mono.subscriberContext() + return Mono + .subscriberContext() .doOnNext(ctx -> { Optional> maybeContextMap = ctx.getOrEmpty(CONTEXT_KEY); if (maybeContextMap.isPresent()) { diff --git a/hsweb-core/src/test/java/org/hswebframework/web/logger/ReactiveLoggerTest.java b/hsweb-core/src/test/java/org/hswebframework/web/logger/ReactiveLoggerTest.java index 8a12e8ee4..135a7ea9b 100644 --- a/hsweb-core/src/test/java/org/hswebframework/web/logger/ReactiveLoggerTest.java +++ b/hsweb-core/src/test/java/org/hswebframework/web/logger/ReactiveLoggerTest.java @@ -2,6 +2,7 @@ package org.hswebframework.web.logger; import lombok.extern.slf4j.Slf4j; import org.junit.Test; +import org.slf4j.MDC; import reactor.core.publisher.Flux; import reactor.test.StepVerifier; @@ -17,12 +18,12 @@ public class ReactiveLoggerTest { public void test() { Flux.range(0, 5) - .delayElements(Duration.ofSeconds(1)) .flatMap(i -> ReactiveLogger.mdc("requestId", "test").thenReturn(i)) .doOnEach(ReactiveLogger.onNext(v -> { - log.info("test:{}", v); + + log.info("test:{} {}", v, MDC.getCopyOfContextMap()); })) - .subscriberContext(ReactiveLogger.start("r", "1")) + .subscriberContext(ReactiveLogger.start("r", "1","t","1")) .as(StepVerifier::create) .expectNextCount(5) .verifyComplete();