User verification and authorization process call stack 2 of shrio (detailed)

Posted by ClaytonBellmor on Sat, 20 Jun 2020 08:52:06 +0200

shiro's process of permission management

1, Login authentication

  1. After the request is initiated: it will judge which permissions are needed through doFilter. Before reviewing shiro, you need to have a certain understanding of springMvc. According to the debug, review the processing flow of MVC - according to the recursive call stack:

    • First, determine whether it is a post or Get request according to Httpservlet

    • According to the path and parameter information carried by the request in the servlet, it is handed over to the front-end controller for processing and distribution

    • The dispatcher servlet front-end controller forwards the request information to the RequestMappingHandlerAdapter mapping processor process for processing and forwarding

    • ModelAndView mav;

      mav = invokeHandlerMethod(request, response, handlerMethod); process to get a ModelAndView view view model

    • Further adopted method.invoke Method, the reflection mechanism and the dynamic agent mechanism call back the Controller method (the callback is executed again after the method conditions are met)

  2. Back to the shrio section:

    • Authority verification part

      1. controller layer:

                Subject subject = SecurityUtils.getSubject();
                try {
                    subject.login(token);//core
                    return R.ok();
        
      2. Actual call: public class DelegatingSubject implements Subject

            public void login(AuthenticationToken token) throws AuthenticationException {
                clearRunAsIdentitiesInternal();
                Subject subject = securityManager.login(this, token);//call
        
      3. Actual call: securityManager

            public void login(AuthenticationToken token) throws AuthenticationException {
                clearRunAsIdentitiesInternal();
                Subject subject = securityManager.login(this, token)//Method call of actual instance
        
      4. Actual call: AuthenticationInfo

            public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
                AuthenticationInfo info;//actual
                try {
                    info = authenticate(token);//Further call
        
      5. However, we have rewritten the logic of the AuthenticationInfo method. To query the database to verify the account logic, we need to write the user realm extensions authorizing realm ourselves

        	@Override
        	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
        		Long userId = ShiroUtils.getUserId();
        		Set<String> perms = menuService.listPerms(userId);
        		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        		info.setStringPermissions(perms);
        		return info;
        	}
        

      2, Authorized part

      1. After clicking, spring MVC processes the forwarding

      2. About shiro licensing

        1. Show the controller layer that mvc forwards to the backend according to the request mapping processor
        	@RequiresPermissions("sys:role:role")
        	@GetMapping("/list")
        	@ResponseBody()
        	List<RoleDO> list() {
        		List<RoleDO> roles = roleService.list();
        		return roles;
        	}
        
        1. cglib dynamic proxy is based on aop?

          package org.apache.shiro.authz.aop;
          
          import org.apache.shiro.authz.AuthorizationException;
          import org.apache.shiro.authz.annotation.Logical;
          import org.apache.shiro.authz.annotation.RequiresPermissions;
          import org.apache.shiro.authz.annotation.RequiresRoles;
          import org.apache.shiro.subject.Subject;
          
          import java.lang.annotation.Annotation;
          
          /**
           * Checks to see if a @{@link org.apache.shiro.authz.annotation.RequiresPermissions RequiresPermissions} annotation is
           * declared, and if so, performs a permission check to see if the calling <code>Subject</code> is allowed continued
           * access.
           *
           * @since 0.9.0
           */
          public class PermissionAnnotationHandler extends AuthorizingAnnotationHandler {
          
              /**
               * Default no-argument constructor that ensures this handler looks for
               * {@link org.apache.shiro.authz.annotation.RequiresPermissions RequiresPermissions} annotations.
               */
              public PermissionAnnotationHandler() {
                  super(RequiresPermissions.class);//Annotated class as constructor parameter -- core
              }
          
        2. Dynamic proxy call: interface org.apache.shiro.authz.annotation.RequiresRoles

          	@Override
          	@Nullable
          	public Object proceed() throws Throwable {
          		//	We start with an index of -1 and increment early.
          		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
          			return invokeJoinpoint();
          		}
          
          		Object interceptorOrInterceptionAdvice =
          				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
          		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
          			// Evaluate dynamic method matcher here: static part will already have
          			// been evaluated and found to match.
          			InterceptorAndDynamicMethodMatcher dm =
          					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
          			if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
          				return dm.interceptor.invoke(this);
          			}
          			else {
          				// Dynamic matching failed.
          				// Skip this interceptor and invoke the next in the chain.
          				return proceed();
          			}
          		}
          		else {
          			// It's an interceptor, so we just invoke it: The pointcut will have
          			// been evaluated statically before this object was constructed.
          			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
                      //Call interface org.apache.shiro.authz.annotation.RequiresRoles --Core
          		}
          	}
          
          
        3. Call target method

             public Object invoke(MethodInvocation methodInvocation) throws Throwable {
                  org.apache.shiro.aop.MethodInvocation mi = createMethodInvocation(methodInvocation);
                  return super.invoke(mi);//Target method
              }
          
        4. Further call: Method in DegatingSubject:

              public void checkPermission(String permission) throws AuthorizationException {
                  assertAuthzCheckPossible();
                  securityManager.checkPermission(getPrincipals(), permission);//Authority verification part
              }
          
        5. Continue to call: get authorization information method in the AuthorizingRealm class of abstract class

          public abstract class AuthorizingRealm extends AuthenticatingRealm

                  if (info == null) {
                      // Call template method if the info was not found in a cache
                      info = doGetAuthorizationInfo(principals);//Obtain authorization information
                      // If the info is not null and the cache has been created, then cache the authorization info.
                      if (info != null && cache != null) {
                          if (log.isTraceEnabled()) {
                              log.trace("Caching authorization info for principals: [" + principals + "].");
                          }
                          Object key = getAuthorizationCacheKey(principals);
                          cache.put(key, info);
                      }
                  }
          
        6. Then, in the method of inheriting abstract class rewriting, you can customize the logic of query database, query according to the three tables, and find the following:

          	@Override
          	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
          		Long userId = ShiroUtils.getUserId();
          		MenuService menuService = ApplicationContextRegister.getBean(MenuService.class);
          		Set<String> perms = menuService.listPerms(userId);//Get permission information
          		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
          		info.setStringPermissions(perms);
          		return info;
          	}
          
          
        7. Method call in the ModlarRealmAuthorizer class to determine whether it has access rights

              public boolean isPermitted(PrincipalCollection principals, String permission) {
                  assertRealmsConfigured();
                  for (Realm realm : getRealms()) {
                      if (!(realm instanceof Authorizer)) continue;
                      if (((Authorizer) realm).isPermitted(principals, permission)) {
                          return true;
                      }
                  }
                  return false;
              }
          
          
        8. Judge whether the recursive stack is in the spring stack

              public void checkPermission(PrincipalCollection principals, String permission) throws AuthorizationException {
                  assertRealmsConfigured();
                  if (!isPermitted(principals, permission)) {//Authority judgment complete pop-up stack
                      throw new UnauthorizedException("Subject does not have permission [" + permission + "]");
                  }
              }
          

Supplementary authorization process:

    3. Dynamic proxy call: interface org.apache.shiro.authz.annotation.RequiresRoles

    4. Call the target method


8. Method call in the modlarrealmauthorizer class to determine whether it has access permission

Topics: Shiro Apache Database Spring