The most detailed code flow analysis for Shiro permission verification

Shiro permission verification

1. Test class


first call

subject().isPermitted("user1:update");

subject will delegate to SecurityManager

The SecurityManager will then delegate to the Authorizer;

Authorizer is the real authorizer

Authorizer is an interface, and will then call the isPermitted method of its implementation class (ModularRealmAuthorizer) for authentication

It can be seen that as long as there is a permission verification pass, it will return true

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;
    }
in

The source code of the assertRealmsConfigured method is: its main function is to determine whether the realm is empty (this method is pretty good, learn it)

protected void assertRealmsConfigured() throws IllegalStateException {
        Collection<Realm> realms = getRealms();
        if (realms == null || realms.isEmpty()) {
            String msg = "Configuration error:  No realms have been configured!  One or more realms must be " +
                    "present to execute an authorization operation.";
            throw new IllegalStateException(msg);
        }
    }

At this point, which isPermitted(principals, permission) method above calls the method isPermitted(principals, permission) of its implementation interface (Authorizer)

And then call the method of its implementation class (AuthorizingRealm)

First, parse the incoming permissions to be verified.

Use developer specified resolver BitAndWildPermissionResolver 

public boolean isPermitted(PrincipalCollection principals, String permission) {
        Permission p = getPermissionResolver().resolvePermission(permission);
        return isPermitted (principals, p);
    }

Use a custom permission parser to convert string permissions to permissions of type Permission

public class BitAndWildPermissionResolver implements PermissionResolver {

    public Permission resolvePermission(String permissionString) {
        / / Start with the + sign to go to the custom permission parser
        if(permissionString.startsWith("+")){
            return new BitPermission(permissionString);
        }
        //The rest go shiro, the default wildcard permission parser
        return new WildcardPermission(permissionString);
    }

}


Then compare the incoming permissions :

public boolean isPermitted(PrincipalCollection principals, Permission permission) {
        AuthorizationInfo info = getAuthorizationInfo(principals);
        return isPermitted (permission, info);
    }
This method works:

    (0) First get the permission information from the cache, if not, go to realm to get it

Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache();

    (1) First obtain the permission set from the custom MyRealm class (original, unparsed, including string, wild, bit various types of initial permission parameters)

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

        authorizationInfo.addRole("role1");
        authorizationInfo.addRole("role2");
        authorizationInfo.addObjectPermission(new BitPermission("+user1+10"));
        authorizationInfo.addObjectPermission(new WildcardPermission("user1:*"));
        authorizationInfo.addStringPermission("+user2+10");
        authorizationInfo.addStringPermission("user2:*");

        return authorizationInfo;
    }


Note: Questions about caching

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);
            }
        }

Here is the default cache implementation. If you configure the cache in shiro, the current permission information will be cached in the cache.

   

  (2) Actual comparison of permissions (parsing permission strings)

return isPermitted (permission, info);
The critical moment has come:

protected boolean isPermitted(Permission permission, AuthorizationInfo info) {
       Collection<Permission> perms = getPermissions(info); 
        if (perms != null && !perms.isEmpty()) {
            for (Permission perm : perms) {
                if (perm.implies(permission)) {
                    return true;
                }
            }
        }
        return false;
    }

Among them, the place with blue characters on a yellow background will convert the permission information (good and bad) just obtained from the custom realm into a unified Permission object for comparison

The final parsed permissions are as follows (including directly granted permissions, and permissions owned by those who have roles)



Preparations complete ( permissions to be verified, which the user actually has )

Can start comparing posessPermissions

Collection<Permission> possessPermissions= getPermissions(info);
        if (possessPermissions!= null && !possessPermissions.isEmpty()) {
            for (Permission perm : possessPermissions) {
                if (perm.implies(wantTocheckPermissions)) {
                    return true;
                }
            }
        }
        return false;
true if one returns true





Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325770144&siteId=291194637