美文网首页
Spring Security Remember-me配置

Spring Security Remember-me配置

作者: LoWang | 来源:发表于2019-03-26 08:07 被阅读0次

    scio

    https://github.com/rench/scio

    Remember Me 记住我

    介绍

    • Spring Security框架中提供了一种基于token的记住我的功能,一般的记住我功能主要用于用户在当前浏览器中首次输入密码后,即可在以后指定的一段时间内,不用输入密码即可访问受保护的数据短点,即使session失效。

    实现

    • 记住的我实现是基于浏览器对于cookie的持久化功能,在用户首次勾选记住我登录时,服务端基于当前用户登录信息构造一个加密后的token,该token在Spring中的实现主要有两种:
    1. TokenBasedRememberMeServices 生成cookie中包含用户名的token,每次请求从带入的cookie中获取username,然后根据username从userDetailsService中获取数据生成token进行比较。生成的方式如下:
    username + ":" + expiryTime + ":"
            + Md5Hex(username + ":" + expiryTime + ":" + password + ":" + key)
    
    1. PersistentTokenBasedRememberMeServices 生成的cookie中不包含用户名,包含series和token,并存储于数据库中,同时,存储的数据中还包含了用户名和token最近访问时间。每次请求根据带入cookie中的series和token,查询数据库中的数据进行token比对,同时调用userDetailsService进行用户的状态校验。

    PersistentTokenRepositoryPersistentTokenBasedRememberMeServices中存储实现。默认是InMemoryTokenRepositoryImpl,框架中附带了JdbcTokenRepositoryImpl实现,可以根据自己的实际情况增加实现,比如redis等。

    1. 基于TokenBasedRememberMeServices,可以进行扩展,对cookie进行加密等操作,默认的cookie(mp1:1554694664576:801c3f24109a468dfbeb7cf09cac0531)没有加密用户名,只是进行base64编码,可能存在泄漏的风险。

    配置

    • SecurityConfig
    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class ScioRememberMeSecurityConfig extends WebSecurityConfigurerAdapter {
      private static final String SECRET = "scio@2019";
      @Autowired private UserDetailsService scioUserDetailsService;
      /**
       * password encoder
       *
       * @return
       */
      @Bean
      public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
      }
    
      /**
       * RememberMeAuthenticationProvider.
       *
       * @return
       */
      @Bean
      public RememberMeAuthenticationProvider rememberMeAuthenticationProvider() {
        return new RememberMeAuthenticationProvider(SECRET);
      }
    
      /**
       * TokenBasedRememberMeServices.
       *
       * @return
       */
      @Bean("tokenBaseRememberMeServices")
      public TokenBasedRememberMeServices tokenBasedRememberMeServices() {
        TokenBasedRememberMeServices rememberMeServices =
            new TokenBasedRememberMeServices(SECRET, scioUserDetailsService);
        rememberMeServices.setAlwaysRemember(false);
        rememberMeServices.setCookieName("remember-me");
        rememberMeServices.setTokenValiditySeconds(AbstractRememberMeServices.TWO_WEEKS_S);
        return rememberMeServices;
      }
    
      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(scioUserDetailsService).passwordEncoder(passwordEncoder());
      }
    
      @Override
      protected void configure(HttpSecurity http) throws Exception {
        // http.antMatcher("/api/**");
        // .antMatchers("/api/login", "/api/logout")
        http.csrf()
            .disable()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
        // permit login
        http.formLogin()
            .permitAll()
            .defaultSuccessUrl("/info")
            .and()
            .authorizeRequests()
            .anyRequest()
            .authenticated();
        http.rememberMe()
            // tokenBased, store token and user name in token data.
            .rememberMeServices(tokenBasedRememberMeServices())
            // .tokenRepository(new InMemoryTokenRepositoryImpl())
            // persistent serials/token, invalidate after delete from token store
            .and()
            .authenticationProvider(rememberMeAuthenticationProvider());
        http.headers().cacheControl();
      }
    }
    
    • ScioUserDetailsService
    /**
     * mock scio users
     *
     * @author Wang.ch
     * @date 2019-03-25 09:05:21
     */
    @Configuration
    public class ScioUserDetailsService implements UserDetailsService {
      /** mock users */
      private Map<String, String> users = new HashMap<>();
    
      public ScioUserDetailsService() {
        users.put("mp1", "{noop}mp1");
        users.put("mp2", "{noop}mp2");
      }
    
      @Override
      public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        if (users.containsKey(username)) {
          String noopPwd = users.get(username);
          User u = new User(username, noopPwd, Arrays.asList(new SimpleGrantedAuthority("USER")));
          return u;
        } else {
          throw new UsernameNotFoundException("user not found");
        }
      }
    }
    

    使用

    • 配置中已开启formLogin,直接访问http://localhost:8006/login,跳转到登录界面
    • 输入配这中mock的用户名和密码,mp1:mp1,勾选中Remember me on this computer,提交。
    • 观察返回的cookie值 Cookie: remember-me=bXAxOjE1NTQ3NjkxNDUwNjE6YjBmNWM4Nzk1NzVjOWMzNWIxNjhkZDJlMDg0MTliZWM; JSESSIONID=B490108E0CFE931DC624C43DCCD02D94 ,多了一个remember-me,这就是返回的token,他有一个有效时间,在关闭浏览器后重新访问 http://localhost:8006/info,依然可以访问当前登录用户的信息。
    • 跟踪 TokenBasedRememberMeServices 源码进去,能够发现会从cookie中解析remember-me字段,并对值进行解析,然后对用户信息进行匹配,然后构造 RememberMeAuthenticationToken 返回,然后调用 authenticationManager.authenticate 进行授权,使用的实例是RememberMeAuthenticationProvider

    相关文章

      网友评论

          本文标题:Spring Security Remember-me配置

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