1. <ul id="0c1fb"></ul>

      <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
      <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区

      RELATEED CONSULTING
      相關(guān)咨詢
      選擇下列產(chǎn)品馬上在線溝通
      服務(wù)時(shí)間:8:30-17:00
      你可能遇到了下面的問題
      關(guān)閉右側(cè)工具欄

      新聞中心

      這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
      SpringSecurity默認(rèn)表單登錄頁展示流程源碼是怎樣的

      這篇文章給大家介紹SpringSecurity默認(rèn)表單登錄頁展示流程源碼是怎樣的,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。

      創(chuàng)新互聯(lián)是一家專業(yè)提供志丹企業(yè)網(wǎng)站建設(shè),專注與網(wǎng)站設(shè)計(jì)制作、成都網(wǎng)站設(shè)計(jì)H5網(wǎng)站設(shè)計(jì)、小程序制作等業(yè)務(wù)。10年已為志丹眾多企業(yè)、政府機(jī)構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)網(wǎng)站制作公司優(yōu)惠進(jìn)行中。

      ?1.準(zhǔn)備工作(體驗(yàn)SpringSecurity默認(rèn)表單認(rèn)證)

      ??1.1 創(chuàng)建SpringSecurity項(xiàng)目

      ??先通過IDEA 創(chuàng)建一個(gè)SpringBoot項(xiàng)目 并且依賴SpringSecurity,Web依賴

      ??此時(shí)pom.xml會(huì)自動(dòng)添加

      org.springframework.boot spring-boot-starter-security

      ??1.2 提供一個(gè)接口

      @RestControllerpublic class HelloController {@RequestMapping("/hello")public String hello() { return "Hello SpringSecurity"; }}

      ??1.3 啟動(dòng)項(xiàng)目

      ??直接訪問 提供的接口

      http://localhost:8080/hello

      ??會(huì)發(fā)現(xiàn)瀏覽器被直接重定向到了 /login 并且顯示如下默認(rèn)的表單登錄頁

      http://localhost:8080/login

      ??1.4 登錄

      ??在啟動(dòng)項(xiàng)目的時(shí)候 控制臺(tái)會(huì)打印一個(gè) seuciryt password : xxx

      Using generated security password: f520875f-ea2b-4b5d-9b0c-f30c0c17b90b

      ??直接登錄

      用戶名:user 密碼 :f520875f-ea2b-4b5d-9b0c-f30c0c17b90b

      ??登錄成功并且 瀏覽器又會(huì)重定向到 剛剛訪問的接口

      ?2.springSecurityFilterchain 過濾器鏈

      ?如果你看過我另一篇關(guān)于SpringSecurity初始化源碼的博客,那么你一定知道當(dāng)SpringSecurity項(xiàng)目啟動(dòng)完成后會(huì)初始化一個(gè) springSecurityFilterchain 它內(nèi)部 additionalFilters屬性初始化了很多Filter 如下所有的請(qǐng)求都會(huì)經(jīng)過這一系列的過濾器 Spring Security就是通過這些過濾器 來進(jìn)行認(rèn)證授權(quán)等

      ?3.FilterSecurityInterceptor (它會(huì)判斷這次請(qǐng)求能否通過)

      ?FilterSecurityInterceptor是過濾器鏈中最后一個(gè)過濾器,主要用于判斷請(qǐng)求能否通過,內(nèi)部通過AccessDecisionManager 進(jìn)行投票判斷

      ?當(dāng)我們未登錄訪問

      http://localhost:8080/hello

      ?請(qǐng)求會(huì)被 FilterSecurityInterceptor 攔截

      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { FilterInvocation fi = new FilterInvocation(request, response, chain); invoke(fi);}

      ?重點(diǎn)看invoke方法

      public void invoke(FilterInvocation fi) throws IOException, ServletException { if ((fi.getRequest() != null)  && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)  && observeOncePerRequest) { // filter already applied to this request and user wants us to observe // once-per-request handling, so don't re-do security checking fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); } else { // first time this request being called, so perform security checking if (fi.getRequest() != null && observeOncePerRequest) {  fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE); } InterceptorStatusToken token = super.beforeInvocation(fi); try {  fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); } finally {  super.finallyInvocation(token); } super.afterInvocation(token, null); }}

      ?源碼中有這樣一句,其實(shí)就是判斷當(dāng)前用戶是否能夠訪問指定的接口,可以則執(zhí)行 fi.getChain().doFilter 調(diào)用訪問的接口否則 內(nèi)部會(huì)拋出異常

      InterceptorStatusToken token = super.beforeInvocation(fi);

      ?beforeInvocation 方法內(nèi)部是通過 accessDecisionManager 去做決定的?Spring Security已經(jīng)內(nèi)置了幾個(gè)基于投票的AccessDecisionManager包括(AffirmativeBased ,ConsensusBased ,UnanimousBased)當(dāng)然如果需要你也可以實(shí)現(xiàn)自己的AccessDecisionManager

      ?使用這種方式,一系列的AccessDecisionVoter將會(huì)被AccessDecisionManager用來對(duì)Authentication是否有權(quán)訪問受保護(hù)對(duì)象進(jìn)行投票,然后再根據(jù)投票結(jié)果來決定是否要拋出AccessDeniedException

      this.accessDecisionManager.decide(authenticated, object, attributes);

      ?AffirmativeBased的 decide的實(shí)現(xiàn)如下

      public void decide(Authentication authentication, Object object, Collection configAttributes) throws AccessDeniedException { int deny = 0; Iterator var5 = this.getDecisionVoters().iterator(); while(var5.hasNext()) { AccessDecisionVoter voter = (AccessDecisionVoter)var5.next(); int result = voter.vote(authentication, object, configAttributes); if (this.logger.isDebugEnabled()) {  this.logger.debug("Voter: " + voter + ", returned: " + result); } switch(result) { case -1:  ++deny;  break; case 1:  return; } } if (deny > 0) { throw new AccessDeniedException(this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied")); } else { this.checkAllowIfAllAbstainDecisions(); }}

      ?AffirmativeBased的邏輯是這樣的:

      (1)只要有AccessDecisionVoter的投票為ACCESS_GRANTED則同意用戶進(jìn)行訪問;(2)如果全部棄權(quán)也表示通過;(3)如果沒有一個(gè)人投贊成票,但是有人投反對(duì)票,則將拋出AccessDeniedException。

      ?當(dāng)我們第一次訪問的時(shí)候

      http://localhost:8080/hello的時(shí)候

      ?返回 result = -1 會(huì)拋出 AccessDeniedException 拒絕訪問異常

      ?4.ExceptionTranslationFilter (捕獲AccessDeniedException異常)

      ?該過濾器它會(huì)接收到FilterSecurityInterceptor拋出的 AccessDeniedException異常)并且進(jìn)行捕獲,然后發(fā)送重定向到/login請(qǐng)求

      ?源碼如下:

      public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; try { chain.doFilter(request, response); logger.debug("Chain processed normally"); } catch (IOException ex) { throw ex; } catch (Exception ex) { // Try to extract a SpringSecurityException from the stacktrace Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex); RuntimeException ase = (AuthenticationException) throwableAnalyzer  .getFirstThrowableOfType(AuthenticationException.class, causeChain); if (ase == null) {  ase = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType(   AccessDeniedException.class, causeChain); } if (ase != null) {  if (response.isCommitted()) {  throw new ServletException("Unable to handle the Spring Security Exception because the response is already committed.", ex);  }  handleSpringSecurityException(request, response, chain, ase); } else {  // Rethrow ServletExceptions and RuntimeExceptions as-is  if (ex instanceof ServletException) {  throw (ServletException) ex;  }  else if (ex instanceof RuntimeException) {  throw (RuntimeException) ex;  }  // Wrap other Exceptions. This shouldn't actually happen  // as we've already covered all the possibilities for doFilter  throw new RuntimeException(ex); } }}

      ?當(dāng)獲取異常后 調(diào)用

      handleSpringSecurityException(request, response, chain, ase);

      ?handleSpringSecurityException 源碼如下:

      private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response, FilterChain chain, RuntimeException exception) throws IOException, ServletException { if (exception instanceof AuthenticationException) { logger.debug(  "Authentication exception occurred; redirecting to authentication entry point",  exception); sendStartAuthentication(request, response, chain,  (AuthenticationException) exception); } else if (exception instanceof AccessDeniedException) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authenticationTrustResolver.isAnonymous(authentication) || authenticationTrustResolver.isRememberMe(authentication)) {  logger.debug(   "Access is denied (user is " + (authenticationTrustResolver.isAnonymous(authentication) ? "anonymous" : "not fully authenticated") + "); redirecting to authentication entry point",   exception);  sendStartAuthentication(   request,   response,   chain,   new InsufficientAuthenticationException(   messages.getMessage(    "ExceptionTranslationFilter.insufficientAuthentication",    "Full authentication is required to access this resource"))); } else {  logger.debug(   "Access is denied (user is not anonymous); delegating to AccessDeniedHandler",   exception);  accessDeniedHandler.handle(request, response,   (AccessDeniedException) exception); } }}

      ?先判斷獲取的異常是否是AccessDeniedException 再判斷是否是匿名用戶,如果是則調(diào)用 sendStartAuthentication 重定向到登錄頁面

      ?重定向登錄頁面之前會(huì)保存當(dāng)前訪問的路徑,這就是為什么我們?cè)L問 /hello接口后 再登錄成功后又會(huì)跳轉(zhuǎn)到 /hello接口,因?yàn)樵谥囟ㄏ虻?login接口前 這里進(jìn)行了保存 requestCache.saveRequest(request, response);

      protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, AuthenticationException reason) throws ServletException, IOException { // SEC-112: Clear the SecurityContextHolder's Authentication, as the // existing Authentication is no longer considered valid SecurityContextHolder.getContext().setAuthentication(null); requestCache.saveRequest(request, response); logger.debug("Calling Authentication entry point."); authenticationEntryPoint.commence(request, response, reason);}

      ?authenticationEntryPoint.commence(request, response, reason);方法內(nèi)部

      ?調(diào)用LoginUrlAuthenticationEntryPoint 的 commence方法

      ?LoginUrlAuthenticationEntryPoint 的commence方法內(nèi)部有 構(gòu)造重定向URL的方法

      redirectUrl = buildRedirectUrlToLoginPage(request, response, authException);protected String buildRedirectUrlToLoginPage(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) { String loginForm = determineUrlToUseForThisRequest(request, response,  authException);protected String determineUrlToUseForThisRequest(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) { return getLoginFormUrl();}

      ?最終會(huì)獲取到需要重定向的URL /login

      ?然后sendRedirect 既會(huì)重定向到 /login 請(qǐng)求

      ?5.DefaultLoginPageGeneratingFilter (會(huì)捕獲重定向的/login 請(qǐng)求)

      ?DefaultLoginPageGeneratingFilter是過濾器鏈中的一個(gè)用于捕獲/login請(qǐng)求,并且渲染出一個(gè)默認(rèn)表單頁面

      public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; boolean loginError = isErrorPage(request); boolean logoutSuccess = isLogoutSuccess(request); if (isLoginUrlRequest(request) || loginError || logoutSuccess) { String loginPageHtml = generateLoginPageHtml(request, loginError,  logoutSuccess); response.setContentType("text/html;charset=UTF-8"); response.setContentLength(loginPageHtml.getBytes(StandardCharsets.UTF_8).length); response.getWriter().write(loginPageHtml); return; } chain.doFilter(request, response);}

      ?isLoginUrlRequest 判斷請(qǐng)求是否是 loginPageUrl

      private boolean isLoginUrlRequest(HttpServletRequest request) { return matches(request, loginPageUrl);}

      ?因?yàn)槲覀儧]有配置所以 默認(rèn)的 loginPageUrl = /login

      ?驗(yàn)證通過請(qǐng)求路徑 能匹配 loginPageUrl

      String loginPageHtml = generateLoginPageHtml(request, loginError,  logoutSuccess);

      ?generateLoginPageHtml 繪制默認(rèn)的HTML 頁面,到此我們默認(rèn)的登錄頁面怎么來的就解釋清楚了

      private String generateLoginPageHtml(HttpServletRequest request, boolean loginError,  boolean logoutSuccess) { String errorMsg = "Invalid credentials"; if (loginError) {  HttpSession session = request.getSession(false);  if (session != null) {   AuthenticationException ex = (AuthenticationException) session     .getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);   errorMsg = ex != null ? ex.getMessage() : "Invalid credentials";  } } StringBuilder sb = new StringBuilder(); sb.append("\n"   + "\n"   + " \n"   + " \n"   + " \n"   + " \n"   + " \n"   + " Please sign in\n"   + " \n"   + " \n"   + " \n"   + " \n"   + "  

      \n"); String contextPath = request.getContextPath(); if (this.formLoginEnabled) {  sb.append("  

      \n"    + "  \n"    + createError(loginError, errorMsg)    + createLogoutSuccess(logoutSuccess)    + "  

      \n"    + "   \n"    + "   \n"    + "  

      \n"    + "  

      \n"    + "   \n"    + "   \n"    + "  

      \n"    + createRememberMe(this.rememberMeParameter)    + renderHiddenInputs(request)    + "  \n"    + "  
      \n"); } if (openIdEnabled) {  sb.append("  
      \n"    + "  \n"    + createError(loginError, errorMsg)    + createLogoutSuccess(logoutSuccess)    + "  

      \n"    + "   \n"    + "   \n"    + "  

      \n"    + createRememberMe(this.openIDrememberMeParameter)    + renderHiddenInputs(request)    + "  \n"    + "  
      \n"); } if (oauth3LoginEnabled) {  sb.append("");  sb.append(createError(loginError, errorMsg));  sb.append(createLogoutSuccess(logoutSuccess));  sb.append("\n");  for (Map.Entry clientAuthenticationUrlToClientName : oauth3AuthenticationUrlToClientName.entrySet()) {   sb.append(" \n");  }  sb.append("
      ");   String url = clientAuthenticationUrlToClientName.getKey();   sb.append("");   String clientName = HtmlUtils.htmlEscape(clientAuthenticationUrlToClientName.getValue());   sb.append(clientName);   sb.append("");   sb.append("
      \n"); } if (this.saml2LoginEnabled) {  sb.append("");  sb.append(createError(loginError, errorMsg));  sb.append(createLogoutSuccess(logoutSuccess));  sb.append("\n");  for (Map.Entry relyingPartyUrlToName : saml2AuthenticationUrlToProviderName.entrySet()) {   sb.append(" \n");  }  sb.append("
      ");   String url = relyingPartyUrlToName.getKey();   sb.append("");   String partyName = HtmlUtils.htmlEscape(relyingPartyUrlToName.getValue());   sb.append(partyName);   sb.append("");   sb.append("
      \n"); } sb.append("

      \n"); sb.append(""); return sb.toString();}

      至此 SpringSecurity 默認(rèn)表單登錄頁展示流程源碼部分已經(jīng)全部講解完畢,會(huì)渲染出下面的頁面,但是一定要有網(wǎng)的情況,否則樣式可能會(huì)變化

      6.總結(jié)

      本篇主要講解 SpringSecurity提供的默認(rèn)表單登錄頁 它是如何展示的的流程,包括涉及這一流程中相關(guān)的 3個(gè)過濾器

      1.FilterSecurityInterceptor,2.ExceptionTranslationFilter ,3.DefaultLoginPageGeneratingFilter 過濾器,并且簡(jiǎn)單介紹了一下 AccessDecisionManager 它主要進(jìn)行投票來判斷該用戶是否能夠訪問相應(yīng)的 資源AccessDecisionManager 投票機(jī)制我也沒有深究 后續(xù)我會(huì)詳細(xì)深入一下再展開

      關(guān)于SpringSecurity默認(rèn)表單登錄頁展示流程源碼是怎樣的就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。


      網(wǎng)站標(biāo)題:SpringSecurity默認(rèn)表單登錄頁展示流程源碼是怎樣的
      標(biāo)題網(wǎng)址:http://www.ef60e0e.cn/article/jecejh.html
      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区
      1. <ul id="0c1fb"></ul>

        <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
        <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

        淄博市| 宁蒗| 岳阳市| 教育| 拉孜县| 浦东新区| 城口县| 安泽县| 宜春市| 京山县| 临夏县| 桂林市| 镇雄县| 惠州市| 易门县| 平塘县| 多伦县| 祁东县| 安阳市| 恩平市| 哈尔滨市| 丹凤县| 缙云县| 巨野县| 开原市| 定日县| 原平市| 平度市| 玉田县| 余干县| 红河县| 黔江区| 息烽县| 铜鼓县| 铜山县| 西藏| 台北市| 桐柏县| 无棣县| 合江县| 栖霞市|