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存储的授权数据(角色、权限等)。角色被表示为一个字符串集合(集合),通常每个元素都是角色名。
权限以两种方式提供:
- 字符串的集合,其中每个字符串通常可以通过域的PermissionResolver转换为权限对象
- 权限对象的集合
两个权限集合一起表示权限的总聚合集合。根据偏好和需要,可以使用其中一个或两个。
(完)