mirror of
https://github.com/hs-web/hsweb-framework.git
synced 2026-05-31 09:53:21 +08:00
代码结构优化
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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))
|
||||
;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user