代码结构优化

This commit is contained in:
zhou-hao
2021-07-29 16:39:48 +08:00
parent 1a456899e2
commit ed5e3b0751
15 changed files with 213 additions and 71 deletions

View File

@@ -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;
}

View File

@@ -20,13 +20,15 @@ public class ReactiveTokenAuthenticationSupplier implements ReactiveAuthenticati
@Override
public Mono<Authentication> 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));
}
}

View File

@@ -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<Void> changeUserState(String userId, TokenState state);

View File

@@ -68,20 +68,18 @@ public class UserTokenReactiveAuthenticationSupplier implements ReactiveAuthenti
@Override
public Mono<Authentication> 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))
;

View File

@@ -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);
}
};
}

View File

@@ -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
*/

View File

@@ -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<String, Object> 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;
}
}

View File

@@ -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<AuthorizationExitEvent> {
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<AuthorizationExitEven
return null != token ? token.getToken() : "";
}
@Override
@EventListener
public void onApplicationEvent(AuthorizationExitEvent event) {
userTokenManager.signOutByToken(geToken());
event.async(userTokenManager.signOutByToken(geToken()));
}
}

View File

@@ -24,6 +24,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
@Component
@Slf4j
@@ -40,13 +41,28 @@ public class UserTokenWebFilter implements WebFilter, BeanPostProcessor {
@NonNull
public Mono<Void> 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());
}
}

View File

@@ -22,27 +22,31 @@ import java.util.stream.Collectors;
*/
public class WebUserTokenInterceptor extends HandlerInterceptorAdapter {
private UserTokenManager userTokenManager;
private final UserTokenManager userTokenManager;
private List<UserTokenParser> userTokenParser;
private final List<UserTokenParser> userTokenParser;
private AopMethodAuthorizeDefinitionParser parser;
private final AopMethodAuthorizeDefinitionParser parser;
private boolean enableBasicAuthorization = false;
private final boolean enableBasicAuthorization;
public WebUserTokenInterceptor(UserTokenManager userTokenManager, List<UserTokenParser> userTokenParser, AopMethodAuthorizeDefinitionParser definitionParser) {
public WebUserTokenInterceptor(UserTokenManager userTokenManager,
List<UserTokenParser> 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<ParsedToken> tokens = userTokenParser.stream()
List<ParsedToken> 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) {

View File

@@ -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<Authentication> getAuthenticationByToken(String accessToken);
/**
* 根据ClientId以及权限信息创建token
*
* @param clientId clientId {@link OAuth2Client#getClientId()}
* @param authentication 权限信息
* @param singleton 是否单例,如果为true,重复创建token将返回首次创建的token
* @return
*/
Mono<AccessToken> createAccessToken(String clientId,
Authentication authentication,
boolean singleton);
/**
* 刷新token
*
* @param clientId clientId {@link OAuth2Client#getClientId()}
* @param refreshToken refreshToken
* @return 新的token
*/
Mono<AccessToken> refreshAccessToken(String clientId, String refreshToken);
}

View File

@@ -47,15 +47,16 @@ public class ReactiveOAuth2AccessTokenParser implements ReactiveUserTokenParser,
@Override
public Mono<Authentication> 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));
}
}

View File

@@ -18,11 +18,12 @@ public class ContextUtils {
}
public static Mono<Context> reactiveContext() {
return Mono.subscriberContext()
return Mono
.subscriberContext()
.<Context>handle((context, sink) -> {
if (context.hasKey(Context.class)) {
sink.next(context.get(Context.class));
}else {
} else {
sink.complete();
}
})

View File

@@ -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<Context, Context> start(String key, String value) {
return start(Collections.singletonMap(key, value));
}
public static Function<Context, Context> start(String... keyAndValue) {
Map<String, String> 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<Void> mdc(String key, String value) {
return Mono.<Void>empty()
return Mono
.<Void>empty()
.subscriberContext(start(key, value));
}
public static Mono<Void> mdc(String... keyAndValue) {
return Mono
.<Void>empty()
.subscriberContext(start(keyAndValue));
}
public static Function<Context, Context> start(Map<String, String> context) {
return ctx -> {
Optional<Map<String, String>> maybeContextMap = ctx.getOrEmpty(CONTEXT_KEY);
@@ -74,7 +89,8 @@ public class ReactiveLogger {
}
public static Mono<Void> mdc(Consumer<Map<String, String>> consumer) {
return Mono.subscriberContext()
return Mono
.subscriberContext()
.doOnNext(ctx -> {
Optional<Map<String, String>> maybeContextMap = ctx.getOrEmpty(CONTEXT_KEY);
if (maybeContextMap.isPresent()) {

View File

@@ -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();