Spring Security Permission Authentication Basics
Authorization authentication in Spring Security is handled by the AccessDecisionManager interface. Specifically, the decide() method is responsible for it, which is defined as follows.
void decide ( Authentication authentication , Object object , Collection < ConfigAttribute > configAttributes ) throws AccessDeniedException , InsufficientAuthenticationException ;
As you can see, the method accepts three parameters, the first parameter is the Authentication object that contains the current user information; the second parameter represents the protected object currently being requested, basically MethodInvocation (using AOP), JoinPoint There are three types (using Aspectj) and FilterInvocation (Web request); the third parameter represents a configuration property related to the protected object currently being accessed, such as a list of roles.
Spring Security's AOP Advice idea
For using AOP, we can use several different types of advice: before, after, throws and around. Among them, around advice is very practical, through which we can control whether to execute the method, whether to modify the return value of the method, and whether to throw an exception. Spring Security also uses the idea of around advice for method calls and web requests. When invoking a method, you can use standard Spring AOP to achieve the effect of around advice, and when making Web requests, you can achieve the effect of around advice through standard Filter.
For most people, they prefer to control the permissions of method calls in the Service layer, because our main business logic is implemented in the Service layer. If you just want to protect the methods of the Service layer, then using Spring AOP is fine. If you need to protect domain objects directly, then you might consider using Aspectj.
You can choose to use Aspectj or Spring AOP to authenticate method calls, or choose to use Filter to authenticate web requests. Of course, you can also choose to use any combination of these three methods for authentication. The usual practice is to use Filter to perform a rougher authentication on Web requests, supplemented by Spring AOP to perform finer-grained authentication to the methods of the Service layer.
AbstractSecurityInterceptor
AbstractSecurityInterceptor is an abstract class that implements interception of access to protected objects. There are several important methods. The beforeInvocation() method implements the permission check for accessing protected objects, and uses AccessDecisionManager and AuthenticationManager internally; the finallyInvocation() method is used to implement some cleanup work after the protected object request is completed, mainly if it is changed in beforeInvocation() The SecurityContext needs to be restored to the original SecurityContext in finallyInvocation(). The call of this method should be included in the finally statement block when the subclass requests the protected resource; the afterInvocation() method implements the processing of the returned result. In the case of injected AfterInvocationManager, its decide() method will be called by default. AbstractSecurityInterceptor only provides these methods and includes the default implementation. How to call it will be the responsibility of the subclass. Each protected object has an interceptor class that inherits from AbstractSecurityInterceptor, MethodSecurityInterceptor will be used to call protected methods, and FilterSecurityInterceptor will be used for protected Web requests. They all have the same logic when handling requests for protected objects. The specific logic is as follows.
First pass the protected object being called to the beforeInvocation() method for permission authentication.
If the authorization authentication fails, an exception is thrown directly.
A successful authentication will attempt to invoke the protected object, and after the invocation is complete, whether the invocation succeeds or an exception is thrown, finallyInvocation() will be executed.
AfterInvocation() is called if no exception was thrown after calling the protected object.
The following is the core code of MethodSecurityInterceptor making method calls.
public Object invoke ( MethodInvocation mi ) throws Throwable { InterceptorStatusToken token = super.beforeInvocation ( mi ) ; _ Object result ; try { result = mi . proceed (); } finally { super.finallyInvocation ( token ) ; _ } returnsuper .afterInvocation ( token , result ) ; }
ConfigAttribute
The beforeInvocation() method of AbstractSecurityInterceptor uses the decide() method of the injected AccessDecisionManager to perform authentication. As mentioned earlier, the decide() method needs to receive a ConfigAttribute collection corresponding to a protected object. A ConfigAttribute might just be a simple role name, depending on the implementer of AccessDecisionManager. The AbstractSecurityInterceptor will use a SecurityMetadataSource object to obtain the set of ConfigAttributes associated with the protected object, the concrete SecurityMetadataSource will be provided by the subclass implementation. ConfigAttribute will be defined in the form of an annotation on a protected method, or on a protected URL through the access attribute. For example, it is common for us to apply ConfigAttribute ROLE_USER and ROLE_ADMIN to all URL requests. For the default AccessDecisionManager implementation, the above configuration means that access is allowed as long as the user has a GrantedAuthority that matches one of these two ConfigAttributes. Of course, strictly speaking, ConfigAttribute is just a simple configuration attribute, and the specific interpretation will be decided by AccessDecisionManager.
RunAsManager
In some cases you may want to replace the Authentication stored in the SecurityContext. This can be achieved with RunAsManager. In the beforeInvocation() method body of AbstractSecurityInterceptor, after the AccessDecisionManager is successfully authenticated, a new Authentication will be constructed on the basis of the existing Authentication through RunAsManager. If the new Authentication is not empty, a new SecurityContext will be generated, and the new Authentication will be generated. The resulting Authentication is stored in it. In this way, the Authentication obtained from the SecurityContext when the protected resource is requested is the newly generated Authentication. After the request is completed, the original SecurityContext will be reset to the SecurityContextHolder in finallyInvocation(). AbstractSecurityInterceptor holds a NullRunAsManager with an empty implementation of RunAsManager by default. In addition, Spring Security has a non-empty implementation class RunAsManagerImpl for RunAsManager, which has the following logic when constructing a new Authentication: if the ConfigAttribute corresponding to the protected object has a configuration attribute starting with "RUN_AS_", then in this Add "ROLE_" before the attribute, and then assign it as a GrantedAuthority to the Authentication to be created (for example, if there is a "RUN_AS_ADMIN" attribute in ConfigAttribute, a GrantedAuthority of "ROLE_RUN_AS_ADMIN" will be constructed), and finally use the original Authentication Principal, permissions and other information to build a new Authenticat ion to return; if there is no ConfigAttribute starting with "RUN_AS_", return null directly. The core code of RunAsManagerImpl to build a new Authentication is shown below.
public Authentication buildRunAs ( Authentication authentication , Object object , Collection < ConfigAttribute > attributes ) { List < GrantedAuthority > newAuthorities = new ArrayList < GrantedAuthority >(); for ( ConfigAttribute attribute : attributes ) { if ( this . supports ( attribute )) { GrantedAuthority extraAuthority = new SimpleGrantedAuthority ( getRolePrefix () + attribute . getAttribute ()); newAuthorities . add ( extraAuthority ); } } if ( newAuthorities . size () == 0 ) { returnnull ; } // Add existing authorities newAuthorities . addAll ( authentication . getAuthorities ()); return new RunAsUserToken ( this . key , authentication . getPrincipal ( ), authentication . getCredentials (), newAuthorities , authentication . getClass ()); }
AfterInvocationManager
After the request for the protected object is complete, the return value can be modified through the afterInvocation() method. The AbstractSecurityInterceptor transfers the control of modifying the return value to the AfterInvocationManager it holds. AfterInvocationManager can choose to modify the return value, not modify it, or throw an exception (for example, the post-authorization authentication fails).
The following is a diagram of the relationship between AbstractSecurityInterceptor provided by the official Spring Security documentation.
0 Comments