中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Spring中Security Remember me怎么用

發布時間:2021-08-09 11:39:49 來源:億速云 閱讀:186 作者:小新 欄目:編程語言

這篇文章主要介紹了Spring中Security Remember me怎么用,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

Remember me功能就是勾選"記住我"后,一次登錄,后面在有效期內免登錄。

先看具體配置:

pom文件:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Security的配置:

@Autowired
  private UserDetailsService myUserDetailServiceImpl; // 用戶信息服務
  @Autowired
  private DataSource dataSource; // 數據源
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    // formLogin()是默認的登錄表單頁,如果不配置 loginPage(url),則使用 spring security
    // 默認的登錄頁,如果配置了 loginPage()則使用自定義的登錄頁
    http.formLogin() // 表單登錄
        .loginPage(SecurityConst.AUTH_REQUIRE)
        .loginProcessingUrl(SecurityConst.AUTH_FORM) // 登錄請求攔截的url,也就是form表單提交時指定的action
        .successHandler(loginSuccessHandler)
        .failureHandler(loginFailureHandler)
        .and()
      .rememberMe()
        .userDetailsService(myUserDetailServiceImpl) // 設置userDetailsService
        .tokenRepository(persistentTokenRepository()) // 設置數據訪問層
        .tokenValiditySeconds(60 * 60) // 記住我的時間(秒)
        .and()
      .authorizeRequests() // 對請求授權
        .antMatchers(SecurityConst.AUTH_REQUIRE, securityProperty.getBrowser().getLoginPage()).permitAll() // 允許所有人訪問login.html和自定義的登錄頁
        .anyRequest() // 任何請求
        .authenticated()// 需要身份認證
        .and()
      .csrf().disable() // 關閉跨站偽造
    ;
  }
  /**
   * 持久化token
   * 
   * Security中,默認是使用PersistentTokenRepository的子類InMemoryTokenRepositoryImpl,將token放在內存中
   * 如果使用JdbcTokenRepositoryImpl,會創建表persistent_logins,將token持久化到數據庫
   */
  @Bean
  public PersistentTokenRepository persistentTokenRepository() {
    JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
    tokenRepository.setDataSource(dataSource); // 設置數據源
//    tokenRepository.setCreateTableOnStartup(true); // 啟動創建表,創建成功后注釋掉
    return tokenRepository;
  }

上面的myUserDetailServiceImpl是自己實現的UserDetailsService接口,dataSource會自動讀取數據庫配置。過期時間設置的3600秒,即一個小時

在登錄頁面加一行(name必須是remeber-me):

"記住我"基本原理:

Spring中Security Remember me怎么用

1、第一次發送認證請求,會被UsernamePasswordAuthenticationFilter攔截,然后身份認證。

認證成功后,在AbstracAuthenticationProcessingFilter中,有個RememberMeServices接口。

該接口默認實現類是NullRememberMeServices,這里會調用另一個實現抽象類AbstractRememberMeServices

// ...
  private RememberMeServices rememberMeServices = new NullRememberMeServices();
  protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
      Authentication authResult) throws IOException, ServletException {
    // ...
    SecurityContextHolder.getContext().setAuthentication(authResult);
    // 登錄成功后,調用RememberMeServices保存Token相關信息
    rememberMeServices.loginSuccess(request, response, authResult);
    // ...
  }

2、調用AbstractRememberMeServices的loginSuccess方法。

可以看到如果request中name為"remember-me"為true時,才會調用下面的onLoginSuccess()方法。這也是為什么上面登錄頁中的表單,name必須是"remember-me"的原因:

Spring中Security Remember me怎么用

3、在Security中配置了rememberMe()之后, 會由PersistentTokenBasedRememberMeServices去實現父類AbstractRememberMeServices中的抽象方法。

在PersistentTokenBasedRememberMeServices中,有一個PersistentTokenRepository,會生成一個Token,并將這個Token寫到cookie里面返回瀏覽器。PersistentTokenRepository的默認實現類是InMemoryTokenRepositoryImpl,該默認實現類會將token保存到內存中。這里我們配置了它的另一個實現類JdbcTokenRepositoryImpl,該類會將Token持久化到數據庫中

// ...

  private PersistentTokenRepository tokenRepository = new InMemoryTokenRepositoryImpl();
  protected void onLoginSuccess(HttpServletRequest request,
      HttpServletResponse response, Authentication successfulAuthentication) {
    String username = successfulAuthentication.getName();

    logger.debug("Creating new persistent login for user " + username);

    // 創建一個PersistentRememberMeToken
    PersistentRememberMeToken persistentToken = new PersistentRememberMeToken(
        username, generateSeriesData(), generateTokenData(), new Date());
    try {
      // 保存Token
      tokenRepository.createNewToken(persistentToken);
      // 將Token寫到Cookie中
      addCookie(persistentToken, request, response);
    }
    catch (Exception e) {
      logger.error("Failed to save persistent token ", e);
    }
  }

4、JdbcTokenRepositoryImpl將Token持久化到數據庫

/** The default SQL used by <tt>createNewToken</tt> */
  public static final String DEF_INSERT_TOKEN_SQL = "insert into persistent_logins (username, series, token, last_used) values(?,?,?,?)";

  public void createNewToken(PersistentRememberMeToken token) {
    getJdbcTemplate().update(insertTokenSql, token.getUsername(), token.getSeries(),
        token.getTokenValue(), token.getDate());
  }

查看數據庫,可以看到往persistent_logins 中插入了一條數據:

Spring中Security Remember me怎么用

5、重啟服務,發送第二次認證請求,只會攜帶Cookie。

所以直接會被RememberMeAuthenticationFilter攔截,并且此時內存中沒有認證信息。

可以看到,此時的RememberMeServices是由PersistentTokenBasedRememberMeServices實現

Spring中Security Remember me怎么用

6、在PersistentTokenBasedRememberMeServices中,調用processAutoLoginCookie方法,獲取用戶相關信息

protected UserDetails processAutoLoginCookie(String[] cookieTokens,
      HttpServletRequest request, HttpServletResponse response) {

    if (cookieTokens.length != 2) {
      throw new InvalidCookieException("Cookie token did not contain " + 2
          + " tokens, but contained '" + Arrays.asList(cookieTokens) + "'");
    }

    // 從Cookie中獲取Series和Token
    final String presentedSeries = cookieTokens[0];
    final String presentedToken = cookieTokens[1]; 

    //在數據庫中,通過Series查詢PersistentRememberMeToken
    PersistentRememberMeToken token = tokenRepository
        .getTokenForSeries(presentedSeries);

    if (token == null) {
      throw new RememberMeAuthenticationException(
          "No persistent token found for series id: " + presentedSeries);
    }

    // 校驗數據庫中Token和Cookie中的Token是否相同
    if (!presentedToken.equals(token.getTokenValue())) {
      tokenRepository.removeUserTokens(token.getUsername());

      throw new CookieTheftException(
          messages.getMessage(
              "PersistentTokenBasedRememberMeServices.cookieStolen",
              "Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack."));
    }

    // 判斷Token是否超時
    if (token.getDate().getTime() + getTokenValiditySeconds() * 1000L < System
        .currentTimeMillis()) {
      throw new RememberMeAuthenticationException("Remember-me login has expired");
    }

    if (logger.isDebugEnabled()) {
      logger.debug("Refreshing persistent login token for user '"
          + token.getUsername() + "', series '" + token.getSeries() + "'");
    }
    
    // 創建一個新的PersistentRememberMeToken
    PersistentRememberMeToken newToken = new PersistentRememberMeToken(
        token.getUsername(), token.getSeries(), generateTokenData(), new Date());

    try {
      //更新數據庫中Token
      tokenRepository.updateToken(newToken.getSeries(), newToken.getTokenValue(),
          newToken.getDate());
      //重新寫到Cookie
      addCookie(newToken, request, response);
    }
    catch (Exception e) {
      logger.error("Failed to update token: ", e);
      throw new RememberMeAuthenticationException(
          "Autologin failed due to data access problem");
    }
    //調用UserDetailsService獲取用戶信息
    return getUserDetailsService().loadUserByUsername(token.getUsername());
  }

7、獲取用戶相關信息后,再調用AuthenticationManager去認證授權

感謝你能夠認真閱讀完這篇文章,希望小編分享的“Spring中Security Remember me怎么用”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

林甸县| 宁海县| 株洲县| 什邡市| 大理市| 晋江市| 鄂尔多斯市| 遵义县| 壶关县| 宁乡县| 临潭县| 永泰县| 桂东县| 镇巴县| 胶南市| 宁南县| 修文县| 武威市| 日喀则市| 屏东市| 嘉定区| 扎囊县| 平遥县| 阿拉善盟| 江门市| 神农架林区| 衡阳市| 南平市| 尚志市| 藁城市| 大渡口区| 石首市| 长乐市| 高州市| 资兴市| 磴口县| 民乐县| 乐平市| 个旧市| 新河县| 黑河市|