Shiro源码分析(十一)——鉴权流程

2021SC@SDUSC

一.Demo代码

这是在shiro源码的quickstart目录下有QuickStart.java文件,截取了其中鉴权部分的代码。

		// 测试一个角色:
		// 判断当前用户的角色是否为schwartz
        if (currentUser.hasRole("schwartz")) { // 1
            log.info("May the Schwartz be with you!");
        } else {
            log.info("Hello, mere mortal.");
        }

        // 测试类型化权限(而不是实例级)
        // 判断当前用户是否被允许使用lightsaber执行wield动作
        if (currentUser.isPermitted("lightsaber:wield")) { // 2
            log.info("You may use a lightsaber ring.  Use it wisely.");
        } else {
            log.info("Sorry, lightsaber rings are for schwartz masters only.");
        }

        // 实例级权限:
        // 判断当前用户是否被允许驾驶牌照为eagle5的winnebago
        if (currentUser.isPermitted("winnebago:drive:eagle5")) { // 3
            log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'.  " +
                    "Here are the keys - have fun!");
        } else {
            log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
        }

二.源码分析

1

public class DelegatingSubject implements Subject {
	// 如果Subject具有指定的角色则返回true,否则返回false。
    public boolean hasRole(String roleIdentifier) {
    	// 1.1 hasPrincipals()
    	// 1.2 securityManager.hasRole(getPrincipals(), roleIdentifier)
        return hasPrincipals() && securityManager.hasRole(getPrincipals(), roleIdentifier);
    }
}

1.1

public class DelegatingSubject implements Subject {
	// 判断是否有principal与Subject绑定
    protected boolean hasPrincipals() {
        return !isEmpty(getPrincipals()); // 1.1.1 getPrincipals()
    }
	
	private static boolean isEmpty(PrincipalCollection pc) {
		// 如果该集合为空则返回true,否则返回false。
        return pc == null || pc.isEmpty();
    }
}

1.1.1

public class DelegatingSubject implements Subject {
    public PrincipalCollection getPrincipals() {
        List<PrincipalCollection> runAsPrincipals = getRunAsPrincipalsStack();
        return CollectionUtils.isEmpty(runAsPrincipals) ? this.principals : runAsPrincipals.get(0);
    }
    
    private List<PrincipalCollection> getRunAsPrincipalsStack() {
    	// 获取当前session(若获取不到,不创建新的)
        Session session = getSession(false);
        if (session != null) {
        	// 获取session中RUN_AS_PRINCIPALS_SESSION_KEY对应的属性
            return (List<PrincipalCollection>) session.getAttribute(RUN_AS_PRINCIPALS_SESSION_KEY);
        }
        return null;
    }
}

1.2

// ModularRealmAuthorizer是一个授权器实现,它在授权操作期间咨询一个或多个配置好的realm。
public class ModularRealmAuthorizer implements Authorizer, PermissionResolverAware, RolePermissionResolverAware {

	// 如果对应的Subject/user具有指定的角色则返回true,否则返回false
    public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {
    	// 由Authorizer实现方法使用,以确保领域已经设置。默认实现确保属性不为空。
        assertRealmsConfigured(); // 1.2.1
        for (Realm realm : getRealms()) { // 1.2.2
            if (!(realm instanceof Authorizer)) continue;
            if (((Authorizer) realm).hasRole(principals, roleIdentifier)) { // 1.2.3
                return true;
            }
        }
        return false;
    }
}

1.2.1

public class ModularRealmAuthorizer implements Authorizer, PermissionResolverAware, RolePermissionResolverAware {
	// 由Authorizer实现方法使用,以确保Realm已经设置。
	// 默认实现确保属性不为空。
    protected void assertRealmsConfigured() throws IllegalStateException {
        Collection<Realm> realms = getRealms(); // 1.2.2
        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);
        }
    }
}

1.2.2

public class ModularRealmAuthorizer implements Authorizer, PermissionResolverAware, RolePermissionResolverAware {
	// 返回由此授权器包装的Realm,这些Realm在授权检查期间被咨询。
    public Collection<Realm> getRealms() {
        return this.realms;
    }
}

1.2.3

// Shiro支持SecurityManager类层次结构,将所有授权(访问控制)操作委托给包装好的Authorizer实例。
// 也就是说,这个类实现了SecurityManager接口中的所有Authorizer方法,但实际上,这些方法仅仅是对底层“真正的”Authorizer实例的传递调用。
// 所有其他SecurityManager方法没有被这个类或它的父类覆盖(主要是Session支持),剩下的由子类来实现。
// 为了与这个层次结构中的其他类保持一致,以及Shiro尽可能最小化配置的愿望,将在实例化时为所有依赖项创建合适的默认实例。
public abstract class AuthorizingSecurityManager extends AuthenticatingSecurityManager {
    public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {
        return this.authorizer.hasRole(principals, roleIdentifier);
    }
}

// 通过添加授权(访问控制)支持,AuthorizingRealm扩展了AuthenticatingRealm的功能。
// 只要getAuthorizationInfo(PrincipalCollection)方法返回AuthorizationInfo,此实现将自动执行所有角色和权限检查(子类不必编写此逻辑)。
// 如果不想使用AuthorizationInfo构造,可以直接子类化AuthenticatingRealm,并直接实现其余的Realm接口方法。
// 如果希望更好地控制特定数据源的角色和权限检查的发生方式,则可以这样做。
// 然而,使用AuthorizationInfo(及其默认实现SimpleAuthorizationInfo)在绝大多数Realm情况下就足够了。
public abstract class AuthorizingRealm extends AuthenticatingRealm
        implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware {
    
    public boolean hasRole(PrincipalCollection principal, String roleIdentifier) {
        AuthorizationInfo info = getAuthorizationInfo(principal);
        return hasRole(roleIdentifier, info);
    }

	// 返回指定主体的特定于授权的帐户信息,如果找不到帐户,则为空。
	// 这个类中的其他方法实现使用产生的AuthorizationInfo对象自动执行相应Subject的访问控制检查。
	// 这个实现从doGetAuthorizationInfo的子类实现中获取实际的AuthorizationInfo对象,然后如果启用了缓存,就对其进行缓存以便有效地重用。
	// 这个方法的调用应该被认为是与获取authenticationInfo完全正交的,因为两者都可以以任何顺序发生。
	// 例如,在“Remember Me”场景中,用户身份会被记住(并假定)到他们的当前会话中,并且该会话期间的身份验证尝试可能永远不会发生。
	// 但是因为它们的标识将被记住,所以这就足够调用这个方法来执行任何必要的授权检查了。
	// 因此,身份验证和授权应该是松散耦合的,彼此不依赖。
	protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {

        if (principals == null) {
            return null;
        }

        AuthorizationInfo info = null;

        if (log.isTraceEnabled()) {
            log.trace("Retrieving AuthorizationInfo for principals [" + principals + "]");
        }

        Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache();
        if (cache != null) {
            if (log.isTraceEnabled()) {
                log.trace("Attempting to retrieve the AuthorizationInfo from cache.");
            }
            Object key = getAuthorizationCacheKey(principals);
            info = cache.get(key);
            if (log.isTraceEnabled()) {
                if (info == null) {
                    log.trace("No AuthorizationInfo found in cache for principals [" + principals + "]");
                } else {
                    log.trace("AuthorizationInfo found in cache for principals [" + principals + "]");
                }
            }
        }


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

        return info;
    }
}

AuthorizationInfo表示仅在授权(访问控制)检查期间使用的单个Subject存储的授权数据(角色、权限等)。角色被表示为一个字符串集合(集合),通常每个元素都是角色名。
权限以两种方式提供:

  1. 字符串的集合,其中每个字符串通常可以通过域的PermissionResolver转换为权限对象
  2. 权限对象的集合
    两个权限集合一起表示权限的总聚合集合。根据偏好和需要,可以使用其中一个或两个。
    (完)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值