In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/02 Report--
Spring Security how to analyze the authorization process, many novices are not very clear about this, in order to help you solve this problem, the following editor will explain for you in detail, people with this need can come to learn, I hope you can gain something.
Spring Security parsing (1)-- Authorization process
> when learning Spring Cloud, always has little knowledge of oauth related to authorization service, so he decides to first study and sort through the permissions such as Spring Security, Spring Security Oauth3, authentication-related content, principles and design.
> Project environment: >-JDK1.8 >-Spring boot 2.x >-Spring Security 5.x
1. A simple Security Demo1, custom UserDetailsService implementation
customizes the MyUserDetailsUserService class and implements the loadUserByUsername () method of the UserDetailsService interface, which simply returns a User object provided by Spring Security. In order to demonstrate the permission control of Spring Security later, we use AuthorityUtils.commaSeparatedStringToAuthorityList ("admin") to set the role permission information of the user account with an admin. In the actual project, the information of users and their roles and permissions can be obtained by accessing the database.
@ Componentpublic class MyUserDetailsUserService implements UserDetailsService {@ Override public UserDetails loadUserByUsername (String username) throws UsernameNotFoundException {/ / cannot be encrypted directly using the create BCryptPasswordEncoder object, which does not have a {bcrypt} prefix / / causes problems with algorithms that cannot get encryption during matches / / java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null" / / the reason is that Spring Security5 uses DelegatingPasswordEncoder (delegate) instead of NoOpPasswordEncoder / / and BCryptPasswordEncoder encryption is used by default (note that the DelegatingPasswordEncoder delegated encryption method BCryptPasswordEncoder encryption is prefixed with the encryption type) https://blog.csdn.net/alinyua/article/details/80219500 return new User ("user", PasswordEncoderFactories.createDelegatingPasswordEncoder () .encode ("123456"), AuthorityUtils.commaSeparatedStringToAuthorityList ("admin")) }}
note that Spring Security 5 does not use NoOpPasswordEncoder as its default cipher encoder at the beginning, but uses DelegatingPasswordEncoder as its cryptographic encoder by default. Its encode method implements encode by using the name of the cryptographic encoder as the prefix + delegating all kinds of cryptographic encoders.
Public String encode (CharSequence rawPassword) {return "{" + this.idForEncode + "}" + this.passwordEncoderForEncode.encode (rawPassword);}
the idForEncode here is the abbreviated name of the password encoder. You can see through the internal implementation of PasswordEncoderFactories.createDelegatingPasswordEncoder () that the prefix used by default is bcrypt, that is, BCryptPasswordEncoder.
Public class PasswordEncoderFactories {public static PasswordEncoder createDelegatingPasswordEncoder () {String encodingId = "bcrypt"; Map encoders = new HashMap (); encoders.put (encodingId, new BCryptPasswordEncoder ()); encoders.put ("ldap", new LdapShaPasswordEncoder ()); encoders.put ("MD4", new Md4PasswordEncoder ()); encoders.put ("MD5", new MessageDigestPasswordEncoder ("MD5")); encoders.put ("noop", NoOpPasswordEncoder.getInstance ()) Encoders.put ("pbkdf2", new Pbkdf2PasswordEncoder ()); encoders.put ("scrypt", new SCryptPasswordEncoder ()); encoders.put ("SHA-1", new MessageDigestPasswordEncoder ("SHA-1")); encoders.put ("SHA-256", new MessageDigestPasswordEncoder ("SHA-256")); encoders.put ("sha256", new StandardPasswordEncoder ()); return new DelegatingPasswordEncoder (encodingId, encoders);}} 2. Set Spring Security configuration
defines the SpringSecurityConfig configuration class and inherits WebSecurityConfigurerAdapter to override its configure (HttpSecurity http) method.
@ Configuration@EnableWebSecurity / / 1public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {@ Override protected void configure (HttpSecurity http) throws Exception {http.formLogin () / / 2 and () .authorizeRequests () / 3. AntMatrices ("/ index", "/"). PermitAll () / / 4. AnyRequest (). Authenticated (); / / 6}}
Configuration resolution:
@ EnableWebSecurity checks the source code of its annotations, mainly by quoting WebSecurityConfiguration.class and adding @ EnableGlobalAuthentication annotations. We will not introduce them here, as long as we understand that adding @ EnableWebSecurity annotations will enable the Security function.
FormLogin () uses a form login (the default request address is / login). In Spring Security 5, it has actually changed the default httpBasic () to formLogin () in the old version, and this is to show that the form login is configured once.
AuthorizeRequests () starts requesting permission configuration
AntMatchers () uses Ant-style path matching, where matching / and / index are configured
PermitAll () users can access it at will
AnyRequest () matches all paths
Authenticated () users can be accessed after logging in
3. Configure html and test interface
creates a new index.html under the resources/static directory, which internally defines a button to access the test interface.
Welcome, Spring Security. Welcome!
Test and verify Security permission control
creates a rest-style interface for obtaining user information
@ RestControllerpublic class TestController {@ GetMapping ("/ get_user/ {username}") public String getUser (@ PathVariable String username) {return username;}} 4, start the project test
1. Access to localhost:8080 is a direct success without any obstruction.
2. Click the test verification permission control button to be redirected to the default login page of Security.
3. Log in using the default account user: 123456 defined by MyUserDetailsUserService and successfully jump to the / get_user interface.
2. @ EnableWebSecurity configuration resolution
Does remember saying that @ EnableWebSecurity references the WebSecurityConfiguration configuration class and the @ EnableGlobalAuthentication annotation? WebSecurityConfiguration is the configuration related to authorization, and @ EnableGlobalAuthentication configures authentication-related configuration, which we will discuss in the next section.
first let's look at the WebSecurityConfiguration source code and we can clearly find the springSecurityFilterChain () method.
Bean (name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) public Filter springSecurityFilterChain () throws Exception {boolean hasConfigurers = webSecurityConfigurers! = null & &! webSecurityConfigurers.isEmpty () If (! hasConfigurers) {WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor .postProcess (new WebSecurityConfigurerAdapter () {}); webSecurity.apply (adapter);} return webSecurity.build () / / 1}
this method will first determine whether webSecurityConfigurers is empty, load a default WebSecurityConfigurerAdapter object for the empty, because the custom SpringSecurityConfig itself inherits the WebSecurityConfigurerAdapter object, so our custom Security configuration will certainly be loaded (if you want to know how to load it, you can take a look at the WebSecurityConfiguration.setFilterChainProxySecurityConfigurer () method).
Let's take a look at the webSecurity.build () method implementation that actually calls the AbstractConfiguredSecurityBuilder.doBuild () method, and its internal implementation is as follows:
@ Override protected final O doBuild () throws Exception {synchronized (configurers) {buildState = BuildState.INITIALIZING; beforeInit (); init (); buildState = BuildState.CONFIGURING; beforeConfigure (); configure () BuildState = BuildState.BUILDING; O result = performBuild (); / / 1 create DefaultSecurityFilterChain (SecurityFilter chain of responsibility) buildState = BuildState.BUILT; return result;}}
We focus on the performBuild () method and see that the subclass HttpSecurity.performBuild () method, which internally sorts the filters and creates the DefaultSecurityFilterChain object.
Override protected DefaultSecurityFilterChain performBuild () throws Exception {Collections.sort (filters, comparator); return new DefaultSecurityFilterChain (requestMatcher, filters);}
When looks at the construction method of DefaultSecurityFilterChain, we can see that there are logs.
Public DefaultSecurityFilterChain (RequestMatcher requestMatcher, List filters) {logger.info ("Creating filter chain:" + requestMatcher + "," + filters); / / normally, we can see the console output this log this.requestMatcher = requestMatcher; this.filters = new ArrayList (filters);}
, we can look back at the project startup log. You can see that the following figure clearly prints this log and prints out all the Filter names. = (please note the filter chain printed here, all of our authorization processes are based on this filter chain) = =
then there is another question: how is the filters in the HttpSecurity.performBuild () method loaded? At this point, you need to look at the WebSecurityConfigurerAdapter.init () method, which internally calls the getHttp () method to return the HttpSecurity object (see here we should be able to think of filters as the data added to this method), without describing how to load it.
Public void init (final WebSecurity web) throws Exception {final HttpSecurity http = getHttp (); / / 1 web.addSecurityFilterChainBuilder (http) .postBuildAction (new Runnable () {public void run () {FilterSecurityInterceptor securityInterceptor = http .getSharedObject (FilterSecurityInterceptor.class)) Web.securityInterceptor (securityInterceptor);});}
It took such a long time to parse @ EnableWebSecurity. In fact, the most important thing is to create the DefaultSecurityFilterChain, that is, we often security filter the responsibility chain. Next, we parse the authorization process around the filters in this DefaultSecurityFilterChain.
III. Analysis of authorization process
> the authorization process of Security can be understood as the final completion of an authorization by various filter processes. So let's take a look at the filter chain printed before. Here, for convenience, post the picture again.
here we only focus on the following important filter: >-SecurityContextPersistenceFilter >-UsernamePasswordAuthenticationFilter (AbstractAuthenticationProcessingFilter) >-BasicAuthenticationFilter >-AnonymousAuthenticationFilter >-ExceptionTranslationFilter >-FilterSecurityInterceptor
1 、 SecurityContextPersistenceFilter
The filter of SecurityContextPersistenceFilter is mainly responsible for the following things:
>-get the SecurityContext (Security context, similar to ApplicaitonContext) object from the request Session through the (SecurityContextRepository) repo.loadContext () method. If an authentication (key object for authentication) is not created by default in the request Session, since this section only talks about authorization SecurityContext object with the attribute null >-SecurityContextHolder.setContext () put the SecurityContext object into SecurityContextHolder for management (SecurityContextHolder uses ThreadLocal policy by default to store authentication information) >-because the implementation in finally clears the SecurityContext object from the SecurityContextHolder through SecurityContextHolder.clearContext () at last >-because the implementation in finally finally puts the SecurityContext object in Session through repo.saveContext ()
HttpRequestResponseHolder holder = new HttpRequestResponseHolder (request, response); / / get the SecurityContxt object from Session, and if not in Session, create a SecurityContext object with the authtication attribute null SecurityContext contextBeforeChainExecution = repo.loadContext (holder) Try {/ / put SecurityContext objects into SecurityContextHolder for management (SecurityContextHolder uses ThreadLocal policy by default to store authentication information) SecurityContextHolder.setContext (contextBeforeChainExecution); chain.doFilter (holder.getRequest (), holder.getResponse ()) } finally {SecurityContext contextAfterChainExecution = SecurityContextHolder .getContext (); / / clear the SecurityContext object from the SecurityContextHolder SecurityContextHolder.clearContext () / / put the SecurityContext object into Session repo.saveContext (contextAfterChainExecution, holder.getRequest (), holder.getResponse ()); request.removeAttribute (FILTER_APPLIED) If (debug) {logger.debug ("SecurityContextHolder now cleared, as request processing completed");}}
Let's make a breakpoint in SecurityContextPersistenceFilter, start the project, visit localhost:8080, and take a look at the debug implementation:
We can clearly see that we have created a SecurityContext object with authtication as null, and we can see the specific filter chain that the request calls. Next, let's take a look at the finally internal processing.
you will find that the authtication in the SecurityContxt here is an authentication information called anonymousUser (anonymous user). This is because the request is called to AnonymousAuthenticationFilter, and Security creates an anonymous user access by default.
2. UsernamePasswordAuthenticationFilter (AbstractAuthenticationProcessingFilter)
knows from the literal meaning of filter that this is a filter that authorizes by obtaining the account password in the request. According to the usual practice, it organizes the duties of the filter: >-determine whether to request / login > by requiresAuthentication ()-call the attemptAuthentication () method for authentication, internally create a UsernamePasswordAuthenticationToken object with the authenticated attribute of false (that is, unauthorized), and pass it to the AuthenticationManager () .authenticate () method for authentication. Return an authenticated = true (i.e. authorized successfully) UsernamePasswordAuthenticationToken object >-put Authentication into Session through sessionStrategy.onAuthentication () >-call AuthenticationSuccessHandler's onAuthenticationSuccess interface through successfulAuthentication () for successful processing (you can write successful processing logic by inheriting AuthenticationSuccessHandler) successfulAuthentication (request, response, chain, authResult) >-call AuthenticationFailureHandler's onAuthenticationFailure API through unsuccessfulAuthentication () to handle failures (you can write your own failure handling logic by inheriting AuthenticationFailureHandler)
, let's take a look at the processing logic of the official source code:
/ / 1 doFilter method of AbstractAuthenticationProcessingFilter public void doFilter (ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res / / 2 determine whether the request address is / login and the request method is POST (determined by UsernamePasswordAuthenticationFilter construction method) if (! requiresAuthentication (request, response)) {chain.doFilter (request, response); return;} Authentication authResult Try {/ / 3 calls the attemptAuthentication method / / attemptAuthentication method of the subclass UsernamePasswordAuthenticationFilter to create a UsernamePasswordAuthenticationToken object whose authenticated attribute is false (that is, unauthorized), and passes it to the AuthenticationManager () .authenticate () method for authentication. / / return an authenticated = true (that is, authorized successfully) UsernamePasswordAuthenticationToken object authResult = attemptAuthentication (request, response) after successful authentication If (authResult = = null) {return;} / / 4 deposits the successfully authenticated Authentication in Session sessionStrategy.onAuthentication (authResult, request, response) } catch (InternalAuthenticationServiceException failed) {/ / 5 after authentication fails, call AuthenticationFailureHandler's onAuthenticationFailure API for failure handling (you can write failure handling logic by inheriting AuthenticationFailureHandler) unsuccessfulAuthentication (request, response, failed); return } catch (AuthenticationException failed) {/ / 5 after authentication fails, call AuthenticationFailureHandler's onAuthenticationFailure API for failure handling (you can write failure handling logic by inheriting AuthenticationFailureHandler) unsuccessfulAuthentication (request, response, failed); return }. / / 6 call AuthenticationSuccessHandler's onAuthenticationSuccess API for failure handling after successful authentication (you can inherit AuthenticationSuccessHandler to write successful processing logic) successfulAuthentication (request, response, chain, authResult);}
from the source code point of view, the whole process is actually very clear: from judging whether to process, to authentication, and finally to judge the authentication results to deal with the success and failure of authentication respectively.
Check the result under debug debugging. This time we request localhast:8080/get_user/test. Since we have no permission, we will jump directly to the login interface. First, we enter the wrong account password to see if the authentication failure is consistent with our summary.
The results are consistent with expectations, and you may wonder why the hints here are in Chinese, so you have to say that Security 5 began to support Chinese, indicating that Chinese programmers are becoming more and more important in the world!
Enter the correct password for this time and take a look at the returned Authtication object information:
can see that this time it successfully returns an authticated = ture, user account information without a password, and also contains an admin permission information that we defined. Release the breakpoint, and since Security's default successful processor is SimpleUrlAuthenticationSuccessHandler, this processor will be redirected to the previously accessed address, which is localhast:8080/get_user/test. This is the end of the whole process. No, we need one more, Session, and we see Session in the browser Cookie:
3 、 BasicAuthenticationFilter
BasicAuthenticationFilter is similar to UsernameAuthticationFilter, but the difference is obvious. BasicAuthenticationFilter mainly obtains Authorization parameter information from Header, and then calls authentication. After successful authentication, the API is accessed directly, unlike the UsernameAuthticationFilter process, which is redirected through AuthenticationSuccessHandler. Here is no longer posted code, students who want to know can directly look at the source code. One thing to note, however, is that BasicAuthenticationFilter's onSuccessfulAuthentication () successful processing method is an empty method.
in order to experiment with BasicAuthenticationFilter, we need to replace formLogin () in SpringSecurityConfig with httpBasic () to support BasicAuthenticationFilter, restart the project, and also access localhast:8080/get_user/test. Because we do not have permission to access this interface address, a login box will pop up on the page. Students who are familiar with Security4 must look familiar. Similarly, after entering the account password, take a look at the debug data:
At this time, , we can obtain the Authorization parameter, and then parse the account and password information obtained to verify. We check that the Authtication object information returned after successful verification is in fact the same as that in UsernamePasswordAuthticationFilter. Finally, we call the next filter again. Since the authentication has been successful, we will directly enter the FilterSecurityInterceptor for permission verification.
4 、 AnonymousAuthenticationFilter
Why mentions AnonymousAuthenticationFilter here is mainly because there is no saying that there is no account in Security (the description here may not be very clear, but the general meaning is like this). This AnonymousAuthenticationFilter is officially specified for this Security, which is used to automatically create a default anonymous user with anonymous access if all previous filter authentication fails. Remember the anonymous autication information we saw when explaining SecurityContextPersistenceFilter? If you don't remember, you have to look back, then you won't talk about it here.
5 、 ExceptionTranslationFilter
ExceptionTranslationFilter doesn't actually do any filtering, but don't underestimate its usefulness. The biggest and most awesome thing about it is that it catches AuthenticationException and AccessDeniedException. If these two exceptions occur, it will call the handleSpringSecurityException () method to handle them. To simulate the situation of AccessDeniedException (no permission, access exception is prohibited), we first need to modify the / get_user API:
Add @ EnableGlobalMethodSecurity (prePostEnabled = true) to Controller to enable permission control at the Security method level
Add @ PreAuthorize ("hasRole ('user')") to the interface to allow access only to accounts with the user role (remember the admin role when we default to the user account? )
@ RestController@EnableGlobalMethodSecurity (prePostEnabled = true) / / enable method-level permission control public class TestController {@ PreAuthorize ("hasRole ('user')") / / only allow user roles to access @ GetMapping ("/ get_user/ {username}") public String getUser (@ PathVariable String username) {return username;}}
restart the project, re-visit the / get_user interface, enter the correct account password, and find that an error page with a 403 status is returned, which is consistent with the process we will use before. Debug, take a look at the processing:
can clearly see that the exception object is AccessDeniedException, and access to the exception information is not allowed. Let's take a look at the handling method accessDeniedHandler.handle () after the AccessDeniedException exception, and enter the handle () method of AccessDeniedHandlerImpl. This method will first determine whether the system is configured with errorPage (error page). If not, set the 403 status code directly into the response.
6 、 FilterSecurityInterceptor
FilterSecurityInterceptor is the last and most important one in the whole Security filter chain. Its main function is to determine whether the user with successful authentication has permission to access the interface. The main processing method is to call the super.beforeInvocation (fi) of the parent class (AbstractSecurityInterceptor). Let's sort out the processing flow of this method:
>-obtain the permission information required for the current access address through obtainSecurityMetadataSource (). GetAttributes () >-obtain the permission information of the current access user through authenticateIfRequired () >-use the voting mechanism to determine the right through accessDecisionManager.decide (). If the decision fails, an AccessDeniedException exception is thrown directly.
Protected InterceptorStatusToken beforeInvocation (Object object) {. / / 1 get permission information of access address Collection attributes = this.obtainSecurityMetadataSource () .getAttributes (object); if (attributes = = null | | attributes.isEmpty ()) {. Return null;}. / / 2 get the current access user permission information Authentication authenticated = authenticateIfRequired (); try {/ / 3 calls the AffirmativeBased.decide () method by default, and its internal AccessDecisionVoter object is used for voting mechanism to determine the right. If the decision fails, the AccessDeniedException exception this.accessDecisionManager.decide (authenticated, object, attributes) is thrown directly. } catch (AccessDeniedException accessDeniedException) {publishEvent (new AuthorizationFailureEvent (object, attributes, authenticated, accessDeniedException); throw accessDeniedException;}. Return new InterceptorStatusToken (SecurityContextHolder.getContext (), false, attributes, object);}
In fact, the whole process of does not look complicated. It is mainly divided into three parts. The first is to obtain the permission information of the access address, the second is to obtain the permission information of the current access user, and finally the voting mechanism is used to determine whether the user has the right.
Third, the core of personal summary of the whole authorization process lies in the processing of these core filter. Here, I use the sequence diagram to outline the authorization process.
Is it helpful for you to read the above content? If you want to know more about the relevant knowledge or read more related articles, please follow the industry information channel, thank you for your support.
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.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.