日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

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

SpringBoot 配合 SpringSecurity 實現自動登錄功能的代碼

瀏覽:25日期:2023-04-20 16:52:24

自動登錄是我們在軟件開發時一個非常常見的功能,例如我們登錄 QQ 郵箱:

SpringBoot 配合 SpringSecurity 實現自動登錄功能的代碼

很多網站我們在登錄的時候都會看到類似的選項,畢竟總讓用戶輸入用戶名密碼是一件很麻煩的事。

自動登錄功能就是,用戶在登錄成功后,在某一段時間內,如果用戶關閉了瀏覽器并重新打開,或者服務器重啟了,都不需要用戶重新登錄了,用戶依然可以直接訪問接口數據

作為一個常見的功能,我們的 Spring Security 肯定也提供了相應的支持,本文我們就來看下 Spring Security 中如何實現這個功能。

一、加入 remember-me

為了配置方便,加入兩個依賴即可:

SpringBoot 配合 SpringSecurity 實現自動登錄功能的代碼

配置類中添加如下代碼:

@Configurationpublic class SecurityConfig extends WebSecurityConfigurerAdapter { @Bean PasswordEncoder passwordEncoder(){ return NoOpPasswordEncoder.getInstance(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser('yolo') .password('123').roles('admin'); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .and() .rememberMe() .and() .csrf().disable(); }}

大家看到,這里只需要添加一個 .rememberMe() 即可,自動登錄功能就成功添加進來了。

接下來我們隨意添加一個測試接口:

@RestControllerpublic class HelloController { @GetMapping('/hello') public String hello(){ return 'Hello Yolo !!!'; }}

SpringBoot 配合 SpringSecurity 實現自動登錄功能的代碼

這個時候大家發現,默認的登錄頁面多了一個選項,就是記住我。我們輸入用戶名密碼,并且勾選上記住我這個框,然后點擊登錄按鈕執行登錄操作。

可以看到,登錄數據中,除了 username 和 password 之外,還有一個 remember-me,之所以給大家看這個,是想告訴大家,如果你你需要自定義登錄頁面,RememberMe 這個選項的 key 該怎么寫。

登錄成功之后,就會自動跳轉到 hello 接口了。我們注意,系統訪問 hello 接口的時候,攜帶的 cookie:

SpringBoot 配合 SpringSecurity 實現自動登錄功能的代碼

大家注意到,這里多了一個 remember-me,這就是這里實現的核心,關于這個 remember-me 我一會解釋,我們先來測試效果。

接下來,我們關閉瀏覽器,再重新打開瀏覽器。正常情況下,瀏覽器關閉再重新打開,如果需要再次訪問 hello 接口,就需要我們重新登錄了。但是此時,我們再去訪問 hello 接口,發現不用重新登錄了,直接就能訪問到,這就說明我們的 RememberMe 配置生效了(即下次自動登錄功能生效了)。

二、原理分析

按理說,瀏覽器關閉再重新打開,就要重新登錄,現在竟然不用等了,那么這個功能到底是怎么實現的呢?

首先我們來分析一下 cookie 中多出來的這個 remember-me,這個值一看就是一個 Base64 轉碼后的字符串,我們可以使用網上的一些在線工具來解碼,可以自己簡單寫兩行代碼來解碼:

@Test void contextLoads() { String s = new String( Base64.getDecoder().decode('eW9sbzoxNjAxNDczNTY2NTA1OjlmMGY5YjBjOTAzYmNjYmU3ZjMwYWM0NjVlZjEzNmQ5')); System.out.println('s = ' + s); }

執行這段代碼,輸出結果如下:

s = yolo:1601473566505:9f0f9b0c903bccbe7f30ac465ef136d9

可以看到,這段 Base64 字符串實際上用 : 隔開,分成了三部分:

(1)第一段是用戶名,這個無需質疑。(2)第二段看起來是一個時間戳,我們通過在線工具或者 Java 代碼解析后發現,這是一個兩周后的數據。(3)第三段我就不賣關子了,這是使用 MD5 散列函數算出來的值,他的明文格式是username + ':' + tokenExpiryTime + ':' + password + ':' + key,最后的 key 是一個散列鹽值,可以用來防治令牌被修改。

了解到 cookie 中 remember-me 的含義之后,那么我們對于記住我的登錄流程也就很容易猜到了了。

在瀏覽器關閉后,并重新打開之后,用戶再去訪問 hello 接口,此時會攜帶著 cookie 中的 remember-me 到服務端,服務到拿到值之后,可以方便的計算出用戶名和過期時間,再根據用戶名查詢到用戶密碼,然后通過 MD5 散列函數計算出散列值,再將計算出的散列值和瀏覽器傳遞來的散列值進行對比,就能確認這個令牌是否有效。

流程就是這么個流程,接下來我們通過分析源碼來驗證一下這個流程對不對。

三、源碼分析

接下來,我們通過源碼來驗證一下我們上面說的對不對。

這里主要從兩個方面來介紹,一個是 remember-me 這個令牌生成的過程,另一個則是它解析的過程。

1. 生成

生成的核心處理方法在:TokenBasedRememberMeServices#onLoginSuccess:

@Overridepublic void onLoginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) { String username = retrieveUserName(successfulAuthentication); String password = retrievePassword(successfulAuthentication); if (!StringUtils.hasLength(password)) { UserDetails user = getUserDetailsService().loadUserByUsername(username); password = user.getPassword(); } int tokenLifetime = calculateLoginLifetime(request, successfulAuthentication); long expiryTime = System.currentTimeMillis(); expiryTime += 1000L * (tokenLifetime < 0 ? TWO_WEEKS_S : tokenLifetime); String signatureValue = makeTokenSignature(expiryTime, username, password); setCookie(new String[] { username, Long.toString(expiryTime), signatureValue }, tokenLifetime, request, response);}protected String makeTokenSignature(long tokenExpiryTime, String username, String password) { String data = username + ':' + tokenExpiryTime + ':' + password + ':' + getKey(); MessageDigest digest; digest = MessageDigest.getInstance('MD5'); return new String(Hex.encode(digest.digest(data.getBytes())));}

(1)首先從登錄成功的 Authentication 中提取出用戶名/密碼。(2)由于登錄成功之后,密碼可能被擦除了,所以,如果一開始沒有拿到密碼,就再從 UserDetailsService 中重新加載用戶并重新獲取密碼。(3)再接下來去獲取令牌的有效期,令牌有效期默認就是兩周。(4)再接下來調用 makeTokenSignature 方法去計算散列值,實際上就是根據 username、令牌有效期以及 password、key 一起計算一個散列值。如果我們沒有自己去設置這個 key,默認是在 RememberMeConfigurer#getKey 方法中進行設置的,它的值是一個 UUID 字符串。(5)最后,將用戶名、令牌有效期以及計算得到的散列值放入 Cookie 中。

關于第四點,我這里再說一下。

由于我們自己沒有設置 key,key 默認值是一個 UUID 字符串,這樣會帶來一個問題,就是如果服務端重啟,這個 key 會變,這樣就導致之前派發出去的所有 remember-me 自動登錄令牌失效,所以,我們可以指定這個 key。指定方式如下:

@Overrideprotected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .and() .rememberMe() .key('yolo') .and() .csrf().disable();}

如果自己配置了 key,即使服務端重啟,即使瀏覽器打開再關閉,也依然能夠訪問到 hello 接口

這是 remember-me 令牌生成的過程。至于是如何走到 onLoginSuccess 方法的,這里可以給大家稍微提醒一下思路:

AbstractAuthenticationProcessingFilter#doFilter -> AbstractAuthenticationProcessingFilter#successfulAuthentication -> AbstractRememberMeServices#loginSuccess -> TokenBasedRememberMeServices#onLoginSuccess。

2. 解析

那么當用戶關掉并打開瀏覽器之后,重新訪問 /hello 接口,此時的認證流程又是怎么樣的呢?

我們之前說過,Spring Security 中的一系列功能都是通過一個過濾器鏈實現的,RememberMe 這個功能當然也不例外。

Spring Security 中提供了 RememberMeAuthenticationFilter 類專門用來做相關的事情,我們來看下 RememberMeAuthenticationFilter 的 doFilter 方法:

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 = rememberMeServices.autoLogin(request, response); if (rememberMeAuth != null) { rememberMeAuth = authenticationManager.authenticate(rememberMeAuth); SecurityContextHolder.getContext().setAuthentication(rememberMeAuth); onSuccessfulAuthentication(request, response, rememberMeAuth); if (this.eventPublisher != null) { eventPublisher .publishEvent(new InteractiveAuthenticationSuccessEvent( SecurityContextHolder.getContext() .getAuthentication(), this.getClass())); } if (successHandler != null) { successHandler.onAuthenticationSuccess(request, response, rememberMeAuth); return; } } chain.doFilter(request, response); } else { chain.doFilter(request, response); }}

這個方法最關鍵的地方在于,如果從 SecurityContextHolder 中無法獲取到當前登錄用戶實例,那么就調用 rememberMeServices.autoLogin 邏輯進行登錄,我們來看下這個方法:

public final Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) { String rememberMeCookie = extractRememberMeCookie(request); if (rememberMeCookie == null) { return null; } logger.debug('Remember-me cookie detected'); if (rememberMeCookie.length() == 0) { logger.debug('Cookie was empty'); cancelCookie(request, response); return null; } UserDetails user = null; try { String[] cookieTokens = decodeCookie(rememberMeCookie); user = processAutoLoginCookie(cookieTokens, request, response); userDetailsChecker.check(user); logger.debug('Remember-me cookie accepted'); return createSuccessfulAuthentication(request, user); } catch (CookieTheftException cte) { throw cte; } cancelCookie(request, response); return null;}

可以看到,這里就是提取出 cookie 信息,并對 cookie 信息進行解碼,解碼之后,再調用 processAutoLoginCookie 方法去做校驗,processAutoLoginCookie 方法的代碼我就不貼了,核心流程就是首先獲取用戶名和過期時間,再根據用戶名查詢到用戶密碼,然后通過 MD5 散列函數計算出散列值,再將拿到的散列值和瀏覽器傳遞來的散列值進行對比,就能確認這個令牌是否有效,進而確認登錄是否有效。

三、總結

看了上面的文章,大家可能已經發現,如果我們開啟了 RememberMe 功能,最最核心的東西就是放在 cookie 中的令牌了,這個令牌突破了 session 的限制,即使服務器重啟、即使瀏覽器關閉又重新打開,只要這個令牌沒有過期,就能訪問到數據。

一旦令牌丟失,別人就可以拿著這個令牌隨意登錄我們的系統了,這是一個非常危險的操作。

但是實際上這是一段悖論,為了提高用戶體驗(少登錄),我們的系統不可避免的引出了一些安全問題,不過我們可以通過技術將安全風險降低到最小

到此這篇關于SpringBoot 配合 SpringSecurity 實現自動登錄功能的代碼的文章就介紹到這了,更多相關SpringSecurity自動登錄內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Spring
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
石原莉奈在线亚洲三区| 国产农村妇女精品一二区| 中文亚洲免费| 在线亚洲自拍| 另类激情亚洲| 久久精品xxxxx| 日本久久精品| 国产中文一区| 日韩专区视频网站| 麻豆精品视频在线观看| 狠狠久久伊人| 黄色成人91| 国产乱码精品一区二区三区亚洲人| 麻豆精品蜜桃视频网站| 久久久久国产精品一区二区| 亚洲男女自偷自拍| 日韩精品a在线观看91| 欧美极品一区二区三区| 99精品在线免费在线观看| 最新国产精品| 亚洲va中文在线播放免费| 亚洲黄色影院| 精品高清久久| 免费成人av在线播放| 久久精品理论片| 亚洲最大av| 香蕉视频亚洲一级| 日韩av网站在线免费观看| 日韩一区欧美| 国产精品啊v在线| 伊伊综合在线| 97精品国产99久久久久久免费| 国产高清不卡| 国产欧美一区二区三区米奇| 中文一区在线| 亚洲www啪成人一区二区| 国产欧美69| 亚洲免费毛片| 日韩午夜电影| 综合日韩av| 人人精品亚洲| 国产精品久久久亚洲一区| 蜜臀久久久久久久| 久久精品不卡| 亚洲国产福利| 国产aⅴ精品一区二区四区| 中文无码日韩欧| 免费毛片在线不卡| 日本久久精品| 国产高清不卡| 国产在线看片免费视频在线观看| 91九色综合| 日韩久久99| 热久久国产精品| 视频一区视频二区中文| 欧美 日韩 国产一区二区在线视频 | 欧美日韩国产精品一区二区亚洲| 国产精品九九| 国产精品对白| 成人免费一区| 91av亚洲| 午夜av一区| 亚洲自拍另类| 亚洲深夜影院| 乱人伦精品视频在线观看| 欧美中文字幕| 婷婷精品在线| 免费在线日韩av| 岛国精品一区| 日韩av二区| 欧美亚洲国产激情| 99久久久国产精品美女| 伊人久久大香伊蕉在人线观看热v| 在线国产日韩| 久久亚洲国产精品尤物| 久久国产电影| 亚洲tv在线| 日韩1区在线| 亚洲一区欧美| av中文资源在线资源免费观看| 婷婷成人基地| 蜜桃91丨九色丨蝌蚪91桃色| 国产精品成人一区二区网站软件| 伊人久久国产| 日韩av在线免费观看不卡| 色天使综合视频| 亚洲电影在线一区二区三区| 热久久久久久| 在线日韩一区| 久久精品亚洲| 天海翼精品一区二区三区| 久久精品卡一| 欧美精品二区| 丝袜国产日韩另类美女| 国产一区二区三区探花| 免费视频一区二区| 日韩在线高清| 欧美成人aaa| 偷拍亚洲精品| 久久国产精品亚洲77777| 国产拍在线视频| 亚洲专区欧美专区| 国产一区二区三区91| 一级成人国产| 午夜电影亚洲| 日韩一区二区三区免费播放| 国产日韩三级| 久久午夜精品| 亚洲特色特黄| 日韩天堂在线| 国产精品一国产精品k频道56| 在线一区欧美| 午夜久久tv| 国产精品成人国产| 国产伦乱精品| 欧美一区自拍| 久久国产福利| 亚洲综合精品| 蜜臀av国产精品久久久久| 免费精品国产的网站免费观看| 日本精品不卡| 欧美va天堂| 午夜欧美在线| 欧美日韩视频| 国产美女一区| 免费视频一区二区| 亚洲精选91| 91精品国产自产精品男人的天堂| 国产免费播放一区二区| 日韩av不卡一区二区| 91成人在线精品视频| 亚洲青青久久| 日本在线视频一区二区| 欧美精品国产白浆久久久久| 国产伦精品一区二区三区千人斩| 日韩超碰人人爽人人做人人添| 日本v片在线高清不卡在线观看| 天堂久久一区| 狠狠久久伊人| 欧美日韩免费观看一区=区三区| 亚洲一区二区动漫| 蜜桃视频一区二区| 国产三级一区| 日韩免费小视频| 国产精品美女久久久浪潮软件| 日韩一区二区三区免费视频 | 日韩av有码| 国产亚洲在线观看| 国产精品久久久免费| 99成人在线视频| 日韩国产精品久久久久久亚洲| 精品视频一区二区三区在线观看| 亚洲天堂一区二区| 日本精品一区二区三区在线观看视频| 久久这里只有| 99国产精品| 精品成人18| 视频一区二区三区中文字幕| 麻豆精品在线观看| 在线亚洲自拍| 日韩久久电影| 国产精品一区二区中文字幕| 精品欧美激情在线观看| 国产福利资源一区| 亚洲人成精品久久久| 久久精品99久久无色码中文字幕| 国产精品jk白丝蜜臀av小说| 夜夜嗨av一区二区三区网站四季av| 国产黄色一区| 模特精品在线| 国产一区观看| 色偷偷偷在线视频播放| 蜜臀国产一区二区三区在线播放 | 国产日韩一区二区三区在线 | 日韩午夜在线| 国产一区亚洲| 91精品婷婷色在线观看| 欧美日一区二区在线观看| 亚洲欧美日韩国产一区| 四虎4545www国产精品| 国产精品久久| 国产精品亚洲欧美一级在线| 在线精品亚洲| 亚洲精品动态| 日韩在线黄色| 午夜精品一区二区三区国产| 久久国产日本精品| 久久一级电影| av亚洲免费| 亚洲精华国产欧美| 午夜一级久久| 色8久久久久| 国产精品一区二区精品| 国产图片一区| 国精品产品一区| 欧美亚洲日本精品| 久久高清免费| 日韩中文字幕av电影| 日韩国产一区二| 色婷婷综合网|