Browse Source

UserTokenManager支持可指定权限信息

zhou-hao 3 years ago
parent
commit
01c5eb9e6c
12 changed files with 265 additions and 108 deletions
  1. 21 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/AuthenticationUserToken.java
  2. 23 5
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/DefaultUserTokenManager.java
  3. 22 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/LocalAuthenticationUserToken.java
  4. 21 2
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenManager.java
  5. 10 4
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenReactiveAuthenticationSupplier.java
  6. 1 1
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/event/UserTokenCreatedEvent.java
  7. 34 15
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/redis/RedisUserTokenManager.java
  8. 15 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/redis/SimpleAuthenticationUserToken.java
  9. 5 1
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/redis/SimpleUserToken.java
  10. 4 4
      hsweb-authorization/hsweb-authorization-api/src/test/java/org/hswebframework/web/authorization/AuthenticationTests.java
  11. 39 21
      hsweb-authorization/hsweb-authorization-api/src/test/java/org/hswebframework/web/authorization/UserTokenManagerTests.java
  12. 70 55
      hsweb-authorization/hsweb-authorization-api/src/test/java/org/hswebframework/web/authorization/token/redis/RedisUserTokenManagerTest.java

+ 21 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/AuthenticationUserToken.java

@@ -0,0 +1,21 @@
+package org.hswebframework.web.authorization.token;
+
+import org.hswebframework.web.authorization.Authentication;
+
+/**
+ * 包含认证信息的token
+ *
+ * @author zhouhao
+ * @since 4.0.12
+ */
+public interface AuthenticationUserToken extends UserToken {
+
+    /**
+     * 获取认证信息
+     *
+     * @return auth
+     * @see Authentication
+     */
+    Authentication getAuthentication();
+
+}

+ 23 - 5
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/DefaultUserTokenManager.java

@@ -20,6 +20,7 @@ package org.hswebframework.web.authorization.token;
 
 import lombok.Getter;
 import lombok.Setter;
+import org.hswebframework.web.authorization.Authentication;
 import org.hswebframework.web.authorization.exception.AccessDenyException;
 import org.hswebframework.web.authorization.token.event.UserTokenChangedEvent;
 import org.hswebframework.web.authorization.token.event.UserTokenCreatedEvent;
@@ -33,6 +34,7 @@ import reactor.core.publisher.Mono;
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.function.Supplier;
 
 /**
  * 默认到用户令牌管理器,使用ConcurrentMap来存储令牌信息
@@ -122,9 +124,9 @@ public class DefaultUserTokenManager implements UserTokenManager {
             return Flux.empty();
         }
         return Flux.fromStream(tokens
-                .stream()
-                .map(tokenStorage::get)
-                .filter(Objects::nonNull));
+                                       .stream()
+                                       .map(tokenStorage::get)
+                                       .filter(Objects::nonNull));
     }
 
     @Override
@@ -228,14 +230,23 @@ public class DefaultUserTokenManager implements UserTokenManager {
     @Override
     public Mono<Void> changeUserState(String user, TokenState state) {
         return Mono.from(getByUserId(user)
-                .flatMap(token -> changeTokenState(token.getToken(), state)));
+                                 .flatMap(token -> changeTokenState(token.getToken(), state)));
     }
 
     @Override
     public Mono<UserToken> signIn(String token, String type, String userId, long maxInactiveInterval) {
 
+        return doSignIn(token, type, userId, maxInactiveInterval, LocalUserToken::new)
+                .cast(UserToken.class);
+
+    }
+
+    private <T extends LocalUserToken> Mono<T> doSignIn(String token, String type, String userId, long maxInactiveInterval, Supplier<T> tokenSupplier) {
+
         return Mono.defer(() -> {
-            LocalUserToken detail = new LocalUserToken(userId, token);
+            T detail = tokenSupplier.get();
+            detail.setUserId(userId);
+            detail.setToken(token);
             detail.setType(type);
             detail.setMaxInactiveInterval(maxInactiveInterval);
             detail.setState(TokenState.normal);
@@ -272,6 +283,13 @@ public class DefaultUserTokenManager implements UserTokenManager {
 
     }
 
+    @Override
+    public Mono<AuthenticationUserToken> signIn(String token, String type, String userId, long maxInactiveInterval, Authentication authentication) {
+        return this
+                .doSignIn(token, type, userId, maxInactiveInterval, () -> new LocalAuthenticationUserToken(authentication))
+                .cast(AuthenticationUserToken.class);
+    }
+
     @Override
     public Mono<Void> touch(String token) {
         LocalUserToken userToken = tokenStorage.get(token);

+ 22 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/LocalAuthenticationUserToken.java

@@ -0,0 +1,22 @@
+package org.hswebframework.web.authorization.token;
+
+import lombok.AllArgsConstructor;
+import org.hswebframework.web.authorization.Authentication;
+
+
+/**
+ * 包含认证信息的用户令牌信息
+ *
+ * @author zhouhao
+ * @since 4.0.12
+ */
+@AllArgsConstructor
+public class LocalAuthenticationUserToken extends LocalUserToken implements AuthenticationUserToken {
+
+    private final Authentication authentication;
+
+    @Override
+    public Authentication getAuthentication() {
+        return authentication;
+    }
+}

+ 21 - 2
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenManager.java

@@ -18,6 +18,7 @@
 
 package org.hswebframework.web.authorization.token;
 
+import org.hswebframework.web.authorization.Authentication;
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
@@ -116,11 +117,29 @@ public interface UserTokenManager {
      * @param token               token
      * @param type                令牌类型
      * @param userId              用户id
-     * @param maxInactiveInterval 最大不活动时间,超过后令牌状态{@link UserToken#getState()}将变为过期{@link TokenState#expired}
+     * @param maxInactiveInterval 最大不活动时间(单位毫秒),超过后令牌状态{@link UserToken#getState()}将变为过期{@link TokenState#expired}
      * @see org.hswebframework.web.authorization.token.event.UserTokenCreatedEvent
      */
     Mono<UserToken> signIn(String token, String type, String userId, long maxInactiveInterval);
 
+    /**
+     * 登记一个包含认证信息的token
+     *
+     * @param token               token
+     * @param type                令牌类型
+     * @param userId              用户ID
+     * @param maxInactiveInterval 最大不活动时间(单位毫秒),小于0永不过期,超过后令牌状态{@link UserToken#getState()}将变为过期{@link TokenState#expired}
+     * @param authentication      认证信息
+     * @return token信息
+     */
+    default Mono<AuthenticationUserToken> signIn(String token,
+                                                 String type,
+                                                 String userId,
+                                                 long maxInactiveInterval,
+                                                 Authentication authentication) {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * 更新token,使其不过期
      *
@@ -131,7 +150,7 @@ public interface UserTokenManager {
     /**
      * 检查已过期的token,并将其remove
      *
-     * @see this#signOutByToken(String)
+     * @see UserTokenManager#signOutByToken(String)
      */
     Mono<Void> checkExpiredToken();
 

+ 10 - 4
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenReactiveAuthenticationSupplier.java

@@ -72,10 +72,16 @@ public class UserTokenReactiveAuthenticationSupplier implements ReactiveAuthenti
                 .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())))
+                        .map(t -> userTokenManager
+                                .getByToken(t.getToken())
+                                .filter(UserToken::validate)
+                                .flatMap(token -> {
+                                    Mono<Void> before = userTokenManager.touch(token.getToken());
+                                    if (token instanceof AuthenticationUserToken) {
+                                        return before.thenReturn(((AuthenticationUserToken) token).getAuthentication());
+                                    }
+                                    return before.then(get(thirdPartAuthenticationManager.get(token.getType()), token.getUserId()));
+                                }))
                         .orElseGet(Mono::empty))
                 .flatMap(auth -> ReactiveLogger
                         .mdc("userId", auth.getUser().getId(),

+ 1 - 1
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/event/UserTokenCreatedEvent.java

@@ -5,7 +5,7 @@ import org.hswebframework.web.authorization.events.AuthorizationEvent;
 import org.springframework.context.ApplicationEvent;
 
 public class UserTokenCreatedEvent extends ApplicationEvent implements AuthorizationEvent {
-    private UserToken detail;
+    private final UserToken detail;
 
     public UserTokenCreatedEvent(UserToken detail) {
         super(detail);

+ 34 - 15
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/redis/RedisUserTokenManager.java

@@ -2,11 +2,9 @@ package org.hswebframework.web.authorization.token.redis;
 
 import lombok.Getter;
 import lombok.Setter;
+import org.hswebframework.web.authorization.Authentication;
 import org.hswebframework.web.authorization.exception.AccessDenyException;
-import org.hswebframework.web.authorization.token.AllopatricLoginMode;
-import org.hswebframework.web.authorization.token.TokenState;
-import org.hswebframework.web.authorization.token.UserToken;
-import org.hswebframework.web.authorization.token.UserTokenManager;
+import org.hswebframework.web.authorization.token.*;
 import org.hswebframework.web.authorization.token.event.UserTokenChangedEvent;
 import org.hswebframework.web.authorization.token.event.UserTokenCreatedEvent;
 import org.hswebframework.web.authorization.token.event.UserTokenRemovedEvent;
@@ -26,6 +24,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
@@ -51,14 +50,14 @@ public class RedisUserTokenManager implements UserTokenManager {
                 .subscribe(msg -> localCache.remove(String.valueOf(msg.getMessage())));
 
         Flux.<UserToken>create(sink -> this.touchSink = sink)
-                .buffer(Flux.interval(Duration.ofSeconds(10)), HashSet::new)
-                .flatMap(list -> Flux
-                        .fromIterable(list)
-                        .flatMap(token -> operations
-                                .expire(getTokenRedisKey(token.getToken()), Duration.ofMillis(token.getMaxInactiveInterval()))
-                                .then())
-                        .onErrorResume(err -> Mono.empty()))
-                .subscribe();
+            .buffer(Flux.interval(Duration.ofSeconds(10)), HashSet::new)
+            .flatMap(list -> Flux
+                    .fromIterable(list)
+                    .flatMap(token -> operations
+                            .expire(getTokenRedisKey(token.getToken()), Duration.ofMillis(token.getMaxInactiveInterval()))
+                            .then())
+                    .onErrorResume(err -> Mono.empty()))
+            .subscribe();
 
     }
 
@@ -212,8 +211,11 @@ public class RedisUserTokenManager implements UserTokenManager {
                 });
     }
 
-    @Override
-    public Mono<UserToken> signIn(String token, String type, String userId, long maxInactiveInterval) {
+    private Mono<UserToken> signIn(String token,
+                                   String type,
+                                   String userId,
+                                   long maxInactiveInterval,
+                                   Consumer<Map<String, Object>> cacheBuilder) {
         return Mono
                 .defer(() -> {
                     Mono<SimpleUserToken> doSign = Mono.defer(() -> {
@@ -225,7 +227,7 @@ public class RedisUserTokenManager implements UserTokenManager {
                         map.put("state", TokenState.normal.getValue());
                         map.put("signInTime", System.currentTimeMillis());
                         map.put("lastRequestTime", System.currentTimeMillis());
-
+                        cacheBuilder.accept(map);
                         String key = getTokenRedisKey(token);
                         return userTokenStore
                                 .putAll(key, map)
@@ -265,6 +267,23 @@ public class RedisUserTokenManager implements UserTokenManager {
                 .flatMap(this::onUserTokenCreated);
     }
 
+    @Override
+    public Mono<UserToken> signIn(String token, String type, String userId, long maxInactiveInterval) {
+        return signIn(token, type, userId, maxInactiveInterval, ignore -> {
+        });
+    }
+
+    @Override
+    public Mono<AuthenticationUserToken> signIn(String token,
+                                                String type,
+                                                String userId,
+                                                long maxInactiveInterval,
+                                                Authentication authentication) {
+        return this
+                .signIn(token, type, userId, maxInactiveInterval,
+                        cache -> cache.put("authentication", authentication))
+                .cast(AuthenticationUserToken.class);
+    }
 
     @Override
     public Mono<Void> touch(String token) {

+ 15 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/redis/SimpleAuthenticationUserToken.java

@@ -0,0 +1,15 @@
+package org.hswebframework.web.authorization.token.redis;
+
+import lombok.AllArgsConstructor;
+import org.hswebframework.web.authorization.Authentication;
+import org.hswebframework.web.authorization.token.AuthenticationUserToken;
+
+@AllArgsConstructor
+public class SimpleAuthenticationUserToken  extends SimpleUserToken implements AuthenticationUserToken {
+    private final Authentication authentication;
+
+    @Override
+    public Authentication getAuthentication() {
+        return authentication;
+    }
+}

+ 5 - 1
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/redis/SimpleUserToken.java

@@ -4,6 +4,7 @@ import lombok.EqualsAndHashCode;
 import lombok.Getter;
 import lombok.Setter;
 import lombok.ToString;
+import org.hswebframework.web.authorization.Authentication;
 import org.hswebframework.web.authorization.token.TokenState;
 import org.hswebframework.web.authorization.token.UserToken;
 import org.hswebframework.web.bean.FastBeanCopier;
@@ -33,7 +34,10 @@ public class SimpleUserToken implements UserToken {
     private long maxInactiveInterval;
 
     public static SimpleUserToken of(Map<String, Object> map) {
-
+        Object authentication = map.get("authentication");
+        if(authentication instanceof Authentication){
+            return FastBeanCopier.copy(map, new SimpleAuthenticationUserToken(((Authentication) authentication)));
+        }
         return FastBeanCopier.copy(map, new SimpleUserToken());
     }
 

+ 4 - 4
hsweb-authorization/hsweb-authorization-api/src/test/java/org/hswebframework/web/authorization/AuthenticationTests.java

@@ -106,16 +106,16 @@ public class AuthenticationTests {
 
             @Override
             public Mono<Authentication> getByUserId(String userId) {
-                if (userId.equals("admin")) {
-                    return Mono.just(authentication);
-                }
+//                if (userId.equals("admin")) {
+//                    return Mono.just(authentication);
+//                }
                 return Mono.empty();
             }
 
         };
         //绑定用户token
         UserTokenManager userTokenManager = new DefaultUserTokenManager();
-        UserToken token = userTokenManager.signIn("test", "token-test", "admin", -1).block();
+        UserToken token = userTokenManager.signIn("test", "token-test", "admin", -1,authentication).block();
 
         ReactiveAuthenticationHolder.addSupplier(new UserTokenReactiveAuthenticationSupplier(userTokenManager, authenticationManager));
         ParsedToken parsedToken=new ParsedToken() {

+ 39 - 21
hsweb-authorization/hsweb-authorization-api/src/test/java/org/hswebframework/web/authorization/UserTokenManagerTests.java

@@ -1,6 +1,7 @@
 package org.hswebframework.web.authorization;
 
 import org.hswebframework.web.authorization.exception.AccessDenyException;
+import org.hswebframework.web.authorization.simple.SimpleAuthentication;
 import org.hswebframework.web.authorization.token.*;
 import org.junit.Assert;
 import org.junit.Test;
@@ -11,7 +12,8 @@ public class UserTokenManagerTests {
 
     /**
      * 基本功能测试
-     * @throws InterruptedException  Thread.sleep error
+     *
+     * @throws InterruptedException Thread.sleep error
      */
     @Test
     public void testDefaultSetting() throws InterruptedException {
@@ -26,15 +28,15 @@ public class UserTokenManagerTests {
 
         //2个token
         userTokenManager.totalToken()
-                .as(StepVerifier::create)
-                .expectNext(2)
-                .verifyComplete();
+                        .as(StepVerifier::create)
+                        .expectNext(2)
+                        .verifyComplete();
 
         //1个用户
         userTokenManager.totalUser()
-                .as(StepVerifier::create)
-                .expectNext(1)
-                .verifyComplete();
+                        .as(StepVerifier::create)
+                        .expectNext(1)
+                        .verifyComplete();
 
         //改变token状态
         userTokenManager.changeUserState("admin", TokenState.deny).subscribe();
@@ -48,28 +50,28 @@ public class UserTokenManagerTests {
         Thread.sleep(1200);
 
         userTokenManager.getByToken(userToken.getToken())
-                .map(UserToken::isExpired)
-                .as(StepVerifier::create)
-                .expectNext(true)
-                .verifyComplete();
+                        .map(UserToken::isExpired)
+                        .as(StepVerifier::create)
+                        .expectNext(true)
+                        .verifyComplete();
 
         userTokenManager.checkExpiredToken().subscribe();
 
 
         userTokenManager.getByToken(userToken.getToken())
-                .as(StepVerifier::create)
-                .expectNextCount(0)
-                .verifyComplete();
+                        .as(StepVerifier::create)
+                        .expectNextCount(0)
+                        .verifyComplete();
 
         userTokenManager.totalToken()
-                .as(StepVerifier::create)
-                .expectNext(1)
-                .verifyComplete();
+                        .as(StepVerifier::create)
+                        .expectNext(1)
+                        .verifyComplete();
 
         userTokenManager.totalUser()
-                .as(StepVerifier::create)
-                .expectNext(1)
-                .verifyComplete();
+                        .as(StepVerifier::create)
+                        .expectNext(1)
+                        .verifyComplete();
 
     }
 
@@ -99,7 +101,7 @@ public class UserTokenManagerTests {
      * 测试异地登录模式之踢下线
      */
     @Test
-    public void testOffline()   {
+    public void testOffline() {
         DefaultUserTokenManager userTokenManager = new DefaultUserTokenManager();
         userTokenManager.setAllopatricLoginMode(AllopatricLoginMode.offlineOther); //将其他地方登录的用户踢下线
 
@@ -113,5 +115,21 @@ public class UserTokenManagerTests {
 
     }
 
+    @Test
+    public void testAuth() {
+        UserTokenManager userTokenManager = new DefaultUserTokenManager();
+        Authentication authentication = new SimpleAuthentication();
+
+        userTokenManager.signIn("test", "test", "test", 1000, authentication)
+                        .as(StepVerifier::create)
+                        .expectNextMatches(token -> token.getAuthentication() == authentication)
+                        .verifyComplete();
+
+        userTokenManager.getByToken("test")
+                        .cast(AuthenticationUserToken.class)
+                        .as(StepVerifier::create)
+                        .expectNextMatches(token -> token.getAuthentication() == authentication)
+                        .verifyComplete();
+    }
 
 }

+ 70 - 55
hsweb-authorization/hsweb-authorization-api/src/test/java/org/hswebframework/web/authorization/token/redis/RedisUserTokenManagerTest.java

@@ -1,12 +1,11 @@
 package org.hswebframework.web.authorization.token.redis;
 
 import lombok.SneakyThrows;
+import org.hswebframework.web.authorization.Authentication;
 import org.hswebframework.web.authorization.exception.AccessDenyException;
 import org.hswebframework.web.authorization.exception.UnAuthorizedException;
-import org.hswebframework.web.authorization.token.AllopatricLoginMode;
-import org.hswebframework.web.authorization.token.TokenState;
-import org.hswebframework.web.authorization.token.UserToken;
-import org.hswebframework.web.authorization.token.UserTokenManager;
+import org.hswebframework.web.authorization.simple.SimpleAuthentication;
+import org.hswebframework.web.authorization.token.*;
 import org.junit.Before;
 import org.junit.Test;
 import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
@@ -48,30 +47,30 @@ public class RedisUserTokenManagerTest {
     public void testSign() {
 
         tokenManager.signIn("test-token", "test", "test", 10000)
-                .map(UserToken::getToken)
-                .as(StepVerifier::create)
-                .expectNext("test-token")
-                .verifyComplete();
+                    .map(UserToken::getToken)
+                    .as(StepVerifier::create)
+                    .expectNext("test-token")
+                    .verifyComplete();
 
         tokenManager.userIsLoggedIn("test")
-                .as(StepVerifier::create)
-                .expectNext(true)
-                .verifyComplete();
+                    .as(StepVerifier::create)
+                    .expectNext(true)
+                    .verifyComplete();
 
         tokenManager.tokenIsLoggedIn("test-token")
-                .as(StepVerifier::create)
-                .expectNext(true)
-                .verifyComplete();
+                    .as(StepVerifier::create)
+                    .expectNext(true)
+                    .verifyComplete();
 
         tokenManager.getByToken("test-token")
-                .map(UserToken::getState)
-                .as(StepVerifier::create)
-                .expectNext(TokenState.normal)
-                .verifyComplete();
+                    .map(UserToken::getState)
+                    .as(StepVerifier::create)
+                    .expectNext(TokenState.normal)
+                    .verifyComplete();
 
         tokenManager.signOutByToken("test-token")
-                .as(StepVerifier::create)
-                .verifyComplete();
+                    .as(StepVerifier::create)
+                    .verifyComplete();
 
     }
 
@@ -80,65 +79,81 @@ public class RedisUserTokenManagerTest {
     @SneakyThrows
     public void testOfflineOther() {
         tokenManager.signIn("test-token_offline1", "offline", "user1", 1000)
-                .map(UserToken::getToken)
-                .as(StepVerifier::create)
-                .expectNext("test-token_offline1")
-                .verifyComplete();
+                    .map(UserToken::getToken)
+                    .as(StepVerifier::create)
+                    .expectNext("test-token_offline1")
+                    .verifyComplete();
 
         tokenManager.signIn("test-token_offline2", "offline", "user1", 1000)
-                .map(UserToken::getToken)
-                .as(StepVerifier::create)
-                .expectNext("test-token_offline2")
-                .verifyComplete();
+                    .map(UserToken::getToken)
+                    .as(StepVerifier::create)
+                    .expectNext("test-token_offline2")
+                    .verifyComplete();
 
         tokenManager.getByToken("test-token_offline1")
-                .map(UserToken::getState)
-                .as(StepVerifier::create)
-                .expectNext(TokenState.offline)
-                .verifyComplete();
+                    .map(UserToken::getState)
+                    .as(StepVerifier::create)
+                    .expectNext(TokenState.offline)
+                    .verifyComplete();
     }
 
     @Test
     @SneakyThrows
     public void testDeny() {
         tokenManager.signIn("test-token_offline3", "deny", "user2", 1000)
-                .map(UserToken::getToken)
-                .as(StepVerifier::create)
-                .expectNext("test-token_offline3")
-                .verifyComplete();
+                    .map(UserToken::getToken)
+                    .as(StepVerifier::create)
+                    .expectNext("test-token_offline3")
+                    .verifyComplete();
 
         tokenManager.signIn("test-token_offline4", "deny", "user2", 1000)
-                .map(UserToken::getToken)
-                .as(StepVerifier::create)
-                .expectError(AccessDenyException.class)
-                .verify();
+                    .map(UserToken::getToken)
+                    .as(StepVerifier::create)
+                    .expectError(AccessDenyException.class)
+                    .verify();
     }
 
     @Test
     @SneakyThrows
     public void testSignTimeout() {
         tokenManager.signIn("test-token_2", "test", "test2", 1000)
-                .map(UserToken::getToken)
-                .as(StepVerifier::create)
-                .expectNext("test-token_2")
-                .verifyComplete();
+                    .map(UserToken::getToken)
+                    .as(StepVerifier::create)
+                    .expectNext("test-token_2")
+                    .verifyComplete();
 
         tokenManager.touch("test-token_2")
-                .as(StepVerifier::create)
-                .expectComplete()
-                .verify();
+                    .as(StepVerifier::create)
+                    .expectComplete()
+                    .verify();
 
         Thread.sleep(2000);
         tokenManager.getByToken("test-token_2")
-                .switchIfEmpty(Mono.error(new UnAuthorizedException()))
-                .as(StepVerifier::create)
-                .expectError(UnAuthorizedException.class)
-                .verify();
+                    .switchIfEmpty(Mono.error(new UnAuthorizedException()))
+                    .as(StepVerifier::create)
+                    .expectError(UnAuthorizedException.class)
+                    .verify();
 
         tokenManager.getByUserId("test2")
-                .count()
-                .as(StepVerifier::create)
-                .expectNext(0L)
-                .verifyComplete();
+                    .count()
+                    .as(StepVerifier::create)
+                    .expectNext(0L)
+                    .verifyComplete();
+    }
+
+    @Test
+    public void testAuth() {
+        Authentication authentication = new SimpleAuthentication();
+
+        tokenManager.signIn("testAuth", "test", "test", 1000, authentication)
+                    .as(StepVerifier::create)
+                    .expectNextMatches(token -> token.getAuthentication() == authentication)
+                    .verifyComplete();
+
+        tokenManager.getByToken("testAuth")
+                    .cast(AuthenticationUserToken.class)
+                    .as(StepVerifier::create)
+                    .expectNextMatches(token -> token.getAuthentication() != null)
+                    .verifyComplete();
     }
 }