您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“Spring Security的登陸流程是什么”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
在Spring Security中,認(rèn)證授權(quán)都是通過過濾器來實(shí)現(xiàn)的。
當(dāng)開始登陸的時(shí)候,有一個(gè)關(guān)鍵的過濾器UsernamePasswordAuthenticationFilter,該類繼承抽象類AbstractAuthenticationProcessingFilter,在AbstractAuthenticationProcessingFilter里有一個(gè)doFilter方法,一切先從這里說起。
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { if (!requiresAuthentication(request, response)) { chain.doFilter(request, response); return; } try { Authentication authenticationResult = attemptAuthentication(request, response); if (authenticationResult == null) { // return immediately as subclass has indicated that it hasn't completed return; } this.sessionStrategy.onAuthentication(authenticationResult, request, response); // Authentication success if (this.continueChainBeforeSuccessfulAuthentication) { chain.doFilter(request, response); } successfulAuthentication(request, response, chain, authenticationResult); } catch (InternalAuthenticationServiceException failed) { this.logger.error("An internal error occurred while trying to authenticate the user.", failed); unsuccessfulAuthentication(request, response, failed); } catch (AuthenticationException ex) { // Authentication failed unsuccessfulAuthentication(request, response, ex); } }
首先requiresAuthentication先判斷是否嘗試校驗(yàn),通過后調(diào)用attemptAuthentication方法,這個(gè)方法也就是UsernamePasswordAuthenticationFilter 中的attemptAuthentication方法。
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (this.postOnly && !request.getMethod().equals("POST")) { throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); } String username = obtainUsername(request); username = (username != null) ? username : ""; username = username.trim(); String password = obtainPassword(request); password = (password != null) ? password : ""; UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); // Allow subclasses to set the "details" property setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); }
1.在UsernamePasswordAuthenticationFilter 的attemptAuthentication方法中,先是驗(yàn)證請(qǐng)求的類型,是否是POST請(qǐng)求,如果不是的話,拋出異常。(PS:登陸肯定要用POST方法了)
2.然后拿到username和password。這里使用的是obtainUsername方法,也就是get方法。
@Nullable protected String obtainPassword(HttpServletRequest request) { return request.getParameter(this.passwordParameter); } @Nullable protected String obtainUsername(HttpServletRequest request) { return request.getParameter(this.usernameParameter); }
由此我們知道了Spring Security中是通過get方法來拿到參數(shù),所以在進(jìn)行前后端分離的時(shí)候是無法接受JSON數(shù)據(jù),處理方法就是自定義一個(gè)Filter來繼承UsernamePasswordAuthenticationFilter,重寫attemptAuthentication方法,然后創(chuàng)建一個(gè)Filter實(shí)例寫好登陸成功和失敗的邏輯處理,在HttpSecurity參數(shù)的configure中通過addFilterAt來替換Spring Security官方提供的過濾器。
3.創(chuàng)建一個(gè)UsernamePasswordAuthenticationToken 實(shí)例。
4.設(shè)置Details,在這里關(guān)鍵的是在WebAuthenticationDetails類中記錄了用戶的remoteAddress和sessionId。
public WebAuthenticationDetails(HttpServletRequest request) { this.remoteAddress = request.getRemoteAddr(); HttpSession session = request.getSession(false); this.sessionId = (session != null) ? session.getId() : null; }
5.拿到一個(gè)AuthenticationManager通過authenticate方法進(jìn)行校驗(yàn),這里以實(shí)現(xiàn)類ProviderManager為例。
@Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { //獲取Authentication的運(yùn)行時(shí)類 Class<? extends Authentication> toTest = authentication.getClass(); AuthenticationException lastException = null; AuthenticationException parentException = null; Authentication result = null; Authentication parentResult = null; int currentPosition = 0; int size = this.providers.size(); for (AuthenticationProvider provider : getProviders()) { //判斷是否支持處理該類別的provider if (!provider.supports(toTest)) { continue; } if (logger.isTraceEnabled()) { logger.trace(LogMessage.format("Authenticating request with %s (%d/%d)", provider.getClass().getSimpleName(), ++currentPosition, size)); } try { //獲取用戶的信息 result = provider.authenticate(authentication); if (result != null) { copyDetails(authentication, result); break; } } catch (AccountStatusException | InternalAuthenticationServiceException ex) { prepareException(ex, authentication); // SEC-546: Avoid polling additional providers if auth failure is due to // invalid account status throw ex; } catch (AuthenticationException ex) { lastException = ex; } } //不支持的話跳出循環(huán)再次執(zhí)行 if (result == null && this.parent != null) { // Allow the parent to try. try { parentResult = this.parent.authenticate(authentication); result = parentResult; } catch (ProviderNotFoundException ex) { // ignore as we will throw below if no other exception occurred prior to // calling parent and the parent // may throw ProviderNotFound even though a provider in the child already // handled the request } catch (AuthenticationException ex) { parentException = ex; lastException = ex; } } if (result != null) { //擦除用戶的憑證 也就是密碼 if (this.eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) { // Authentication is complete. Remove credentials and other secret data // from authentication ((CredentialsContainer) result).eraseCredentials(); } // If the parent AuthenticationManager was attempted and successful then it // will publish an AuthenticationSuccessEvent // This check prevents a duplicate AuthenticationSuccessEvent if the parent // AuthenticationManager already published it if (parentResult == null) { //公示登陸成功 this.eventPublisher.publishAuthenticationSuccess(result); } return result; } // Parent was null, or didn't authenticate (or throw an exception). if (lastException == null) { lastException = new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound", new Object[] { toTest.getName() }, "No AuthenticationProvider found for {0}")); } // If the parent AuthenticationManager was attempted and failed then it will // publish an AbstractAuthenticationFailureEvent // This check prevents a duplicate AbstractAuthenticationFailureEvent if the // parent AuthenticationManager already published it if (parentException == null) { prepareException(lastException, authentication); } throw lastException; }
6.經(jīng)過一系列校驗(yàn),此時(shí)登陸校驗(yàn)基本完成,當(dāng)驗(yàn)證通過后會(huì)執(zhí)行doFilter中的successfulAuthentication方法,跳轉(zhuǎn)到我們?cè)O(shè)置的登陸成功界面,驗(yàn)證失敗會(huì)執(zhí)行unsuccessfulAuthentication方法,跳轉(zhuǎn)到我們?cè)O(shè)置的登陸失敗界面。
“Spring Security的登陸流程是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。