精通Spring Boot—— 第二十篇:Spring Security記住我功能

引言

本章的代碼實現是在上一篇教程:精通Spring Boot——第十九篇:Spring Security 整合驗證碼登陸基礎上,若是感受本篇跳躍幅度較大,可先閱讀上一篇,或訪問個人github.com(文末會附上地址),下載源碼閱讀。html

1.基本原理介紹

Spring Security記住我功能的基本原理: 首先當咱們的瀏覽器發送請求到UsernamePasswordAuthenticationFilter時,若是認證成功的話,會調用一個RememberMeService服務, 在RememberMeService中,存在一個TokenRepository,它會將token寫入到瀏覽器的cookie中,並將token同時寫入到數據庫。當用戶在下一次從新登陸時,服務請求會通過RememberMeAuthentionFilter,這個filter會讀取瀏覽器Cookie中的token,而後再傳遞給RememberMeService, RememberMeService再去數據庫中查詢該token是否有效,若該token有效,則會取出對應的用戶名,再去調用UserDetailsService獲取用戶信息,再將用戶信息放入SecurityContext裏面。 java

---圖片來源網絡git

RememberMeAuthentionFilter在Spring Security過濾器鏈中處於AuthenticationFileter中的最後,當其餘驗證方式沒法驗證用戶信息時,纔會調用RememberMeAuthentionFilter類來驗證用戶信息。 github

---圖片來源網絡web

2.如何實現

要實現記住我功能,在Spring Security中操做比較簡單,只須要實例化PersistentTokenRepository,而後在配置中增長rememberMe配置就行,具體代碼實現以下:spring

import com.linking.springsecurityremeberme.filter.CaptchaFilter;
import com.linking.springsecurityremeberme.handler.MyFailureHandler;
import com.linking.springsecurityremeberme.service.impl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

/**
 * @author developlee
 * @since 2019/1/18 15:30
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    private DataSource dataSource;

    @Bean
    public PersistentTokenRepository tokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        // 設置爲true,則項目啓動時,會在對應數據源中自動建表token表
        jdbcTokenRepository.setCreateTableOnStartup(false);
        return jdbcTokenRepository;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Autowired
    private AppConfig appConfig;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin().loginPage("/sign_in").loginProcessingUrl(appConfig.getLoginUri())
                .defaultSuccessUrl("/welcome").permitAll()
                .failureHandler(new MyFailureHandler())
                .and().authorizeRequests().antMatchers("/code/image").permitAll()
                .and().addFilterBefore(new CaptchaFilter(appConfig, new MyFailureHandler()), UsernamePasswordAuthenticationFilter.class)
                .logout().logoutUrl("/auth/logout").clearAuthentication(true)
                .and().rememberMe().tokenRepository(tokenRepository())//設置tokenRepository
                .alwaysRemember(true) // 老是記住,會刷新過時時間
                .tokenValiditySeconds(300)// 設置過時時間爲5分鐘
                .userDetailsService(userDetailsService) // 設置userDetailsService,用來獲取username
                .and().authorizeRequests().anyRequest().authenticated();
    }
}

在頁面中,新增記住我sql

<span><input name="rememberMe" type="checkbox" th:value="true">記住我</span>

3.測試

通過上面的步驟,咱們基本上已經完成了如何添加記住我功能,接下來咱們在頁面上輸入用戶名和密碼,並同時點擊記住我。登陸成功後,會跳轉至welcome.html頁。而後重啓系統,清空服務器session, 再次去訪問welcome.html,看看可否直接訪問。 點擊登陸,已經成功跳轉到welcome.html,重啓清空session,直接在地址輸入http:localhost:8080/welcome.html 直接可以訪問,說明在請求在通過Spring Security過濾器鏈時,讀取了cookies中的token,並經過token去查找了用戶的信息,再經過userDetailsService進行了登陸。數據庫

4.記住我源碼分析

OK,咱們先從第一步登陸操做開始提及,第一次登陸時,必然會通過UsernamePasswordAuthenticationFilter,因此把斷點設置在該類中 而後接着代碼走走走,下一步來到successfulAuthentication這個方法,也就是登陸成功後的處理方法,能夠看到這裏rememberService會將登陸成功的用戶信息保存 瀏覽器

OK,再接着走,看下這個rememberService.onLoginSuccess具體實現 能夠看到,這個方法,會將一條token記錄插入到數據庫中,並寫入到Cookie。到這裏爲止,登陸操做基本已經完成。 接下來從新啓動下服務,並直接訪問welcome.html,看看代碼的執行順序如何。斷點要打在RememberMeService中的autoLogin方法。 看下實現,首先是判斷cookie是否爲null和爲空,接着往下走,來到decodeCookie解析cookie,而後經過processAutoLoginCookie這個方法,去數據庫中經過cookieToken查詢用戶信息,接下來再對用戶信息進行校驗(用戶更換了密碼之類的狀況),check成功後,再執行登陸操做,寫入用戶信息到SecurityContext中。 服務器

如下是processAutoLoginCookie方法的具體實現。

在Spring Security中實現記住我功能有兩種實現,一種是上面介紹的PersistentTokenBasedRememberMeService,一種是TokenBasedRememberService。 這兩種的區別主要在於什麼呢? 它們是AbstractRememberMeServices.onLoginSuccess方法的不一樣實現。 上面介紹的PersistentTokenBasedRememberMeService,是生成token的key,expireTime等信息保存在數據庫中的,而TokenBasedRememberService則是將key,expireTime等信息保存在客戶端(瀏覽器)中的,驗證方式也全部不一樣。 看下TokenBasedRememberService中對token的驗證明現,大概能夠得出這樣的結論

5.總結

這篇的內容主要目的是爲系統增長‘記住我’功能,相對比較簡單,代碼的編寫量也比較少。在本篇咱們也分析了一下Spring Security中實現這個功能的源碼,須要實現的主要是PersistentTokenRepository,並在配置中增長RememberMe的配置。 本篇的源代碼可在個人github.com中找到,歡迎你們star 精通Spring Boot系列和follow我本人,也能夠加我本人微信探討技術,感謝您觀看此文,謝謝!

相關文章
相關標籤/搜索