Ver código fonte

优化权限

zhou-hao 7 anos atrás
pai
commit
b010cf7a24
14 arquivos alterados com 143 adições e 51 exclusões
  1. 28 17
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/DefaultUserTokenManager.java
  2. 0 2
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenHolder.java
  3. 18 6
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenManager.java
  4. 6 0
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/configuration/AuthorizingHandlerAutoConfiguration.java
  5. 7 0
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/AuthorizedToken.java
  6. 7 1
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/ParsedToken.java
  7. 6 2
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/SessionIdUserTokenGenerator.java
  8. 4 2
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/SessionIdUserTokenParser.java
  9. 17 2
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserOnSignIn.java
  10. 4 0
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserTokenGenerator.java
  11. 4 3
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserTokenParser.java
  12. 26 0
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/session/UserTokenAutoExpiredListener.java
  13. 16 15
      hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/RedisUserTokenManagerTests.java
  14. 0 1
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-controller/src/main/java/org/hswebframework/web/controller/authorization/AuthorizationController.java

+ 28 - 17
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/DefaultUserTokenManager.java

@@ -18,6 +18,8 @@
 
 package org.hswebframework.web.authorization.token;
 
+import org.hswebframework.web.authorization.exception.AccessDenyException;
+import org.hswebframework.web.authorization.exception.UnAuthorizedException;
 import org.hswebframework.web.authorization.token.event.UserTokenChangedEvent;
 import org.hswebframework.web.authorization.token.event.UserTokenCreatedEvent;
 import org.hswebframework.web.authorization.token.event.UserTokenRemovedEvent;
@@ -32,7 +34,7 @@ import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 /**
- * 授权容器,用来操作所有已经授权的用户
+ * 默认到用户令牌管理器,使用ConcurrentMap来存储令牌信息
  *
  * @author zhouhao
  * @since 3.0
@@ -41,7 +43,7 @@ public class DefaultUserTokenManager implements UserTokenManager {
 
     protected final ConcurrentMap<String, SimpleUserToken> tokenUserStorage;
 
-    protected ConcurrentMap<String, List<String>> userStorage;
+    protected final ConcurrentMap<String, Set<String>> userStorage;
 
     public DefaultUserTokenManager() {
         this(new ConcurrentHashMap<>(256));
@@ -52,10 +54,9 @@ public class DefaultUserTokenManager implements UserTokenManager {
         this(storage, new ConcurrentHashMap<>());
     }
 
-    public DefaultUserTokenManager(ConcurrentMap<String, SimpleUserToken> storage, ConcurrentMap<String, List<String>> userStorage) {
+    public DefaultUserTokenManager(ConcurrentMap<String, SimpleUserToken> storage, ConcurrentMap<String, Set<String>> userStorage) {
         this.tokenUserStorage = storage;
         this.userStorage = userStorage;
-
     }
 
     //异地登录模式,默认允许异地登录
@@ -77,8 +78,8 @@ public class DefaultUserTokenManager implements UserTokenManager {
         return allopatricLoginMode;
     }
 
-    protected List<String> getUserToken(String userId) {
-        return userStorage.computeIfAbsent(userId, key -> new ArrayList<>());
+    protected Set<String> getUserToken(String userId) {
+        return userStorage.computeIfAbsent(userId, key -> new HashSet<>());
     }
 
     private SimpleUserToken checkTimeout(SimpleUserToken detail) {
@@ -159,18 +160,18 @@ public class DefaultUserTokenManager implements UserTokenManager {
         if (null == userId) {
             return;
         }
-        List<String> tokens =  getUserToken(userId);
-        tokens.forEach(token->signOutByToken(token,false));
+        Set<String> tokens = getUserToken(userId);
+        tokens.forEach(token -> signOutByToken(token, false));
         tokens.clear();
         userStorage.remove(userId);
     }
 
-    private void signOutByToken(String token,boolean removeUserToken) {
+    private void signOutByToken(String token, boolean removeUserToken) {
         SimpleUserToken tokenObject = tokenUserStorage.remove(token);
         if (tokenObject != null) {
             String userId = tokenObject.getUserId();
-            if(removeUserToken) {
-                List<String> tokens = getUserToken(userId);
+            if (removeUserToken) {
+                Set<String> tokens = getUserToken(userId);
                 if (tokens.size() > 0) {
                     tokens.remove(token);
                 }
@@ -184,7 +185,7 @@ public class DefaultUserTokenManager implements UserTokenManager {
 
     @Override
     public void signOutByToken(String token) {
-       signOutByToken(token,true);
+        signOutByToken(token, true);
     }
 
     protected void publishEvent(ApplicationEvent event) {
@@ -221,17 +222,22 @@ public class DefaultUserTokenManager implements UserTokenManager {
         detail.setMaxInactiveInterval(maxInactiveInterval);
 
         if (allopatricLoginMode == AllopatricLoginMode.deny) {
-            changeTokenState(detail.getToken(), TokenState.deny);
+            boolean hasAnotherToken = getByUserId(userId)
+                    .stream()
+                    .map(SimpleUserToken.class::cast)
+                    .peek(this::checkTimeout)
+                    .anyMatch(UserToken::isEffective);
+            if (hasAnotherToken) {
+                throw new AccessDenyException("该用户已在其他地方登陆");
+            }
         } else if (allopatricLoginMode == AllopatricLoginMode.offlineOther) {
-            detail.setState(TokenState.effective);
-            //将已经登录的用户设置为离线
+            //将在其他地方登录的用户设置为离线
             List<UserToken> oldToken = getByUserId(userId);
             for (UserToken userToken : oldToken) {
                 changeTokenState(userToken.getToken(), TokenState.offline);
             }
-        } else {
-            detail.setState(TokenState.effective);
         }
+        detail.setState(TokenState.effective);
         tokenUserStorage.put(token, detail);
 
         getUserToken(userId).add(token);
@@ -259,6 +265,11 @@ public class DefaultUserTokenManager implements UserTokenManager {
         }
     }
 
+    /**
+     * 同步令牌信息,如果使用redisson等来存储token,应该重写此方法并调用{@link this#tokenUserStorage}.put
+     *
+     * @param userToken 令牌
+     */
     protected void syncToken(UserToken userToken) {
         //do noting
     }

+ 0 - 2
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenHolder.java

@@ -4,8 +4,6 @@ import org.hswebframework.web.ThreadLocalUtils;
 import org.hswebframework.web.authorization.token.UserToken;
 
 /**
- * TODO 完成注释
- *
  * @author zhouhao
  */
 public final class UserTokenHolder {

+ 18 - 6
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenManager.java

@@ -22,7 +22,7 @@ import java.util.List;
 import java.util.function.Consumer;
 
 /**
- * 用户授权容器,用来操作所有已经授权的用户
+ * 用户令牌管理器,用于管理用户令牌
  *
  * @author zhouhao
  * @since 3.0
@@ -68,11 +68,17 @@ public interface UserTokenManager {
     long totalToken();
 
     /**
-     * @return 所有被授权的用户
+     * @return 所有token
      */
     List<UserToken> allLoggedUser();
 
+    /**
+     * 遍历全部token信息
+     *
+     * @param consumer token消费者
+     */
     void allLoggedUser(Consumer<UserToken> consumer);
+
     /**
      * 删除用户授权信息
      *
@@ -83,7 +89,8 @@ public interface UserTokenManager {
     /**
      * 根据token删除
      *
-     * @param token
+     * @param token 令牌
+     * @see org.hswebframework.web.authorization.token.event.UserTokenRemovedEvent
      */
     void signOutByToken(String token);
 
@@ -92,6 +99,8 @@ public interface UserTokenManager {
      *
      * @param userId userId
      * @param state  状态
+     * @see org.hswebframework.web.authorization.token.event.UserTokenChangedEvent
+     * @see this#changeTokenState
      */
     void changeUserState(String userId, TokenState state);
 
@@ -100,15 +109,18 @@ public interface UserTokenManager {
      *
      * @param token token
      * @param state 状态
+     * @see org.hswebframework.web.authorization.token.event.UserTokenChangedEvent
      */
     void changeTokenState(String token, TokenState state);
 
     /**
      * 登记一个用户的token
      *
-     * @param token  token
-     * @param type   令牌类型
-     * @param userId 用户id
+     * @param token               token
+     * @param type                令牌类型
+     * @param userId              用户id
+     * @param maxInactiveInterval 最大不活动时间,超过后令牌状态{@link UserToken#getState()}将变为过期{@link TokenState#expired}
+     * @see org.hswebframework.web.authorization.token.event.UserTokenCreatedEvent
      */
     UserToken signIn(String token, String type, String userId, long maxInactiveInterval);
 

+ 6 - 0
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/configuration/AuthorizingHandlerAutoConfiguration.java

@@ -8,6 +8,7 @@ import org.hswebframework.web.authorization.access.DataAccessHandler;
 import org.hswebframework.web.authorization.basic.handler.DefaultAuthorizingHandler;
 import org.hswebframework.web.authorization.basic.handler.access.DefaultDataAccessController;
 import org.hswebframework.web.authorization.basic.web.*;
+import org.hswebframework.web.authorization.basic.web.session.UserTokenAutoExpiredListener;
 import org.hswebframework.web.authorization.token.DefaultUserTokenManager;
 import org.hswebframework.web.authorization.token.UserTokenAuthenticationSupplier;
 import org.hswebframework.web.authorization.token.UserTokenManager;
@@ -87,6 +88,11 @@ public class AuthorizingHandlerAutoConfiguration {
         return new UserOnSignOut(userTokenManager);
     }
 
+    @Bean
+    public UserTokenAutoExpiredListener userTokenAutoExpiredListener(UserTokenManager userTokenManager) {
+        return new UserTokenAutoExpiredListener(userTokenManager);
+    }
+
     @Configuration
     public static class DataAccessHandlerProcessor implements BeanPostProcessor {
 

+ 7 - 0
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/AuthorizedToken.java

@@ -6,8 +6,15 @@ package org.hswebframework.web.authorization.basic.web;
  * @author zhouhao
  */
 public interface AuthorizedToken extends ParsedToken {
+
+    /**
+     * @return 令牌绑定的用户id
+     */
     String getUserId();
 
+    /**
+     * @return 令牌有效期,单位毫秒,-1为长期有效
+     */
     default long getMaxInactiveInterval() {
         return -1;
     }

+ 7 - 1
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/ParsedToken.java

@@ -1,12 +1,18 @@
 package org.hswebframework.web.authorization.basic.web;
 
 /**
- * TODO 完成注释
+ * 令牌解析结果
  *
  * @author zhouhao
  */
 public interface ParsedToken {
+    /**
+     * @return 令牌
+     */
     String getToken();
 
+    /**
+     * @return 令牌类型
+     */
     String getType();
 }

+ 6 - 2
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/SessionIdUserTokenGenerator.java

@@ -13,9 +13,11 @@ import java.util.Map;
  */
 public class SessionIdUserTokenGenerator implements UserTokenGenerator, Serializable {
 
+    private static final long serialVersionUID = -9197243220777237431L;
+
     @Override
     public String getSupportTokenType() {
-        return "sessionId";
+        return TOKEN_TYPE_SESSION_ID;
     }
 
     @Override
@@ -30,6 +32,8 @@ public class SessionIdUserTokenGenerator implements UserTokenGenerator, Serializ
         String sessionId = request.getSession().getId();
 
         return new GeneratedToken() {
+            private static final long serialVersionUID = 3964183451883410929L;
+
             @Override
             public Map<String, Object> getResponse() {
                 return Collections.emptyMap();
@@ -42,7 +46,7 @@ public class SessionIdUserTokenGenerator implements UserTokenGenerator, Serializ
 
             @Override
             public String getType() {
-                return "session-id-default";
+                return TOKEN_TYPE_SESSION_ID;
             }
 
             @Override

+ 4 - 2
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/SessionIdUserTokenParser.java

@@ -8,6 +8,8 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSession;
 import java.util.function.Predicate;
 
+import static org.hswebframework.web.authorization.basic.web.UserTokenGenerator.TOKEN_TYPE_SESSION_ID;
+
 /**
  * @author zhouhao
  */
@@ -46,7 +48,7 @@ public class SessionIdUserTokenParser implements UserTokenParser {
 
                     @Override
                     public String getType() {
-                        return "session-id-default";
+                        return TOKEN_TYPE_SESSION_ID;
                     }
 
                     @Override
@@ -63,7 +65,7 @@ public class SessionIdUserTokenParser implements UserTokenParser {
 
                 @Override
                 public String getType() {
-                    return "session-id-default";
+                    return TOKEN_TYPE_SESSION_ID;
                 }
             };
         }

+ 17 - 2
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserOnSignIn.java

@@ -12,13 +12,28 @@ import java.util.ArrayList;
 import java.util.List;
 
 /**
+ * 监听授权成功事件,授权成功后,生成token并注册到{@link UserTokenManager}
+ *
  * @author zhouhao
+ * @see org.springframework.context.ApplicationEvent
+ * @see org.hswebframework.web.authorization.listener.event.AuthorizationEvent
+ * @see UserTokenManager
+ * @see UserTokenGenerator
+ * @since 3.0
  */
 public class UserOnSignIn implements AuthorizationListener<AuthorizationSuccessEvent>
-        ,ApplicationListener<AuthorizationSuccessEvent>{
+        , ApplicationListener<AuthorizationSuccessEvent> {
 
+    /**
+     * 默认到令牌类型
+     * @see UserToken#getType()
+     * @see SessionIdUserTokenGenerator#getSupportTokenType()
+     */
     private String defaultTokenType = "sessionId";
 
+    /**
+     * 令牌管理器
+     */
     private UserTokenManager userTokenManager;
 
     private List<UserTokenGenerator> userTokenGenerators = new ArrayList<>();
@@ -38,7 +53,7 @@ public class UserOnSignIn implements AuthorizationListener<AuthorizationSuccessE
 
     @Override
     public void on(AuthorizationSuccessEvent event) {
-       onApplicationEvent(event);
+        onApplicationEvent(event);
     }
 
     @Override

+ 4 - 0
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserTokenGenerator.java

@@ -9,6 +9,10 @@ import org.hswebframework.web.authorization.Authentication;
  *
  */
 public interface UserTokenGenerator {
+    String TOKEN_TYPE_SESSION_ID = "sessionId";
+
+    String TOKEN_TYPE_SIMPLE = "simple-token";
+
     String getSupportTokenType();
 
     GeneratedToken generate(Authentication authentication);

+ 4 - 3
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserTokenParser.java

@@ -4,11 +4,12 @@ import javax.servlet.http.HttpServletRequest;
 import java.util.function.Predicate;
 
 /**
- * TODO 完成注释
- *
+ * 令牌解析器,用于在接受到请求到时候,从请求中获取令牌
  * @author zhouhao
+ * @see 3.0
+ * @see ParsedToken
+ * @see AuthorizedToken
  */
 public interface UserTokenParser {
-
     ParsedToken parseToken(HttpServletRequest request);
 }

+ 26 - 0
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/session/UserTokenAutoExpiredListener.java

@@ -0,0 +1,26 @@
+package org.hswebframework.web.authorization.basic.web.session;
+
+import org.hswebframework.web.authorization.token.UserTokenManager;
+
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+
+public class UserTokenAutoExpiredListener implements HttpSessionListener {
+
+    private UserTokenManager userTokenManager;
+
+    public UserTokenAutoExpiredListener(UserTokenManager userTokenManager) {
+        this.userTokenManager = userTokenManager;
+    }
+
+    @Override
+    public void sessionCreated(HttpSessionEvent se) {
+
+    }
+
+    @Override
+    public void sessionDestroyed(HttpSessionEvent se) {
+        String sessionId = se.getSession().getId();
+        userTokenManager.signOutByToken(sessionId);
+    }
+}

+ 16 - 15
hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/RedisUserTokenManagerTests.java

@@ -1,9 +1,6 @@
 package org.hswebframework.web.authorization;
 
-import org.hswebframework.web.authorization.token.DefaultUserTokenManager;
-import org.hswebframework.web.authorization.token.SimpleUserToken;
-import org.hswebframework.web.authorization.token.UserToken;
-import org.hswebframework.web.authorization.token.UserTokenManager;
+import org.hswebframework.web.authorization.token.*;
 import org.hswebframework.web.id.IDGenerator;
 import org.junit.Assert;
 import org.redisson.Redisson;
@@ -15,12 +12,14 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.ConcurrentMap;
 
 public class RedisUserTokenManagerTests {
 
-    static UserTokenManager userTokenManager;
+    static DefaultUserTokenManager userTokenManager;
 
     static String token = IDGenerator.MD5.generate();
 
@@ -31,26 +30,28 @@ public class RedisUserTokenManagerTests {
 
         try {
             ConcurrentMap<String, SimpleUserToken> repo = client.getMap("hsweb.user-token", new SerializationCodec());
-            ConcurrentMap<String, List<String>> userRepo = client.getMap("hsweb.user-token-u", new SerializationCodec());
+            ConcurrentMap<String, Set<String>> userRepo = client.getMap("hsweb.user-token-u", new SerializationCodec());
 
             userTokenManager = new DefaultUserTokenManager(repo, userRepo) {
                 @Override
-                protected List<String> getUserToken(String userId) {
-                    userRepo.computeIfAbsent(userId,u->new ArrayList<>());
+                protected Set<String> getUserToken(String userId) {
+                    userRepo.computeIfAbsent(userId,u->new HashSet<>());
 
-                    return client.getList("hsweb.user-token-"+userId, new SerializationCodec());
+                    return client.getSet("hsweb.user-token-"+userId, new SerializationCodec());
                 }
 
             };
+
+            userTokenManager.setAllopatricLoginMode(AllopatricLoginMode.deny);
 //            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);
+//            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();

+ 0 - 1
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-controller/src/main/java/org/hswebframework/web/controller/authorization/AuthorizationController.java

@@ -25,7 +25,6 @@ import org.hswebframework.web.NotFoundException;
 import org.hswebframework.web.authorization.Authentication;
 import org.hswebframework.web.authorization.AuthenticationManager;
 import org.hswebframework.web.authorization.annotation.Authorize;
-import org.hswebframework.web.authorization.listener.AuthorizationListenerDispatcher;
 import org.hswebframework.web.authorization.listener.event.*;
 import org.hswebframework.web.commons.entity.DataStatus;
 import org.hswebframework.web.controller.message.ResponseMessage;