private Mono<UserToken> signIn(String token,
                                   String type,
                                   String userId,
                                   long maxInactiveInterval,
                                   Consumer<Map<String, Object>> cacheBuilder) {
        long expires = maxTokenExpires.isNegative() ? maxInactiveInterval : Math.min(maxInactiveInterval, maxTokenExpires.toMillis());

        return Mono
                .defer(() -> {
                    Mono<SimpleUserToken> doSign = Mono.defer(() -> {
                        Map<String, Object> map = new HashMap<>();
                        map.put("token", token);
                        map.put("type", type);
                        map.put("userId", userId);
                        map.put("maxInactiveInterval", expires);
                        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)
                                .then(Mono.defer(() -> {
                                    if (expires > 0) {
                                        return operations.expire(key, Duration.ofMillis(expires));
                                    }
                                    return Mono.empty();
                                }))
                                .then(userTokenMapping.add(getUserRedisKey(userId), token))
                                .thenReturn(SimpleUserToken.of(map));
                    });

                    AllopatricLoginMode mode = allopatricLoginModes.getOrDefault(type, allopatricLoginMode);
                    if (mode == AllopatricLoginMode.deny) {
                        return userIsLoggedIn(userId)
                                .flatMap(r -> {
                                    if (r) {
                                        return Mono.error(new AccessDenyException("error.logged_in_elsewhere", TokenState.deny.getValue()));
                                    }
                                    return doSign;
                                });

                    } else if (mode == AllopatricLoginMode.offlineOther) {
                        return getByUserId(userId)
                                .flatMap(userToken -> {
                                    if (type.equals(userToken.getType())) {
                                        return this.changeTokenState(userToken.getToken(), TokenState.offline);
                                    }
                                    return Mono.empty();
                                })
                                .then(doSign);
                    }

                    return doSign;
                })
                .flatMap(this::onUserTokenCreated);
    }
@Test
    @SneakyThrows
    public void testDeny() {
        tokenManager.signIn("test-token_offline3", "deny", "user2", 1000)
                    .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();
    }