shiro的實現權限管理的流程
一、登錄認證
-
發起請求之後:會經過doFilter判斷需要哪些權限,在複習shiro之前需要對springMvc有一定了解 ,根據debug順便複習一下MVC的處理流程—根據遞歸調用棧:
-
首先根據Httpservlet判斷是post還是Get請求
-
根據servlet中的request攜帶的的路徑以及參數信息交給前端控制器處理分發
-
DispatcherServlet前端控制器將請求信息轉發給RequestMappingHandlerAdapter映射處理器進程處理轉發
-
ModelAndView mav;
mav = invokeHandlerMethod(request, response, handlerMethod);處理得到一個ModelAndView 視圖模型
-
進一步通過method.invoke方法,反射的機制還有動態代理的機制進行回調Controller方法(回調是滿足方法的條件之後重新執行)
-
-
回到shrio部分:
-
權限驗證部分
-
controller層:subject主體實例調用login方法
Subject subject = SecurityUtils.getSubject(); try { subject.login(token);//核心 return R.ok();
-
實際調用:public class DelegatingSubject implements Subject
public void login(AuthenticationToken token) throws AuthenticationException { clearRunAsIdentitiesInternal(); Subject subject = securityManager.login(this, token);//調用
-
實際調用:securityManager
public void login(AuthenticationToken token) throws AuthenticationException { clearRunAsIdentitiesInternal(); Subject subject = securityManager.login(this, token)//實際實例的方法調用
-
實際調用:AuthenticationInfo
public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException { AuthenticationInfo info;//實際 try { info = authenticate(token);//進一步調用
-
然而: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); ```
二、授權部分
-
點擊之後同樣是先是 springMVC處理轉發
-
關於shiro授權的部分
- 顯示mvc根據請求映射處理器轉發到後端的controller層
@RequiresPermissions("sys:role:role") @GetMapping("/list") @ResponseBody() List<RoleDO> list() { List<RoleDO> roles = roleService.list(); return roles; }
-
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);//註解的類作爲構造函數參數 ----核心 }
-
動態代理調用: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--核心 } }
-
調用目標方法
public Object invoke(MethodInvocation methodInvocation) throws Throwable { org.apache.shiro.aop.MethodInvocation mi = createMethodInvocation(methodInvocation); return super.invoke(mi);//目標方法 }
-
進一步調用:DegatingSubject中的方法:
public void checkPermission(String permission) throws AuthorizationException { assertAuthzCheckPossible(); securityManager.checkPermission(getPrincipals(), permission);//權限驗證部分 }
-
繼續調用:抽象類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); } }
-
然後到自己繼承抽象類重寫的方法中:自定義查詢數據庫的邏輯:根據三表關聯查詢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; }
-
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; }
-
判斷完成遞歸棧在彈棧
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類中的方法調用判斷是否具有訪問權限