shiro's process of permission management
1, Login authentication
-
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)
-
-
Back to the shrio section:
-
Authority verification part
-
controller layer:
Subject subject = SecurityUtils.getSubject(); try { subject.login(token);//core return R.ok();
-
Actual call: public class DelegatingSubject implements Subject
public void login(AuthenticationToken token) throws AuthenticationException { clearRunAsIdentitiesInternal(); Subject subject = securityManager.login(this, token);//call
-
Actual call: securityManager
public void login(AuthenticationToken token) throws AuthenticationException { clearRunAsIdentitiesInternal(); Subject subject = securityManager.login(this, token)//Method call of actual instance
-
Actual call: AuthenticationInfo
public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException { AuthenticationInfo info;//actual try { info = authenticate(token);//Further call
-
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
-
After clicking, spring MVC processes the forwarding
-
About shiro licensing
- 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; }
-
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 }
-
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 } }
-
Call target method
public Object invoke(MethodInvocation methodInvocation) throws Throwable { org.apache.shiro.aop.MethodInvocation mi = createMethodInvocation(methodInvocation); return super.invoke(mi);//Target method }
-
Further call: Method in DegatingSubject:
public void checkPermission(String permission) throws AuthorizationException { assertAuthzCheckPossible(); securityManager.checkPermission(getPrincipals(), permission);//Authority verification part }
-
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); } }
-
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; }
-
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; }
-
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