Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

The SpringSecurity default form login page shows what the process source code looks like

2025-04-25 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article introduces the SpringSecurity default form login page to show how the process source code is, the content is very detailed, interested friends can refer to, hope to be helpful to you.

1. Prepare for work (experience SpringSecurity default form authentication)

   1.1Creating SpringSecurity Project

   first creates a SpringBoot project through IDEA and relies on SpringSecurity,Web dependencies

   pom.xml will be added automatically at this time

Org.springframework.boot spring-boot-starter-security

   1.2 provides an interface

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

   1.3Startup Project

   directly accesses the provided interface

Http://localhost:8080/hello

   will find that the browser is redirected directly to / login and displays the following default form login page

Http://localhost:8080/login

   1.4Login

When    starts the project, the console will print a seuciryt password: xxx

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

   login directly

User name: user password: f520875f-ea2b-4b5d-9b0c-f30c0c17b90b

The    login is successful and the browser will be redirected to the interface you just accessed

  2.springSecurityFilterchain filter chain

  if you have read my other blog about SpringSecurity initialization source code, then you must know that when the SpringSecurity project is launched, a springSecurityFilterchain will be initialized, and its internal additionalFilters attribute initializes a lot of Filter. All requests will go through this series of filters, SpringSecurity, authentication, authorization, etc.

  3.FilterSecurityInterceptor (it will determine whether this request can be passed)

  FilterSecurityInterceptor is the last filter in the filter chain, which is mainly used to determine whether the request can be passed or not, and the internal voting judgment is made through AccessDecisionManager.

  when we are not logged in to visit

Http://localhost:8080/hello

  requests will be intercepted by FilterSecurityInterceptor

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

  focuses on invoke method

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);}}

There is a sentence in the   source code that determines whether the current user can access the specified interface, and can execute fi.getChain (). DoFilter to call the access interface, otherwise an exception will be thrown internally

InterceptorStatusToken token = super.beforeInvocation (fi)

Inside the   beforeInvocation method, decisions are made through accessDecisionManager.   Spring Security has built-in several vote-based AccessDecisionManager, including (AffirmativeBased, ConsensusBased, UnanimousBased), and of course you can implement your own AccessDecisionManager if you need to.

Using   in this way, a series of AccessDecisionVoter will be used by AccessDecisionManager to vote on whether the Authentication has access to protected objects, and then decide whether to throw the AccessDeniedException based on the result of the vote.

This.accessDecisionManager.decide (authenticated, object, attributes)

The implementation of decide for   AffirmativeBased is as follows

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 ();}}

The logic of   AffirmativeBased is as follows:

(1) users will be allowed to access as long as the vote of AccessDecisionVoter is ACCESS_GRANTED; (2) if all abstention means yes; (3) if no one votes in favor, but someone votes against, AccessDeniedException will be thrown.

 , when we first visited,

When http://localhost:8080/hello

If   returns result =-1, an AccessDeniedException access denied exception will be thrown.

  4.ExceptionTranslationFilter (catch AccessDeniedException exception)

  this filter it will receive the AccessDeniedException exception thrown by FilterSecurityInterceptor) and catch it, and then send a redirect to / login request

The   source code is as follows:

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 SpringSecurityException 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);}}

  is called when an exception is obtained

HandleSpringSecurityException (request, response, chain, ase)

The   handleSpringSecurityException source code is as follows:

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);}

  first determines whether the exception obtained is AccessDeniedException, and then determines whether it is an anonymous user. If so, call sendStartAuthentication to redirect to the login page.

  saves the current access path before redirecting the login page, which is why we visit the / hello interface and then log in successfully and then jump to the / hello interface, because the requestCache.saveRequest (request, response) is saved here before redirecting to the / login interface.

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); inside the method

  calls the commence method of LoginUrlAuthenticationEntryPoint

The commence method of   LoginUrlAuthenticationEntryPoint contains methods for constructing redirected 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 ();}

  will eventually get the URL / login that needs to be redirected

  and then sendRedirect will redirect to the / login request

  5.DefaultLoginPageGeneratingFilter (redirected / login requests are captured)

  DefaultLoginPageGeneratingFilter is one of the filter chains that captures / login requests and renders a default form page

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 (chain.doFilter, chain.doFilter) }

  isLoginUrlRequest determines whether the request is a loginPageUrl

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

  defaults to loginPageUrl = / login because we don't have a configuration.

  verifies that loginPageUrl can be matched through the request path

String loginPageHtml = generateLoginPageHtml (request, loginError, logoutSuccess)

  generateLoginPageHtml draws the default HTML page, which explains how we got the default login page.

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" + "Please sign in\ 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 ();}

At this point, the SpringSecurity default form login page shows that the source code of the process has been fully explained, and the following page will be rendered, but there must be a network, otherwise the style may change.

6. Summary

This article focuses on the process of how the default form login page provided by SpringSecurity is displayed, including three related filters involved in this process

1.FilterSecurityInterceptorPower2.ExceptionTranslationFilter, 3.DefaultLoginPageGeneratingFilter filter, and a brief introduction to AccessDecisionManager, which mainly votes to determine whether the user can access the corresponding resource AccessDecisionManager voting mechanism, and I have not delved into it. I will delve into it in detail and expand it later.

On the SpringSecurity default form login page to show how the process source code is shared here, I hope the above content can be of some help to you, can learn more knowledge. If you think the article is good, you can share it for more people to see.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report