国产成人精品久久免费动漫-国产成人精品天堂-国产成人精品区在线观看-国产成人精品日本-a级毛片无码免费真人-a级毛片毛片免费观看久潮喷

您的位置:首頁技術文章
文章詳情頁

Spring Security 實現“記住我”功能及原理解析

瀏覽:37日期:2023-09-05 08:05:36

這章繼續擴展功能,來一個“記住我”的功能實現,就是說用戶在登錄一次以后,系統會記住這個用戶一段時間,這段時間內用戶不需要重新登錄就可以使用系統。

記住我功能基本原理

原理說明

用戶登錄發送認證請求的時候會被UsernamePasswordAuthenticationFilter認證攔截,認證成功以后會調用一個RememberMeService服務,服務里面有一個TokenRepository,這個服務會生成一個Token,然后將Token寫入到瀏覽器的Cookie同時會使用TokenRepository把生成的Token寫到數據庫里面,因為這個動作是在認證成功以后做的,所以在Token寫入數據庫的時候會把用戶名同時寫入數據庫。 假如瀏覽器關了重新訪問系統,用戶不需要再次登錄就可以訪問,這個時候請求在過濾器鏈上會經過RememberMeAuthenticationFilter,這個過濾器的作用是讀取Cookie中的Token交給RemeberMeService,RemeberMeService會用TokenRepository到數據庫里去查這個Token在數據庫里有沒有記錄,如果有記錄就會把用戶名取出來,取出來以后會進行各種校驗然后生成新Token再調用之前的UserDetailService,去獲取用戶的信息,然后把用戶信息放到SecurityContext里面,到這里就把用戶給登錄上了。

圖解說明

Spring Security 實現“記住我”功能及原理解析

RememberMeAuthenticationFilter位于過濾器鏈的哪一環?

圖解

Spring Security 實現“記住我”功能及原理解析

首先其他認證過濾器會先進行認證,當其他過濾器都無法認證時,RememberMeAuthenticationFilter會嘗試去做認證。

記住我功能具體實現

前端頁面

登錄的時候加上一行記住我的勾選按鈕,這里要注意,name一定要是remember-me,下面源碼部分會提到。

<tr><td colspan=’2’><input name='remember-me' type='checkbox' value='true' />記住我</td></tr>

后臺

首先配置TokenRepositoryBean

/** * 記住我功能的Token存取器配置 * * @return */@Beanpublic PersistentTokenRepository persistentTokenRepository() {JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();tokenRepository.setDataSource(dataSource);// 啟動的時候自動創建表,建表語句 JdbcTokenRepositoryImpl 已經都寫好了tokenRepository.setCreateTableOnStartup(true);return tokenRepository;}

然后需要在 configure 配置方法那邊進行記住我功能所有組件的配置

protected void configure(HttpSecurity http) throws Exception {ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class).formLogin().loginPage('/authentication/require').loginProcessingUrl('/authentication/form').successHandler(meicloudAuthenticationSuccessHandler).failureHandler(meicloudAuthenticationFailureHandler)// 配置記住我功能.and().rememberMe()// 配置TokenRepository.tokenRepository(persistentTokenRepository())// 配置Token過期時間.tokenValiditySeconds(3600)// 最終拿到用戶名之后,使用UserDetailsService去做登錄.userDetailsService(userDetailsService).and().authorizeRequests().antMatchers('/authentication/require', securityProperties.getBrowser().getSignInPage(), '/code/image').permitAll().anyRequest().authenticated().and().csrf().disable();}

記住我功能Spring Security源碼解析

登錄之前“記住我”源碼流程

在認證成功之后,會調用successfulAuthentication方法(這些第五章源碼部分已經學習過),在將認證信息保存到Context后,RememberMeServices就會調用它的loginSuccess方法。

protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { if (this.logger.isDebugEnabled()) { this.logger.debug('Authentication success. Updating SecurityContextHolder to contain: ' + authResult); } SecurityContextHolder.getContext().setAuthentication(authResult); this.rememberMeServices.loginSuccess(request, response, authResult); if (this.eventPublisher != null) { this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass())); } this.successHandler.onAuthenticationSuccess(request, response, authResult); }

loginSuccess方法里面會先檢查請求中是否有name為remember-me的參數,有才進行下一步。

public final void loginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) { // this.parameter = 'remember-me' if (!this.rememberMeRequested(request, this.parameter)) { this.logger.debug('Remember-me login not requested.'); } else { this.onLoginSuccess(request, response, successfulAuthentication); } }

再進入onLoginSuccess方法,里面主要就是進行寫庫和寫Cookie的操作。

protected void onLoginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) { String username = successfulAuthentication.getName(); this.logger.debug('Creating new persistent login for user ' + username); // 生成Token PersistentRememberMeToken persistentToken = new PersistentRememberMeToken(username, this.generateSeriesData(), this.generateTokenData(), new Date()); try { // 將Token和userName插入數據庫 this.tokenRepository.createNewToken(persistentToken); // 將Token寫到Cookie中 this.addCookie(persistentToken, request, response); } catch (Exception var7) { this.logger.error('Failed to save persistent token ', var7); } }

登錄之后“記住我”源碼流程

首先會進入RememberMeAuthenticationFilter,會先判斷前面的過濾器是否進行過認證(Context中是否有認證信息),未進行過認證的話會調用RememberMeServices的autoLogin方法。

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest)req; HttpServletResponse response = (HttpServletResponse)res; if (SecurityContextHolder.getContext().getAuthentication() == null) { Authentication rememberMeAuth = this.rememberMeServices.autoLogin(request, response); if (rememberMeAuth != null) { try { rememberMeAuth = this.authenticationManager.authenticate(rememberMeAuth); SecurityContextHolder.getContext().setAuthentication(rememberMeAuth); this.onSuccessfulAuthentication(request, response, rememberMeAuth); if (this.logger.isDebugEnabled()) { this.logger.debug('SecurityContextHolder populated with remember-me token: ’' + SecurityContextHolder.getContext().getAuthentication() + '’'); } if (this.eventPublisher != null) { this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(SecurityContextHolder.getContext().getAuthentication(), this.getClass())); } if (this.successHandler != null) { this.successHandler.onAuthenticationSuccess(request, response, rememberMeAuth); return; } } catch (AuthenticationException var8) { if (this.logger.isDebugEnabled()) { this.logger.debug('SecurityContextHolder not populated with remember-me token, as AuthenticationManager rejected Authentication returned by RememberMeServices: ’' + rememberMeAuth + '’; invalidating remember-me token', var8); } this.rememberMeServices.loginFail(request, response); this.onUnsuccessfulAuthentication(request, response, var8); } } chain.doFilter(request, response); } else { if (this.logger.isDebugEnabled()) { this.logger.debug('SecurityContextHolder not populated with remember-me token, as it already contained: ’' + SecurityContextHolder.getContext().getAuthentication() + '’'); } chain.doFilter(request, response); } }

autoLogin方法里面,主要調用this.processAutoLoginCookie(cookieTokens, request, response)這個方法獲取數據庫中的用戶信息,其步驟是:

解析前端傳來的Cookie,里面包含了Token和seriesId,它會使用seriesId查找數據庫的Token 檢查Cookie中的Token和數據庫查出來的Token是否一樣 一樣的話再檢查數據庫中的Token是否已過期 如果以上都符合的話,會使用舊的用戶名和series重新new一個Token,這時過期時間也重新刷新 然后將新的Token保存回數據庫,同時添加回Cookie中 最后再調用UserDetailsService的loadUserByUsername方法返回UserDetails

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) + '’'); } else { String presentedSeries = cookieTokens[0]; String presentedToken = cookieTokens[1]; PersistentRememberMeToken token = this.tokenRepository.getTokenForSeries(presentedSeries); if (token == null) { throw new RememberMeAuthenticationException('No persistent token found for series id: ' + presentedSeries); } else if (!presentedToken.equals(token.getTokenValue())) { this.tokenRepository.removeUserTokens(token.getUsername()); throw new CookieTheftException(this.messages.getMessage('PersistentTokenBasedRememberMeServices.cookieStolen', 'Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack.')); } else if (token.getDate().getTime() + (long)this.getTokenValiditySeconds() * 1000L < System.currentTimeMillis()) { throw new RememberMeAuthenticationException('Remember-me login has expired'); } else { if (this.logger.isDebugEnabled()) { this.logger.debug('Refreshing persistent login token for user ’' + token.getUsername() + '’, series ’' + token.getSeries() + '’'); } PersistentRememberMeToken newToken = new PersistentRememberMeToken(token.getUsername(), token.getSeries(), this.generateTokenData(), new Date()); try { this.tokenRepository.updateToken(newToken.getSeries(), newToken.getTokenValue(), newToken.getDate()); this.addCookie(newToken, request, response); } catch (Exception var9) { this.logger.error('Failed to update token: ', var9); throw new RememberMeAuthenticationException('Autologin failed due to data access problem'); } return this.getUserDetailsService().loadUserByUsername(token.getUsername()); } } }

回到RememberMeAuthenticationFilter,在調用了autoLogin方法之后得到了rememberMeAuth,然后再對其進行一個認證,認證成功之后保存到SecurityContext中,至此整個RememberMe自動登錄流程源碼結束。

相關閱讀:

Spring Security實現圖形驗證碼登錄

Spring Security實現短信驗證碼登錄

總結

到此這篇關于Spring Security 實現“記住我”功能及原理解析的文章就介紹到這了,更多相關spring security記住我內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Spring
相關文章:
主站蜘蛛池模板: 免费看一级欧美毛片视频 | 国产精品高清久久久久久久 | 韩国在线精品福利视频在线观看 | 日韩美女在线看免费观看 | 一区在线视频 | 久久久久久国产精品免费免费 | 中文国产成人精品久久水 | 中文字幕精品视频在线 | 亚洲图片一区二区三区 | 国产久草视频在线 | 免费一级毛片女人图片 | 国内精品久久久久久野外 | 国产欧美一区二区三区观看 | 99久久精品免费看国产一区二区三区 | 国产视频久久久久 | 成人在线观看国产 | 9lporm自拍视频在线 | 国产高清av在线播放 | 成 人 动漫在线观看网站网站 | 日本免费小视频 | 中文字幕日韩有码 | 91精品国产免费 | 欧美日韩成人在线视频 | 国产精品系列在线 | 亚洲国产精品乱码在线观看97 | 亚洲精品自产拍在线观看 | 免费国产一级特黄久久 | 欧美三级做爰全过程 | 高清国产美女一级a毛片 | 久久久久久久99精品免费 | 日本一级毛片高清免费观看视频 | 日本精品视频一区二区三区 | 亚洲最大看欧美片网站 | 免看一级一片一在线看 | 国产精自产拍久久久久久 | 亚洲情a成黄在线观看 | 精品亚洲欧美高清不卡高清 | 性欧美在线 | 91一区二区在线观看精品 | 久久国产精品久久精品国产 | 午夜影院0606 |