自动登录和注销登录-实战四

自动登录时将用户的登录信息保存在用户浏览器的cookie中,当用户下次访问时,自动实现校验并建立登录态的一种机制。
Spring Security提供了两种非常好的令牌:

  1. 用散列算法加密用户必要的登录信息并生成令牌。
  2. 数据库等持久性数据存储机制用的持久化令牌。

一:散列加密方案
首先在静态页面加上

<div>
 <p><input name="remember-me" type="checkbox" value="true">记住我</p>
</div>

接着在Spring Security的配置类配置.

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    MyUserDetailsServiceImpl myUserDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/api/**").hasAuthority("admin")
                //开放/captcha.jpg的访问权限
                .antMatchers("/app/api/**","/captcha.jpg").permitAll()
                .antMatchers("/user/api/**").hasAuthority("user")
                .anyRequest().authenticated()
                .and().formLogin()
                .failureHandler(new SecurityAuthenticationFailureHandler())
                .successHandler(new SecurityAuthenticationSuccessHandler())
                .loginPage("/myLogin.html")
                .loginProcessingUrl("/login")
                .permitAll()
                .and().rememberMe()
                .userDetailsService(myUserDetailsService)
                // 1. 散列加密方案
                .key("blurool")
                .and().csrf().disable();

        // 将过滤器添加在UsernamePasswordAuthenticationFilter之前
        http.addFilterBefore(new VerificationCodeFilter(), UsernamePasswordAuthenticationFilter.class);
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
}

如果没有指定时,key是一个UUID字符串,Spring Security会在每次表单登录成功后更新此令牌。这将导致每次重启服务后,key都会重新生成,使得重启之前的所有自动登录cookie失效。除此之外,在多实例部署的情况下,由于实例间的key并不相同,所以当用户访问系统的另一个实例时,自动登录策略就会失效。解决办法是指定一个唯一的key。

二:持久化令牌方案
在持久化令牌方案中,最核心的是series和token两个值,它们都是用MD5散列过的随机字符串。不同的是,series仅在用户使用密码重新登录时更新,而token会在每个新的session中都重新生成。这样设计,可以解决散列加密方案中一个令牌可以同时在多端登录的问题。每个会话都会引发token的更新,即每个token仅支持单实例登录。
其次,自动登录不会导致series变更,而每次自动登录都需要同时验证series和token两个值,当该令牌还未使用过自动登录就被盗取时,系统会在非法用户验证通过后刷新token值,此时在合法用户的浏览器中,该token值已经失效。当合法用户使用自动登录时,由于该series对应的token不同,系统可以推断该令牌可能已被盗用,从而做一些处理。例如,清理该用户的所有自动登录令牌,并通知该用户可能已被盗号等。

持久化令牌配置如下

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    DataSource dataSource;
    @Autowired
    MyUserDetailsServiceImpl myUserDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        http.authorizeRequests()
                .antMatchers("/admin/api/**").hasAuthority("admin")
                //开放/captcha.jpg的访问权限
                .antMatchers("/app/api/**","/captcha.jpg").permitAll()
                .antMatchers("/user/api/**").hasAuthority("user")
                .anyRequest().authenticated()
                .and().formLogin()
                .failureHandler(new SecurityAuthenticationFailureHandler())
                .successHandler(new SecurityAuthenticationSuccessHandler())
                .loginPage("/myLogin.html")
                .loginProcessingUrl("/login")
                .permitAll()
                .and().rememberMe()
                .userDetailsService(myUserDetailsService)
                //cookie通过Base64解码:6KqcpfMU+ykV5T8o+lTWXg==:UhOurgglqFMoGYpyMutUpg==
                //冒号前是series,冒号后是token
                .tokenRepository(jdbcTokenRepository)  // 持久化令牌方案
                // 7天有效期
                .tokenValiditySeconds(60 * 60 * 24 * 7)
                .and().csrf().disable();

        // 将过滤器添加在UsernamePasswordAuthenticationFilter之前
        http.addFilterBefore(new VerificationCodeFilter(), UsernamePasswordAuthenticationFilter.class);
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
}

对于上面配置的tokenRepository持久化令牌,我们可以按照自己的方式实现PersistentTokenRepository 接口,也可以使用Spring Security提供的JDBC方案实现。上面使用JDBC方案。

public interface PersistentTokenRepository {

   void createNewToken(PersistentRememberMeToken token);

   void updateToken(String series, String tokenValue, Date lastUsed);

   PersistentRememberMeToken getTokenForSeries(String seriesId);

   void removeUserTokens(String username);

}

显然,两种方案都存在被盗取导致身份被暂时利用的可能,如果有更高的安全性需求,建议使用Spring Security提供的令牌持久化方案。当然,最安全的方式还是尽量不使用自动登录,但很多时候,在实际开发中,优质体验比不可预期的安全风险要更为优先。
如果决定提供自动登录功能,就应当限制cookie登录时的部分执行权限。例如,修改密码,修改邮箱(防止找回密码),查看隐私信息(如完整的手机号码,银行卡号等)等,校验登录密码或设置独立密码来做二次校验也是不错的方案。

三:注销登录
配置如下:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    DataSource dataSource;
    @Autowired
    MyUserDetailsServiceImpl myUserDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        http.authorizeRequests()
                .antMatchers("/admin/api/**").hasAuthority("admin")
                //开放/captcha.jpg的访问权限
                .antMatchers("/app/api/**","/captcha.jpg").permitAll()
                .antMatchers("/user/api/**").hasAuthority("user")
                .anyRequest().authenticated()
                .and().formLogin()
                .failureHandler(new SecurityAuthenticationFailureHandler())
                .successHandler(new SecurityAuthenticationSuccessHandler())
                .loginPage("/myLogin.html")
                .loginProcessingUrl("/login")
                .permitAll()
                .and().rememberMe()
                .userDetailsService(myUserDetailsService)
                //6KqcpfMU+ykV5T8o+lTWXg==:UhOurgglqFMoGYpyMutUpg==
                //冒号前是series,冒号后是token
                .tokenRepository(jdbcTokenRepository)  // 持久化令牌方案
                // 7天有效期
                .tokenValiditySeconds(60 * 60 * 24 * 7)
                .and()
                .logout()
                //指定接收注销请求的路由
                .logoutUrl("/myLogout")
                // 注销成功,重定向到该路径下
                .logoutSuccessUrl("/")
                //使该用户的HttpSession失效
                .invalidateHttpSession(true)
                .deleteCookies("JSESSIONID","remember-me")
                .and().csrf().disable();

        // 将过滤器添加在UsernamePasswordAuthenticationFilter之前
        http.addFilterBefore(new VerificationCodeFilter(), UsernamePasswordAuthenticationFilter.class);
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值