From 1e5c880fa9f5b9efbab0eec13675ae77a6246a38 Mon Sep 17 00:00:00 2001 From: zhou-hao Date: Wed, 22 Nov 2017 22:01:25 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=9D=83=E9=99=90=E6=8E=A7?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../listener/AuthorizationListener.java | 2 + .../AuthorizationListenerDispatcher.java | 25 ++- .../event/AbstractAuthorizationEvent.java | 5 +- .../listener/event/AuthorizationEvent.java | 1 + .../event/AuthorizationExitEvent.java | 4 +- .../event/AuthorizationSuccessEvent.java | 4 +- .../token/DefaultUserTokenManager.java | 160 +++++++++++++----- .../authorization/token/SimpleUserToken.java | 25 +++ .../authorization/token/UserTokenManager.java | 11 +- .../token/event/UserTokenChangedEvent.java | 23 +++ .../token/event/UserTokenCreatedEvent.java | 18 ++ ...nEvent.java => UserTokenRemovedEvent.java} | 8 +- .../hsweb-authorization-basic/pom.xml | 10 ++ .../authorization/basic/web/UserOnSignIn.java | 9 +- .../basic/web/UserOnSignOut.java | 17 +- .../basic/web/WebUserTokenInterceptor.java | 3 + .../RedisUserTokenManagerTests.java | 79 +++++++++ .../authorization/UserTokenManagerTests.java | 45 +++++ .../client/feign/FeignUserTokenManager.java | 4 + hsweb-examples/hsweb-examples-simple/pom.xml | 6 - .../web/example/simple/SpringBootExample.java | 1 + .../AuthorizationController.java | 17 +- .../authorization/UserTokenController.java | 67 ++++++++ 23 files changed, 465 insertions(+), 79 deletions(-) create mode 100644 hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/event/UserTokenChangedEvent.java create mode 100644 hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/event/UserTokenCreatedEvent.java rename hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/event/{UserSignInEvent.java => UserTokenRemovedEvent.java} (60%) create mode 100644 hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/RedisUserTokenManagerTests.java create mode 100644 hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/UserTokenManagerTests.java create mode 100644 hsweb-system/hsweb-system-authorization/hsweb-system-authorization-controller/src/main/java/org/hswebframework/web/controller/authorization/UserTokenController.java diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/AuthorizationListener.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/AuthorizationListener.java index ee1d479db..5ff18934d 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/AuthorizationListener.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/AuthorizationListener.java @@ -6,11 +6,13 @@ import org.hswebframework.web.authorization.listener.event.AuthorizationEvent; /** * 授权监听器,用于监听授权过程,以及自定义授权逻辑 + * 已弃用,请使用{@link org.springframework.context.ApplicationListener} * * @author zhouhao * @see AuthorizationEvent * @since 3.0 */ +@Deprecated public interface AuthorizationListener { void on(E event); } diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/AuthorizationListenerDispatcher.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/AuthorizationListenerDispatcher.java index 760468666..0ad5f54a6 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/AuthorizationListenerDispatcher.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/AuthorizationListenerDispatcher.java @@ -19,14 +19,22 @@ package org.hswebframework.web.authorization.listener; import org.hswebframework.web.authorization.listener.event.AuthorizationEvent; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; import java.util.*; /** + * {@link org.springframework.context.ApplicationEventPublisher} * @author zhouhao */ +@Deprecated public class AuthorizationListenerDispatcher { + @Autowired + private ApplicationEventPublisher eventPublisher; + + private Map, List> listenerStore = new HashMap<>(); public void addListener(Class eventClass, AuthorizationListener listener) { @@ -36,16 +44,19 @@ public class AuthorizationListenerDispatcher { @SuppressWarnings("unchecked") public int doEvent(Class eventType, E event) { - List> store = (List) listenerStore.get(eventType); - if (null != store) { - store.forEach(listener -> listener.on(event)); - return store.size(); - } - return 0; + eventPublisher.publishEvent(event); +// List> store = (List) listenerStore.get(eventType); +// if (null != store) { +// store.forEach(listener -> listener.on(event)); +// return store.size(); +// } + return 1; } @SuppressWarnings("unchecked") public int doEvent(E event) { - return doEvent((Class) event.getClass(), event); + eventPublisher.publishEvent(event); + return 1; + //return doEvent((Class) event.getClass(), event); } } diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/event/AbstractAuthorizationEvent.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/event/AbstractAuthorizationEvent.java index 99f2654cd..22d31265f 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/event/AbstractAuthorizationEvent.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/event/AbstractAuthorizationEvent.java @@ -19,6 +19,8 @@ package org.hswebframework.web.authorization.listener.event; +import org.springframework.context.ApplicationEvent; + import java.util.Optional; import java.util.function.Function; @@ -28,7 +30,7 @@ import java.util.function.Function; * @author zhouhao * @since 3.0 */ -public abstract class AbstractAuthorizationEvent implements AuthorizationEvent { +public abstract class AbstractAuthorizationEvent extends ApplicationEvent implements AuthorizationEvent { protected String username; protected String password; @@ -43,6 +45,7 @@ public abstract class AbstractAuthorizationEvent implements AuthorizationEvent { * @param parameterGetter 参数获取函数,用户获取授权时传入的参数 */ public AbstractAuthorizationEvent(String username, String password, Function parameterGetter) { + super(username + "/" + password); if (username == null || password == null || parameterGetter == null) { throw new NullPointerException(); } diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/event/AuthorizationEvent.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/event/AuthorizationEvent.java index de329d749..edf5ed7fa 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/event/AuthorizationEvent.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/event/AuthorizationEvent.java @@ -27,6 +27,7 @@ package org.hswebframework.web.authorization.listener.event; * @see AuthorizationBeforeEvent * @see AuthorizationDecodeEvent * @see AuthorizationExitEvent + * @see org.springframework.context.ApplicationEvent * @since 3.0 */ public interface AuthorizationEvent { diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/event/AuthorizationExitEvent.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/event/AuthorizationExitEvent.java index 3b304c0d5..4185b4ea2 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/event/AuthorizationExitEvent.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/event/AuthorizationExitEvent.java @@ -19,16 +19,18 @@ package org.hswebframework.web.authorization.listener.event; import org.hswebframework.web.authorization.Authentication; +import org.springframework.context.ApplicationEvent; /** * 退出登录事件 * * @author zhouhao */ -public class AuthorizationExitEvent implements AuthorizationEvent { +public class AuthorizationExitEvent extends ApplicationEvent implements AuthorizationEvent { private 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/listener/event/AuthorizationSuccessEvent.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/event/AuthorizationSuccessEvent.java index 466784ea5..78abcd97d 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/event/AuthorizationSuccessEvent.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/event/AuthorizationSuccessEvent.java @@ -19,6 +19,7 @@ package org.hswebframework.web.authorization.listener.event; import org.hswebframework.web.authorization.Authentication; +import org.springframework.context.ApplicationEvent; import java.util.HashMap; import java.util.Map; @@ -32,7 +33,7 @@ import java.util.function.Function; * @see Authentication * @since 3.0 */ -public class AuthorizationSuccessEvent implements AuthorizationEvent { +public class AuthorizationSuccessEvent extends ApplicationEvent implements AuthorizationEvent { private Authentication authentication; private Function parameterGetter; @@ -40,6 +41,7 @@ public class AuthorizationSuccessEvent implements AuthorizationEvent { private Map result = new HashMap<>(); public AuthorizationSuccessEvent(Authentication authentication, Function parameterGetter) { + super(authentication); this.authentication = authentication; this.parameterGetter = parameterGetter; } diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/DefaultUserTokenManager.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/DefaultUserTokenManager.java index f92de1324..46286023a 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/DefaultUserTokenManager.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/DefaultUserTokenManager.java @@ -18,12 +18,17 @@ package org.hswebframework.web.authorization.token; -import org.hswebframework.web.authorization.listener.AuthorizationListenerDispatcher; -import org.hswebframework.web.authorization.token.event.UserSignInEvent; +import org.hswebframework.web.authorization.token.event.UserTokenChangedEvent; +import org.hswebframework.web.authorization.token.event.UserTokenCreatedEvent; +import org.hswebframework.web.authorization.token.event.UserTokenRemovedEvent; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationEventPublisher; -import java.util.List; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.function.Consumer; import java.util.stream.Collectors; /** @@ -36,12 +41,21 @@ public class DefaultUserTokenManager implements UserTokenManager { protected final ConcurrentMap tokenUserStorage; + protected ConcurrentMap> userStorage; + public DefaultUserTokenManager() { this(new ConcurrentHashMap<>(256)); + } public DefaultUserTokenManager(ConcurrentMap storage) { - tokenUserStorage = storage; + this(storage, new ConcurrentHashMap<>()); + } + + public DefaultUserTokenManager(ConcurrentMap storage, ConcurrentMap> userStorage) { + this.tokenUserStorage = storage; + this.userStorage = userStorage; + } //令牌超时事件,默认3600秒 @@ -51,10 +65,11 @@ public class DefaultUserTokenManager implements UserTokenManager { private AllopatricLoginMode allopatricLoginMode = AllopatricLoginMode.allow; //事件转发器 - private AuthorizationListenerDispatcher authorizationListenerDispatcher; + private ApplicationEventPublisher eventPublisher; - public void setAuthorizationListenerDispatcher(AuthorizationListenerDispatcher authorizationListenerDispatcher) { - this.authorizationListenerDispatcher = authorizationListenerDispatcher; + @Autowired(required = false) + public void setEventPublisher(ApplicationEventPublisher eventPublisher) { + this.eventPublisher = eventPublisher; } public void setTimeout(long timeout) { @@ -73,6 +88,10 @@ public class DefaultUserTokenManager implements UserTokenManager { return allopatricLoginMode; } + protected List getUserToken(String userId) { + return userStorage.computeIfAbsent(userId, key -> new ArrayList<>()); + } + private SimpleUserToken checkTimeout(SimpleUserToken detail) { if (null == detail) { return null; @@ -81,7 +100,7 @@ public class DefaultUserTokenManager implements UserTokenManager { return detail; } if (System.currentTimeMillis() - detail.getLastRequestTime() > detail.getMaxInactiveInterval()) { - detail.setState(TokenState.expired); + changeTokenState(detail, TokenState.expired); return detail; } return detail; @@ -94,8 +113,10 @@ public class DefaultUserTokenManager implements UserTokenManager { @Override public List getByUserId(String userId) { - return tokenUserStorage.values().stream() - .filter(detail -> detail.getUserId().equals(userId) && checkTimeout(detail) != null) + return getUserToken(userId) + .stream() + .map(tokenUserStorage::get) + .filter(Objects::nonNull) .collect(Collectors.toList()); } @@ -111,55 +132,92 @@ public class DefaultUserTokenManager implements UserTokenManager { @Override public boolean tokenIsLoggedIn(String token) { + UserToken userToken = getByToken(token); - return getByToken(token) != null; + return userToken != null && !userToken.isExpired(); } @Override public long totalUser() { - return tokenUserStorage.values() - .stream() - .peek(this::checkTimeout)//检查是否已经超时 - .filter(UserToken::isEffective)//只返回有效的 - .map(UserToken::getUserId) - .distinct()//去重复 - .count(); + return userStorage.size(); + +// return tokenUserStorage.values() +// .parallelStream() +// .peek(this::checkTimeout)//检查是否已经超时 +// .filter(UserToken::isEffective)//只返回有效的 +// .map(UserToken::getUserId) +// .distinct()//去重复 +// .count(); } @Override public long totalToken() { - return tokenUserStorage.values() - .stream() - .peek(this::checkTimeout)//检查是否已经超时 - .filter(UserToken::isEffective)//只返回有效的 - .count(); + return tokenUserStorage.size(); + } + + @Override + public void allLoggedUser(Consumer consumer) { + tokenUserStorage.values().forEach(consumer); } @Override public List allLoggedUser() { - return tokenUserStorage.values() - .stream() - .map(this::checkTimeout) - .filter(UserToken::isEffective) - .collect(Collectors.toList()); + return new ArrayList<>(tokenUserStorage.values()); } @Override public void signOutByUserId(String userId) { - getByUserId(userId).forEach(detail -> signOutByToken(detail.getToken())); + if (null == userId) { + return; + } + List tokens = getUserToken(userId); + tokens.forEach(token->signOutByToken(token,false)); + tokens.clear(); + userStorage.remove(userId); + } + + private void signOutByToken(String token,boolean removeUserToken) { + SimpleUserToken tokenObject = tokenUserStorage.remove(token); + if (tokenObject != null) { + String userId = tokenObject.getUserId(); + if(removeUserToken) { + List tokens = getUserToken(userId); + if (tokens.size() > 0) { + tokens.remove(token); + } + if (tokens.size() == 0) { + userStorage.remove(tokenObject.getUserId()); + } + } + publishEvent(new UserTokenRemovedEvent(tokenObject)); + } } @Override public void signOutByToken(String token) { - tokenUserStorage.remove(token); + signOutByToken(token,true); + } + + protected void publishEvent(ApplicationEvent event) { + if (null != eventPublisher) { + eventPublisher.publishEvent(event); + } + } + + public void changeTokenState(SimpleUserToken userToken, TokenState state) { + if (null != userToken) { + SimpleUserToken copy = userToken.copy(); + + userToken.setState(state); + syncToken(userToken); + + publishEvent(new UserTokenChangedEvent(copy, userToken)); + } } @Override public void changeTokenState(String token, TokenState state) { - SimpleUserToken userToken = getByToken(token); - if (null != userToken) { - userToken.setState(state); - } + changeTokenState(getByToken(token), state); } @Override @@ -171,23 +229,25 @@ public class DefaultUserTokenManager implements UserTokenManager { public UserToken signIn(String token, String type, String userId, long maxInactiveInterval) { SimpleUserToken detail = new SimpleUserToken(userId, token); detail.setType(type); - if (null != authorizationListenerDispatcher) { - authorizationListenerDispatcher.doEvent(new UserSignInEvent(detail)); - } + detail.setMaxInactiveInterval(maxInactiveInterval); + if (allopatricLoginMode == AllopatricLoginMode.deny) { - detail.setState(TokenState.deny); + changeTokenState(detail.getToken(), TokenState.deny); } else if (allopatricLoginMode == AllopatricLoginMode.offlineOther) { detail.setState(TokenState.effective); - SimpleUserToken oldToken = (SimpleUserToken) getByUserId(userId); - if (oldToken != null) { - //踢下线 - oldToken.setState(TokenState.offline); + //将已经登录的用户设置为离线 + List oldToken = getByUserId(userId); + for (UserToken userToken : oldToken) { + changeTokenState(userToken.getToken(), TokenState.offline); } } else { detail.setState(TokenState.effective); } - detail.setMaxInactiveInterval(maxInactiveInterval); tokenUserStorage.put(token, detail); + + getUserToken(userId).add(token); + + publishEvent(new UserTokenCreatedEvent(detail)); return detail; } @@ -196,7 +256,21 @@ public class DefaultUserTokenManager implements UserTokenManager { SimpleUserToken userToken = tokenUserStorage.get(token); if (null != userToken) { userToken.touch(); + syncToken(userToken); } } + @Override + public void checkExpiredToken() { + for (SimpleUserToken token : tokenUserStorage.values()) { + checkTimeout(token); + if (token.isExpired()) { + signOutByToken(token.getToken()); + } + } + } + + protected void syncToken(UserToken userToken) { + //do noting + } } diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/SimpleUserToken.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/SimpleUserToken.java index 9bab74334..7ae919124 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/SimpleUserToken.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/SimpleUserToken.java @@ -10,6 +10,8 @@ import java.util.concurrent.atomic.AtomicLong; */ public class SimpleUserToken implements UserToken { + private static final long serialVersionUID = 1L; + private String userId; private String token; @@ -112,4 +114,27 @@ public class SimpleUserToken implements UserToken { public void setType(String type) { this.type = type; } + + public SimpleUserToken copy() { + SimpleUserToken userToken = new SimpleUserToken(); + userToken.firstRequestTime = firstRequestTime; + userToken.lastRequestTime = lastRequestTime; + userToken.requestTimesCounter = new AtomicLong(requestTimesCounter.get()); + userToken.token = token; + userToken.userId = userId; + userToken.state = state; + userToken.maxInactiveInterval = maxInactiveInterval; + userToken.type = type; + return userToken; + } + + @Override + public int hashCode() { + return token.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return obj != null && hashCode() == obj.hashCode(); + } } 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 861f6550e..1a36b4fb6 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 @@ -19,6 +19,7 @@ package org.hswebframework.web.authorization.token; import java.util.List; +import java.util.function.Consumer; /** * 用户授权容器,用来操作所有已经授权的用户 @@ -37,7 +38,7 @@ public interface UserTokenManager { UserToken getByToken(String token); /** - * 根据用户id,获取全部令牌信息,如果设置了不能跨地点登陆,返回值只可能是{@code null}或者size为1的list + * 根据用户id,获取全部令牌信息,如果没有则返回空集合而不是null * * @param userId 用户id * @return 授权信息 @@ -71,6 +72,7 @@ public interface UserTokenManager { */ List allLoggedUser(); + void allLoggedUser(Consumer consumer); /** * 删除用户授权信息 * @@ -116,4 +118,11 @@ public interface UserTokenManager { * @param token token */ void touch(String token); + + /** + * 检查已过期的token,并将其remove + * + * @see this#signOutByToken(String) + */ + void checkExpiredToken(); } diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/event/UserTokenChangedEvent.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/event/UserTokenChangedEvent.java new file mode 100644 index 000000000..f943b1c25 --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/event/UserTokenChangedEvent.java @@ -0,0 +1,23 @@ +package org.hswebframework.web.authorization.token.event; + +import org.hswebframework.web.authorization.listener.event.AuthorizationEvent; +import org.hswebframework.web.authorization.token.UserToken; +import org.springframework.context.ApplicationEvent; + +public class UserTokenChangedEvent extends ApplicationEvent implements AuthorizationEvent { + private UserToken before, after; + + public UserTokenChangedEvent(UserToken before, UserToken after) { + super(after); + this.before = before; + this.after = after; + } + + public UserToken getBefore() { + return before; + } + + public UserToken getAfter() { + return after; + } +} diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/event/UserTokenCreatedEvent.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/event/UserTokenCreatedEvent.java new file mode 100644 index 000000000..439d7bbfa --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/event/UserTokenCreatedEvent.java @@ -0,0 +1,18 @@ +package org.hswebframework.web.authorization.token.event; + +import org.hswebframework.web.authorization.token.UserToken; +import org.hswebframework.web.authorization.listener.event.AuthorizationEvent; +import org.springframework.context.ApplicationEvent; + +public class UserTokenCreatedEvent extends ApplicationEvent implements AuthorizationEvent { + private UserToken detail; + + public UserTokenCreatedEvent(UserToken detail) { + super(detail); + this.detail = detail; + } + + public UserToken getDetail() { + return detail; + } +} diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/event/UserSignInEvent.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/event/UserTokenRemovedEvent.java similarity index 60% rename from hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/event/UserSignInEvent.java rename to hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/event/UserTokenRemovedEvent.java index fb2863b25..167566ade 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/event/UserSignInEvent.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/event/UserTokenRemovedEvent.java @@ -1,12 +1,14 @@ package org.hswebframework.web.authorization.token.event; -import org.hswebframework.web.authorization.token.UserToken; import org.hswebframework.web.authorization.listener.event.AuthorizationEvent; +import org.hswebframework.web.authorization.token.UserToken; +import org.springframework.context.ApplicationEvent; -public class UserSignInEvent implements AuthorizationEvent { +public class UserTokenRemovedEvent extends ApplicationEvent implements AuthorizationEvent { private UserToken detail; - public UserSignInEvent(UserToken detail) { + public UserTokenRemovedEvent(UserToken token) { + super(token); this.detail = detail; } diff --git a/hsweb-authorization/hsweb-authorization-basic/pom.xml b/hsweb-authorization/hsweb-authorization-basic/pom.xml index 7627d740d..e6f8108e9 100644 --- a/hsweb-authorization/hsweb-authorization-basic/pom.xml +++ b/hsweb-authorization/hsweb-authorization-basic/pom.xml @@ -55,6 +55,16 @@ hsweb-commons-entity ${project.version} + + junit + junit + test + + + org.redisson + redisson + test + \ No newline at end of file diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserOnSignIn.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserOnSignIn.java index b60b8a233..068d816c6 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserOnSignIn.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserOnSignIn.java @@ -6,6 +6,7 @@ import org.hswebframework.web.authorization.token.UserToken; import org.hswebframework.web.authorization.token.UserTokenHolder; import org.hswebframework.web.authorization.token.UserTokenManager; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; import java.util.ArrayList; import java.util.List; @@ -13,7 +14,8 @@ import java.util.List; /** * @author zhouhao */ -public class UserOnSignIn implements AuthorizationListener { +public class UserOnSignIn implements AuthorizationListener + ,ApplicationListener{ private String defaultTokenType = "sessionId"; @@ -36,6 +38,11 @@ public class UserOnSignIn implements AuthorizationListener { +public class UserOnSignOut implements AuthorizationListener,ApplicationListener { private UserTokenManager userTokenManager; public UserOnSignOut(UserTokenManager userTokenManager) { @@ -20,11 +20,16 @@ public class UserOnSignOut implements AuthorizationListener repo = client.getMap("hsweb.user-token", new SerializationCodec()); + ConcurrentMap> userRepo = client.getMap("hsweb.user-token-u", new SerializationCodec()); + + userTokenManager = new DefaultUserTokenManager(repo, userRepo) { + @Override + protected List getUserToken(String userId) { + userRepo.computeIfAbsent(userId,u->new ArrayList<>()); + + return client.getList("hsweb.user-token-"+userId, new SerializationCodec()); + } + + }; +// userTokenManager=new DefaultUserTokenManager(); + + + userRepo.clear(); + repo.clear(); + for (int i = 0; i < 1000; i++) { + userTokenManager.signIn(IDGenerator.MD5.generate(), "sessionId", "admin", 60*3600*1000); + } + userTokenManager.signIn(IDGenerator.MD5.generate(), "sessionId", "admin2", 60*3600*1000); + + testGet(); + testGetAll(); + testSignOut(); + + testGetAll(); + } finally { + client.shutdown(); + } + } + public static void testSignOut(){ + userTokenManager.signOutByUserId("admin"); + + } + public static void testGet() { + List userToken = userTokenManager.getByUserId("admin"); + Assert.assertTrue(!userToken.isEmpty()); + } + + public static void testGetAll() { + logger.warn("total user : " + userTokenManager.totalUser()); + logger.warn("total token : " + userTokenManager.totalToken()); + + userTokenManager.allLoggedUser(token -> System.out.println(token.getToken())); + } +} diff --git a/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/UserTokenManagerTests.java b/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/UserTokenManagerTests.java new file mode 100644 index 000000000..db551eaae --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/UserTokenManagerTests.java @@ -0,0 +1,45 @@ +package org.hswebframework.web.authorization; + +import org.hswebframework.web.authorization.token.DefaultUserTokenManager; +import org.hswebframework.web.authorization.token.TokenState; +import org.hswebframework.web.authorization.token.UserToken; +import org.hswebframework.web.authorization.token.UserTokenManager; +import org.junit.Assert; +import org.junit.Test; + +public class UserTokenManagerTests { + + protected UserTokenManager userTokenManager = new DefaultUserTokenManager(); + + + public void setUserTokenManager(UserTokenManager userTokenManager) { + this.userTokenManager = userTokenManager; + } + + @Test + public void simpleTest() throws InterruptedException { + UserToken userToken = userTokenManager.signIn("test", "sessionId", "admin", 1000); + + Assert.assertNotNull(userToken); + + userTokenManager.changeUserState("admin", TokenState.deny); + + userToken = userTokenManager.getByToken(userToken.getToken()); + + Assert.assertEquals(userToken.getState(), TokenState.deny); + + userTokenManager.changeUserState("admin", TokenState.effective); + + Thread.sleep(1200); + + userToken = userTokenManager.getByToken(userToken.getToken()); + Assert.assertTrue(userToken.isExpired()); + + userTokenManager.checkExpiredToken(); + + userToken = userTokenManager.getByToken(userToken.getToken()); + Assert.assertTrue(userToken == null); + } + + +} diff --git a/hsweb-authorization/hsweb-authorization-cloud/src/main/java/org/hswebframework/web/authorization/cloud/client/feign/FeignUserTokenManager.java b/hsweb-authorization/hsweb-authorization-cloud/src/main/java/org/hswebframework/web/authorization/cloud/client/feign/FeignUserTokenManager.java index d958803fa..72034374e 100644 --- a/hsweb-authorization/hsweb-authorization-cloud/src/main/java/org/hswebframework/web/authorization/cloud/client/feign/FeignUserTokenManager.java +++ b/hsweb-authorization/hsweb-authorization-cloud/src/main/java/org/hswebframework/web/authorization/cloud/client/feign/FeignUserTokenManager.java @@ -66,4 +66,8 @@ public interface FeignUserTokenManager extends UserTokenManager { @Override @RequestMapping(value = "${hsweb.cloud.user-center.prefix:/}user-token/{token}/touch", method = RequestMethod.GET) void touch(@PathVariable("token") String token); + + @Override + @RequestMapping(value = "${hsweb.cloud.user-center.prefix:/}user-token/check-expired-token", method = RequestMethod.PUT) + void checkExpiredToken(); } diff --git a/hsweb-examples/hsweb-examples-simple/pom.xml b/hsweb-examples/hsweb-examples-simple/pom.xml index 3cc88c203..cc384a6d2 100644 --- a/hsweb-examples/hsweb-examples-simple/pom.xml +++ b/hsweb-examples/hsweb-examples-simple/pom.xml @@ -108,12 +108,6 @@ hsweb-system-authorization-starter ${project.version} - - - - - - org.hswebframework.web diff --git a/hsweb-examples/hsweb-examples-simple/src/main/java/org/hswebframework/web/example/simple/SpringBootExample.java b/hsweb-examples/hsweb-examples-simple/src/main/java/org/hswebframework/web/example/simple/SpringBootExample.java index 91dfef90b..535a2021b 100644 --- a/hsweb-examples/hsweb-examples-simple/src/main/java/org/hswebframework/web/example/simple/SpringBootExample.java +++ b/hsweb-examples/hsweb-examples-simple/src/main/java/org/hswebframework/web/example/simple/SpringBootExample.java @@ -88,6 +88,7 @@ public class SpringBootExample @Bean public AopMethodAuthorizeDefinitionCustomizerParser customizerParser(){ //自定义权限声明 + //所有控制都通过 return context -> EmptyAuthorizeDefinition.instance; } diff --git a/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-controller/src/main/java/org/hswebframework/web/controller/authorization/AuthorizationController.java b/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-controller/src/main/java/org/hswebframework/web/controller/authorization/AuthorizationController.java index 0e0829c0e..c9a9be35b 100644 --- a/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-controller/src/main/java/org/hswebframework/web/controller/authorization/AuthorizationController.java +++ b/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-controller/src/main/java/org/hswebframework/web/controller/authorization/AuthorizationController.java @@ -35,6 +35,7 @@ import org.hswebframework.web.service.authorization.UserService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; @@ -61,13 +62,13 @@ public class AuthorizationController { private AuthenticationManager authenticationManager; @Autowired - private AuthorizationListenerDispatcher authorizationListenerDispatcher; + private ApplicationEventPublisher eventPublisher; @GetMapping({"/login-out", "/sign-out", "/exit"}) @Authorize @ApiOperation("退出当前登录") public ResponseMessage exit(@ApiParam(hidden = true) Authentication authentication) { - authorizationListenerDispatcher.doEvent(new AuthorizationExitEvent(authentication)); + eventPublisher.publishEvent(new AuthorizationExitEvent(authentication)); return ok(); } @@ -88,12 +89,12 @@ public class AuthorizationController { Function parameterGetter = request::getParameter; try { AuthorizationDecodeEvent decodeEvent = new AuthorizationDecodeEvent(username, password, parameterGetter); - authorizationListenerDispatcher.doEvent(decodeEvent); + eventPublisher.publishEvent(decodeEvent); username = decodeEvent.getUsername(); password = decodeEvent.getPassword(); AuthorizationBeforeEvent beforeEvent = new AuthorizationBeforeEvent(username, password, parameterGetter); - authorizationListenerDispatcher.doEvent(beforeEvent); + eventPublisher.publishEvent(beforeEvent); UserEntity entity = userService.selectByUsername(username); if (entity == null) { reason = AuthorizationFailedEvent.Reason.USER_NOT_EXISTS; @@ -110,17 +111,15 @@ public class AuthorizationController { } // 验证通过 Authentication authentication = authenticationManager.getByUserId(entity.getId()); + //触发授权成功事件 AuthorizationSuccessEvent event = new AuthorizationSuccessEvent(authentication, parameterGetter); event.getResult().put("userId", entity.getId()); - int size = authorizationListenerDispatcher.doEvent(event); - if (size == 0) { - logger.warn("not found any AuthorizationSuccessEvent,access control maybe disabled!"); - } + eventPublisher.publishEvent(event); return ok(event.getResult()); } catch (Exception e) { AuthorizationFailedEvent failedEvent = new AuthorizationFailedEvent(username, password, parameterGetter, reason); failedEvent.setException(e); - authorizationListenerDispatcher.doEvent(failedEvent); + eventPublisher.publishEvent(failedEvent); throw e; } } diff --git a/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-controller/src/main/java/org/hswebframework/web/controller/authorization/UserTokenController.java b/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-controller/src/main/java/org/hswebframework/web/controller/authorization/UserTokenController.java new file mode 100644 index 000000000..cd84251f0 --- /dev/null +++ b/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-controller/src/main/java/org/hswebframework/web/controller/authorization/UserTokenController.java @@ -0,0 +1,67 @@ +package org.hswebframework.web.controller.authorization; + +import org.hswebframework.web.authorization.Permission; +import org.hswebframework.web.authorization.annotation.Authorize; +import org.hswebframework.web.authorization.token.TokenState; +import org.hswebframework.web.authorization.token.UserToken; +import org.hswebframework.web.authorization.token.UserTokenHolder; +import org.hswebframework.web.authorization.token.UserTokenManager; +import org.hswebframework.web.controller.message.ResponseMessage; +import org.hswebframework.web.logging.AccessLogger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("${hsweb.web.mappings.user-token:user-token}") +@AccessLogger("token信息") +@Authorize(permission = "user-token") +public class UserTokenController { + + @Autowired + private UserTokenManager userTokenManager; + + @GetMapping("/token/total") + @Authorize(merge = false) + public ResponseMessage allLoginToken() { + return ResponseMessage.ok(userTokenManager.totalToken()); + } + + @GetMapping("/user/total") + @Authorize(merge = false) + public ResponseMessage allUserToken() { + return ResponseMessage.ok(userTokenManager.totalUser()); + } + + @GetMapping("/reset") + @Authorize(merge = false) + public ResponseMessage resetToken() { + UserToken token= UserTokenHolder.currentToken(); + if(token!=null){ + userTokenManager.signOutByToken(token.getToken()); + } + return ResponseMessage.ok(true); + } + + @GetMapping("/token/all") + @Authorize(action = Permission.ACTION_GET) + public ResponseMessage> allTokenInfo() { + return ResponseMessage.ok(userTokenManager.allLoggedUser()); + } + + @PutMapping("/token/{token}/{state}") + @Authorize(action = Permission.ACTION_UPDATE) + public ResponseMessage changeTokenState(@PathVariable String token, @PathVariable TokenState state) { + userTokenManager.changeTokenState(token,state); + + return ResponseMessage.ok(); + } + @PutMapping("/user/{userId}/{state}") + @Authorize(action = Permission.ACTION_GET) + public ResponseMessage changeUserState(@PathVariable String userId, @PathVariable TokenState state) { + userTokenManager.changeUserState(userId,state); + return ResponseMessage.ok(); + } + +}