真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

SpringSecurity如何實(shí)現(xiàn)默認(rèn)表單登錄頁展示-創(chuàng)新互聯(lián)

這篇文章主要為大家展示了“SpringSecurity如何實(shí)現(xiàn)默認(rèn)表單登錄頁展示”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“SpringSecurity如何實(shí)現(xiàn)默認(rèn)表單登錄頁展示”這篇文章吧。

創(chuàng)新互聯(lián)專注于企業(yè)成都全網(wǎng)營銷推廣、網(wǎng)站重做改版、玉溪網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5場景定制、商城網(wǎng)站定制開發(fā)、集團(tuán)公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站制作、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為玉溪等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。

SpringSecurity 默認(rèn)表單登錄頁展示流程源碼

講解 SpringSecurity提供的默認(rèn)表單登錄頁 它是如何展示的的流程,
涉及

1.FilterSecurityInterceptor,
2.ExceptionTranslationFilc,xmccmc,ter ,
3.DefaultLoginPageGeneratingFilter 過濾器,
并且簡單介紹了 AccessDecisionManager 投票機(jī)制

?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依賴

SpringSecurity如何實(shí)現(xiàn)默認(rèn)表單登錄頁展示

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


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

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

@RestController
public 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

SpringSecurity如何實(shí)現(xiàn)默認(rèn)表單登錄頁展示

??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ì)重定向到 剛剛訪問的接口
SpringSecurity如何實(shí)現(xiàn)默認(rèn)表單登錄頁展示

?2.springSecurityFilterchain 過濾器鏈

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

SpringSecurity如何實(shí)現(xiàn)默認(rèn)表單登錄頁展示

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

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

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

http://localhost:8080/hello

?請求會(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用來對Authentication是否有權(quán)訪問受保護(hù)對象進(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è)人投贊成票,但是有人投反對票,則將拋出AccessDeniedException。

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

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

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

?4.ExceptionTranslationFilter (捕獲AccessDeniedException異常)

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

?源碼如下:

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)前訪問的路徑,這就是為什么我們訪問 /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方法

SpringSecurity如何實(shí)現(xiàn)默認(rèn)表單登錄頁展示

?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

SpringSecurity如何實(shí)現(xiàn)默認(rèn)表單登錄頁展示

?然后sendRedirect 既會(huì)重定向到 /login 請求
SpringSecurity如何實(shí)現(xiàn)默認(rèn)表單登錄頁展示

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

?DefaultLoginPageGeneratingFilter是過濾器鏈中的一個(gè)用于捕獲/login請求,并且渲染出一個(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 判斷請求是否是 loginPageUrl

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

?因?yàn)槲覀儧]有配置所以 默認(rèn)的 loginPageUrl = /login
SpringSecurity如何實(shí)現(xiàn)默認(rèn)表單登錄頁展示

?驗(yàn)證通過請求路徑 能匹配 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"
    + "  Please sign in\n"
    + createError(loginError, errorMsg)
    + createLogoutSuccess(logoutSuccess)
    + "  

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

\n"     + "  

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

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

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

\n"     + createRememberMe(this.openIDrememberMeParameter)     + renderHiddenInputs(request)     + "  Sign in\n"     + "  \n");  }  if (oauth3LoginEnabled) {   sb.append("Login with OAuth 2.0");   sb.append(createError(loginError, errorMsg));   sb.append(createLogoutSuccess(logoutSuccess));   sb.append("\n");   for (Map.Entry clientAuthenticationUrlToClientName : oauth3AuthenticationUrlToClientName.entrySet()) {    sb.append(" ");    String url = clientAuthenticationUrlToClientName.getKey();    sb.append("");    String clientName = HtmlUtils.htmlEscape(clientAuthenticationUrlToClientName.getValue());    sb.append(clientName);    sb.append("");    sb.append("\n");   }   sb.append("\n");  }  if (this.saml2LoginEnabled) {   sb.append("Login with SAML 2.0");   sb.append(createError(loginError, errorMsg));   sb.append(createLogoutSuccess(logoutSuccess));   sb.append("\n");   for (Map.Entry relyingPartyUrlToName : saml2AuthenticationUrlToProviderName.entrySet()) {    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("
\n");  sb.append("");  return sb.toString(); }

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

SpringSecurity如何實(shí)現(xiàn)默認(rèn)表單登錄頁展示

以上是“SpringSecurity如何實(shí)現(xiàn)默認(rèn)表單登錄頁展示”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司行業(yè)資訊頻道!

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)建站www.cdcxhl.com,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。


分享題目:SpringSecurity如何實(shí)現(xiàn)默認(rèn)表單登錄頁展示-創(chuàng)新互聯(lián)
路徑分享:http://weahome.cn/article/ccspsj.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部