美文网首页
Spring Security RememberMe Redis

Spring Security RememberMe Redis

作者: 不敢预言的预言家 | 来源:发表于2019-07-22 15:26 被阅读0次

    本配置使用form表单登录的方式
    使用redis保存rememberMe过程中产生的token

    项目源码:https://github.com/dk980241/spring-boot-security-demo
    site.yuyanjia.springbootsecuritydemo.config.FormWebSecurityConfig

    /**
         * http安全配置
         *
         * @param http
         * @throws Exception
         */
        @Override
        protected void configure(HttpSecurity http) throws Exception {
         .....
         .....
         .....
            http
                    .rememberMe()
                    .rememberMeParameter(REMEMBER_ME)
                    .tokenRepository(new RedisTokenRepositoryImpl());
    
        }
    

    Token 仓库实现

        /**
         * redis保存用户token
         * <p>
         * remember me
         * <p>
         * {@link org.springframework.security.web.authentication.rememberme.InMemoryTokenRepositoryImpl}
         * {@link org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices}
         * {@link org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken}
         * PersistentRememberMeToken 没有实现Serializable,无法进行序列化,自定义存储数据结构
         */
        class RedisTokenRepositoryImpl implements PersistentTokenRepository {
            @Override
            public void createNewToken(PersistentRememberMeToken token) {
                if (log.isDebugEnabled()) {
                    log.debug("token create seriesId: [{}]", token.getSeries());
                }
                String key = generateKey(token.getSeries());
                HashMap<String, String> map = new HashMap();
                map.put("username", token.getUsername());
                map.put("tokenValue", token.getTokenValue());
                map.put("date", String.valueOf(token.getDate().getTime()));
                redisTemplate.opsForHash().putAll(key, map);
                redisTemplate.expire(key, TOKEN_VALID_DAYS, TimeUnit.DAYS);
            }
    
            @Override
            public void updateToken(String series, String tokenValue, Date lastUsed) {
                String key = generateKey(series);
                HashMap<String, String> map = new HashMap();
                map.put("tokenValue", tokenValue);
                map.put("date", String.valueOf(lastUsed.getTime()));
                redisTemplate.opsForHash().putAll(key, map);
                redisTemplate.expire(key, TOKEN_VALID_DAYS, TimeUnit.DAYS);
            }
    
            @Override
            public PersistentRememberMeToken getTokenForSeries(String seriesId) {
                String key = generateKey(seriesId);
                List<String> hashKeys = new ArrayList<>();
                hashKeys.add("username");
                hashKeys.add("tokenValue");
                hashKeys.add("date");
                List<String> hashValues = redisTemplate.opsForHash().multiGet(key, hashKeys);
                String username = hashValues.get(0);
                String tokenValue = hashValues.get(1);
                String date = hashValues.get(2);
                if (null == username || null == tokenValue || null == date) {
                    return null;
                }
                Long timestamp = Long.valueOf(date);
                Date time = new Date(timestamp);
                PersistentRememberMeToken token = new PersistentRememberMeToken(username, seriesId, tokenValue, time);
                return token;
            }
    
            @Override
            public void removeUserTokens(String username) {
                if (log.isDebugEnabled()) {
                    log.debug("token remove username: [{}]", username);
                }
                byte[] hashKey = redisTemplate.getHashKeySerializer().serialize("username");
                RedisConnection redisConnection = redisTemplate.getConnectionFactory().getConnection();
                try (Cursor<byte[]> cursor = redisConnection.scan(ScanOptions.scanOptions().match(generateKey("*")).count(1024).build())) {
                    while (cursor.hasNext()) {
                        byte[] key = cursor.next();
                        byte[] hashValue = redisConnection.hGet(key, hashKey);
                        String storeName = (String) redisTemplate.getHashValueSerializer().deserialize(hashValue);
                        if (username.equals(storeName)) {
                            redisConnection.expire(key, 0L);
                            return;
                        }
                    }
                } catch (IOException ex) {
                    log.warn("token remove exception", ex);
                }
            }
    
            /**
             * 生成key
             *
             * @param series
             * @return
             */
            private String generateKey(String series) {
                return "spring:security:rememberMe:token:" + series;
            }
        }
    

    验证

    1. 登录,保存cookie
      • curl -H "content-type:application/x-www-form-urlencoded" -X POST -c cookie.txt -d "username=yuyanjia&password=yuyanjiademima&rememberMe" http://127.0.0.1:8080/security-demo/authc/login
    • image.png
    1. 清除掉redis中缓存的session信息,触发授权验证
    • image.png
    1. 请求一个需要授权才能访问的页面做验证
    • curl -X POST -b cookie.txt http://127.0.0.1:8080/security-demo/authc/watch

    相关文章

      网友评论

          本文标题:Spring Security RememberMe Redis

          本文链接:https://www.haomeiwen.com/subject/eilwlctx.html