The process is briefly

When we successfully log in and obtain the access_token, we can use the token to access the authorized interface. As mentioned above, JwtAuthenticationFilter converts the access_token into Authentication that can be recognized by the system and puts it into the security context. Then came to the last filter FilterSecurityInterceptor, the filter is to judge whether the request has permissions.

public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
  
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
	  FilterInvocation fi = new FilterInvocation(request, response, chain);
	  invoke(fi);
	}
	
  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);
	  }
	  // The process before the request, that is, the actual authentication process
	  InterceptorStatusToken token = super.beforeInvocation(fi);
	  try {
	    // Request a real controller
	    fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
	  }
	  finally {
	    super.finallyInvocation(token);
	  }
	  // Work after request
	  super.afterInvocation(token, null); }}}Copy the code

FilterSecurityInterceptor main body of the method is still in the doFilter, and one of the main methods to invoke (), can be roughly divided into three steps:

  1. beforeInvocation(fi); Verify that the required permissions for Authentication and the destination URL in the Context match. If yes, the permission is passed. If no, an exception is thrown.
  2. fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); In this case, you can view it as actually accessing the target Controller.
  3. afterInvocation(token, null); Action after getting the request.

First look at the beforeInvocation()

beforeInvocation

abstract class AbstractSecurityInterceptor {
  protected InterceptorStatusToken beforeInvocation(Object object) {
     // Get the permission content of the target URL, either from Configuration or MetadataSource
     Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
     / /... omit
    
  	 Authentication authenticated = authenticateIfRequired();
  
  	 // Attempt authorization
  	 try {
  	    // AccessDecisionManager is used to verify that the permissions in Authentication match the permissions required by the target URL. If they do not match, an AccessDeniedException is thrown
  	    this.accessDecisionManager.decide(authenticated, object, attributes);
  	 }
  	 catch (AccessDeniedException accessDeniedException) {
  		publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
  			accessDeniedException));
    			throw accessDeniedException;
  	 }
  
  	 // Attempt to run as a different user
  	 Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);
  	 
  	 // the next step is to generate the InterceptorStatusToken for the AfterInvocation step. You can read it for yourself
  	 if (runAs == null) {
  	   // no further work post-invocation
  		return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);
  	 }
  	 else {
  		SecurityContext origCtx = SecurityContextHolder.getContext();
  		SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
  		SecurityContextHolder.getContext().setAuthentication(runAs);
  		// need to revert to token.Authenticated post-invocation
  		return new InterceptorStatusToken(origCtx, true, attributes, object); }}}Copy the code
  1. Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);Get the permissions required by the target URL, this classFilterInvocationSecurityMetadataSourceInterface methods. Url permissions can also be configured fromWebSecurityConfigThe Configuration method in.
  2. this.accessDecisionManager.decide(authenticated, object, attributes);judgeAuthenticationWhether the required permission of the target URL is matched. If it does not match, it is thrownAccessDeniedExceptionThe exception. The method comes fromAbstractAccessDecisionManagerIs implemented by defaultAffirmativeBased.
  3. new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);implementationInterceptorStatusTokenAnd returns, including information in the parameters, such as the security context, the permissions required for the destination URL, and the original access request.

The target Controller is then accessed to retrieve the actual request.

afterInvocation

AfterInvocationManger when we enable @preauthorize () and @postauthorize () annotations, the validation logic follows.

abstract class AbstractSecurityInterceptor {
  protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) {
    if (token == null) {
  	  // public object
  	  return returnedObject;
  	}
  
  	finallyInvocation(token); // continue to clean in this method for passivity
  
  	if(afterInvocationManager ! =null) {
  	// Attempt after invocation handling
  	  try {
  		returnedObject = afterInvocationManager.decide(token.getSecurityContext()
  		  .getAuthentication(), token.getSecureObject(), token
  		  .getAttributes(), returnedObject);
  	  }
  	  catch (AccessDeniedException accessDeniedException) {
  		AuthorizationFailureEvent event = new AuthorizationFailureEvent(
  		  token.getSecureObject(), token.getAttributes(), token
  			.getSecurityContext().getAuthentication(),
  		    	accessDeniedException);
  		publishEvent(event);
  		throwaccessDeniedException; }}returnreturnedObject; }}Copy the code

The following code contains a concrete implementation of AfterInvocationManager.

public class GlobalMethodSecurityConfiguration {
  protected AfterInvocationManager afterInvocationManager(a) {
    if (prePostEnabled()) {
  	  AfterInvocationProviderManager invocationProviderManager = new AfterInvocationProviderManager();
  		ExpressionBasedPostInvocationAdvice postAdvice = new ExpressionBasedPostInvocationAdvice(
  	    	getExpressionHandler());
  		PostInvocationAdviceProvider postInvocationAdviceProvider = new PostInvocationAdviceProvider(
  			postAdvice);
  		List<AfterInvocationProvider> afterInvocationProviders = new ArrayList<>();
  		afterInvocationProviders.add(postInvocationAdviceProvider);
  		invocationProviderManager.setProviders(afterInvocationProviders);
  		return invocationProviderManager;
  	  }
  	return null; }}Copy the code

What can be done?

  1. Realize FilterInvocationSecurityMetadataSource, when used to start loading url required permissions, so you need not target url in the configuration or annotations permissions’ write ‘death. Can the realization of the reference example written MyFilterInvocationSecurityMetadataSource.

  2. Overloading AbstractAccessDecisionManager, according to the business needs to be rewritten, request target permissions and privileges in the Authentication validation process. For example, the default RBAC in Spring Security is that permission authentication is based on roles, and fixed roles can only access fixed interfaces. Now we need the ACL permissions model, user permissions for A 1, user permissions for 5 B, user permissions for 9 C, interface requires A permission to 6, the user can access C, while the user A and B can not access, means permission can access the interface, if need to change the permissions are overloaded class model.

conclusion

What are the main authorization processes?

  1. Gets the permissions required by the request target fromFilterInvocationSecurityMetadataSourceThe implementation class of the interface.
  2. Compare the security contextAuthenticationIs the permission match inAbstractAccessDecisionManagerIs compared in the implementation class of.

link

The code involved in the article has been uploaded to Gitee for your reference: gitee.com/yangzijing/…

Spring Security source code is huge and complex, my level is limited, the article inevitably has mistakes and omissions, unclear, please pay. We welcome exchanges and hope to make progress together with you.