shiro的用戶校驗與授權處理流程調用棧2(詳細)

shiro的實現權限管理的流程

一、登錄認證

  1. 發起請求之後:會經過doFilter判斷需要哪些權限,在複習shiro之前需要對springMvc有一定了解 ,根據debug順便複習一下MVC的處理流程—根據遞歸調用棧:

    • 首先根據Httpservlet判斷是post還是Get請求

    • 根據servlet中的request攜帶的的路徑以及參數信息交給前端控制器處理分發

    • DispatcherServlet前端控制器將請求信息轉發給RequestMappingHandlerAdapter映射處理器進程處理轉發

    • ModelAndView mav;

      mav = invokeHandlerMethod(request, response, handlerMethod);處理得到一個ModelAndView 視圖模型

    • 進一步通過method.invoke方法,反射的機制還有動態代理的機制進行回調Controller方法(回調是滿足方法的條件之後重新執行)

  2. 回到shrio部分:

    • 權限驗證部分

      1. controller層:subject主體實例調用login方法

                Subject subject = SecurityUtils.getSubject();
                try {
                    subject.login(token);//核心
                    return R.ok();
        
      2. 實際調用:public class DelegatingSubject implements Subject

            public void login(AuthenticationToken token) throws AuthenticationException {
                clearRunAsIdentitiesInternal();
                Subject subject = securityManager.login(this, token);//調用
        
      3. 實際調用:securityManager

            public void login(AuthenticationToken token) throws AuthenticationException {
                clearRunAsIdentitiesInternal();
                Subject subject = securityManager.login(this, token)//實際實例的方法調用
        
      4. 實際調用:AuthenticationInfo

            public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
                AuthenticationInfo info;//實際
                try {
                    info = authenticate(token);//進一步調用
        
      5. 然而:AuthenticationInfo方法被我們重寫了裏面的邏輯,實際查詢數據庫驗證賬戶邏輯需要我們自己編寫:UserRealm extends AuthorizingRealm

        
                ```java
                	@Override
        	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        		String username = (String) token.getPrincipal();
        		Map<String, Object> map = new HashMap<>(16);
        		map.put("username", username);
        		String password = new String((char[]) token.getCredentials());
        
        	UserDao userMapper = ApplicationContextRegister.getBean(UserDao.class);
        		// 查詢用戶信息
        		UserDO user = userMapper.list(map).get(0);
        
                ```
        

      二、授權部分

      1. 點擊之後同樣是先是 springMVC處理轉發

      2. 關於shiro授權的部分

        1. 顯示mvc根據請求映射處理器轉發到後端的controller層
        	@RequiresPermissions("sys:role:role")
        	@GetMapping("/list")
        	@ResponseBody()
        	List<RoleDO> list() {
        		List<RoleDO> roles = roleService.list();
        		return roles;
        	}
        
        1. cglib動態代理的方式基於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);//註解的類作爲構造函數參數 ----核心
              }
          
        2. 動態代理調用: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);
                      //調用interface org.apache.shiro.authz.annotation.RequiresRoles--核心
          		}
          	}
          
          
        3. 調用目標方法

             public Object invoke(MethodInvocation methodInvocation) throws Throwable {
                  org.apache.shiro.aop.MethodInvocation mi = createMethodInvocation(methodInvocation);
                  return super.invoke(mi);//目標方法
              }
          
        4. 進一步調用:DegatingSubject中的方法:

              public void checkPermission(String permission) throws AuthorizationException {
                  assertAuthzCheckPossible();
                  securityManager.checkPermission(getPrincipals(), permission);//權限驗證部分
              }
          
        5. 繼續調用:抽象類AuthorizingRealm類中的獲取授權信息方法

          public abstract class AuthorizingRealm extends AuthenticatingRealm

                  if (info == null) {
                      // Call template method if the info was not found in a cache
                      info = doGetAuthorizationInfo(principals);//獲取授權信息
                      // 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. 然後到自己繼承抽象類重寫的方法中:自定義查詢數據庫的邏輯:根據三表關聯查詢jion on查找:

          	@Override
          	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
          		Long userId = ShiroUtils.getUserId();
          		MenuService menuService = ApplicationContextRegister.getBean(MenuService.class);
          		Set<String> perms = menuService.listPerms(userId);//獲取權限信息
          		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
          		info.setStringPermissions(perms);
          		return info;
          	}
          
          
        7. ModlarRealmAuthorizer類中的方法調用判斷是否具有訪問權限

              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. 判斷完成遞歸棧在彈棧

              public void checkPermission(PrincipalCollection principals, String permission) throws AuthorizationException {
                  assertRealmsConfigured();
                  if (!isPermitted(principals, permission)) {//權限判斷完成彈棧
                      throw new UnauthorizedException("Subject does not have permission [" + permission + "]");
                  }
              }
          

補充授權過程:

    3. 動態代理調用:interface org.apache.shiro.authz.annotation.RequiresRoles

在這裏插入圖片描述

    4. 調用目標方法

在這裏插入圖片描述
8. ModlarRealmAuthorizer類中的方法調用判斷是否具有訪問權限

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章