SpringSecurity(十): 只允許一個用戶登錄


只允許一個用戶在一個地方登錄,也是每個用戶在系統中只能有一個Session。如果同一用戶在第2個地方登錄,則將第1個踢下線。

1.自定義 CustomSessionInformationExpiredStrategy 實現類來定制策略

/**
 * 同一用戶只允許一台電腦登錄
 * 同一用戶在第2個地方登錄,則將第1個踢下線
 * 當同一用戶的 session 達到指定數量時,執行此類
 */
@Component("customSessionInformationExpiredStrategy")
public class CustomSessionInformationExpiredStrategy implements SessionInformationExpiredStrategy {
    @Autowired
    CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
    @Override
    public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException {
        //獲取用戶名
        UserDetails userDetails = (UserDetails) event.getSessionInformation().getPrincipal();
        AuthenticationException exception = new AuthenticationServiceException(String.format("[%s]用戶在另外一台電腦登錄,您已被下線", userDetails.getUsername()));
        try {
            //當用戶在另外一台電腦登錄后,交給失敗處理器響應給前端json數據
            customAuthenticationFailureHandler.onAuthenticationFailure(event.getRequest(), event.getResponse(), exception);
        } catch (ServletException e) {
            e.printStackTrace();
        }
    }
}

2.將自定義CustomSessionInformationExpiredStrategy實例 注入到安全配置類SpringSecurityConfig中,並進行配置

/**
 * 安全配置類作為安全控制中心, 用於實現身份認證與授權配置功能
 */
@Configuration
@EnableWebSecurity //啟動 SpringSecurity 過濾器鏈功能
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    SecurityProperties securityProperties;

    Logger logger = LoggerFactory.getLogger(SpringSecurityConfig.class);


    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        // 加密存儲   明文+隨機鹽值
        return new BCryptPasswordEncoder();
    }


    @Autowired
    CustomUserDetailsService customUserDetailsService;


    /**
     * 認證管理器:
     * 1、認證信息提供方式(用戶名、密碼、當前用戶的資源權限)
     * 2、可采用內存存儲方式,也可能采用數據庫方式等
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //基於內存存儲認證信息 存儲的密碼必須是加密后的 不然會報錯:There is no PasswordEncoder mapped for the id "null"
        //auth.inMemoryAuthentication().withUser("zcc").password("123").authorities("ADMIN");
        /*String password = bCryptPasswordEncoder().encode("123");
        logger.info("加密后的密碼:" + password);
        auth.inMemoryAuthentication().withUser("zcc").password(password).authorities("ADMIN");*/


        // 指定使用自定義查詢用戶信息來完成身份認證
        auth.userDetailsService(customUserDetailsService);

    }



    @Autowired
    CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
    @Autowired
    CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
    @Autowired
    ImageVerifyCodeValidateFilter imageVerifyCodeValidateFilter;

    @Autowired
    SmsVerifyCodeValidateFilter smsVerifyCodeValidateFilter;
    @Autowired
    MobileAuthenticationConfig mobileAuthenticationConfig;

    @Autowired
    CustomInvalidSessionStrategy customInvalidSessionStrategy;

    @Autowired CustomSessionInformationExpiredStrategy customSessionInformationExpiredStrategy; /**
     * 記住我 功能
     */
    @Autowired
    DataSource dataSource;
    @Bean
    public JdbcTokenRepositoryImpl jdbcTokenRepository(){
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        // 是否啟動時自動創建表,第一次啟動創建就行,后面啟動把這個注釋掉,不然報錯已存在表
        //jdbcTokenRepository.setCreateTableOnStartup(true);
        return jdbcTokenRepository;
    }

    /**
     * 資源權限配置(過濾器鏈):
     * 1、被攔截的資源
     * 2、資源所對應的角色權限
     * 3、定義認證方式:httpBasic 、httpForm
     * 4、定制登錄頁面、登錄請求地址、錯誤處理方式
     * 5、自定義 spring security 過濾器
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //http.httpBasic()//采用httpBasic 認證方式
        /*http.formLogin()
                .loginPage("/login/page")// 交給 /login/page 響應認證(登錄)頁面
                .loginProcessingUrl("/login/form")  // 登錄表單提交處理Url, 默認是 /login
                .usernameParameter("name") // 默認用戶名的屬性名是 username
                .passwordParameter("pwd") // 默認密碼的屬性名是 password
                .and()
                .authorizeRequests()//認證請求
                .antMatchers("/login/page").permitAll()//自定義登錄頁不需要認證
                .anyRequest().authenticated();// 所有進入應用的HTTP請求都要進行認證*/

        http
                .addFilterBefore(imageVerifyCodeValidateFilter, UsernamePasswordAuthenticationFilter.class)//將校驗過濾器 imageCodeValidateFilter 添加到 UsernamePasswordAuthenticationFilter 前面
                .addFilterBefore(smsVerifyCodeValidateFilter,UsernamePasswordAuthenticationFilter.class)//將校驗過濾器 smsVerifyCodeValidateFilter 添加到 UsernamePasswordAuthenticationFilter 前面
                .formLogin()
                .loginPage(securityProperties.getLoginPage())// 交給 /login/page 響應認證(登錄)頁面
                .loginProcessingUrl(securityProperties.getLoginProcessingUrl())  // 登錄表單提交處理Url, 默認是 /login
                .usernameParameter(securityProperties.getUsernameParameter()) // 默認用戶名的屬性名是 username
                .passwordParameter(securityProperties.getPasswordParameter()) // 默認密碼的屬性名是 password
                .successHandler(customAuthenticationSuccessHandler)//自定義認證成功處理器
                .failureHandler(customAuthenticationFailureHandler)//自定義認證失敗處理器
                .and()
                .authorizeRequests()//認證請求
                .antMatchers(securityProperties.getLoginPage(),securityProperties.getMobilePage(),securityProperties.getImageCodeUrl(),securityProperties.getMobileCodeUrl()).permitAll()//自定義登錄頁不需要認證,生成圖片驗證碼,發送短信獲取驗證碼也不需要驗證
                .anyRequest().authenticated()// 所有進入應用的HTTP請求都要進行認證
                .and()
                .rememberMe()//記住我功能
                .tokenRepository(jdbcTokenRepository())//保存登錄信息
                .tokenValiditySeconds(securityProperties.getTokenValiditySeconds())//記住我有效時長一周
                .and()
                .sessionManagement()//session會話管理
                .invalidSessionStrategy(customInvalidSessionStrategy)//當session失效后的處理類
                .maximumSessions(1)// 每個用戶在系統中的最大session數
                .expiredSessionStrategy(customSessionInformationExpiredStrategy)//當用戶達到最大session數后,則調用此處的實現
        ;

        // 將手機相關的配置綁定過濾器鏈上
        http.apply(mobileAuthenticationConfig);
    }

    /**
     * 放行靜態資源(js css 等)
     *
     * @param web
     */
    @Override
    public void configure(WebSecurity web) {
        //web.ignoring().antMatchers("/dist/**", "/modules/**", "/plugins/**");
        web.ignoring().antMatchers(securityProperties.getStaticPaths());
    }
}

3、測試

 完整代碼地址:https://gitee.com/zhechaochao/security-parent.git


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM