权限
系统中的权限一般是通过用户、角色、权限来管理。每个用户可以对应多个角色;每个角色对应多个权限;每个权限标识一个资源。这样系统中的每个资源都可以通过这种方式来控制,一般情况下一个接口代表一个资源
授权方式
shiro有三种授权方式:
编程式
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole("admin")) {
//有权限
} else {
//无权限
}
注解式
@RequiresRoles("admin")
public void test() {
//有权限
}
JSP标签
<shiro:hasRole name="admin">
<!— 有权限 —>
</shiro:hasRole>
注解式和JSP标签需要整合web,因此这里先不介绍 ,先介绍shiro编程式
基于一下代码分析授权内部流程:
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//设置realm
securityManager.setRealm(definedRealm);
//绑定securityManager到securityUtils
SecurityUtils.setSecurityManager(securityManager);
//创建登录实体
Subject subject = SecurityUtils.getSubject();
//创建登录token
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan","01d7f40760960e7bd9443513f22ab9af");
//登录
subject.login(token);
System.out.println("是否认证通过 " + subject.isAuthenticated());
System.out.println("是否有角色admin " + subject.hasRole("admin"));
System.out.println("是否有权限permission1 " + subject.isPermitted("permission1"));
第一步:securityManager.setRealm():内部执行逻辑。
其中securityManager的继承结构如下:
当我们创建DefaultSecurityManager时,它的父类均得到实例化。并且最后一步ModularRealmAuthorizer是AuthorizingSecurityManager中的属性,调用setRealms()是实例化ModularRealmAuthorizer中的Collection<Realm>属性。后面调用hasRole方法时会调用该集合中Realm的hasRole方法。
第二步:subject.hashRole()内部源码:
可以看到是调用securityManager.hasRole方法,因为创建的是DefaultSecurityManager,所以调用的是该类的hasRole()方法。查看该类的所有方法(自己去查),发现并没有hasRole(),因此去父类中查找,找到父类AuthorizingSecurityManager的hasRole()方法:
该类的authorizer属性如下:
因此再去ModularRealmAuthorizer中查找hasRole()方法:
发现其实调用的是realms的hasRole()方法。那realm有哪里来的呢?,其实就是第一部中时序图最后一步setRealms()实例化的
第三步:查看realm中的hasRole()方法:
发现是先获取授权信息,在调用重载的hasRole()方法。获取授权信息方法如下:
protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {
if (principals == null) {
return null;
} else {
AuthorizationInfo info = null;
if (log.isTraceEnabled()) {
log.trace("Retrieving AuthorizationInfo for principals [" + principals + "]");
}
Cache<Object, AuthorizationInfo> cache = this.getAvailableAuthorizationCache();
Object key;
if (cache != null) {
if (log.isTraceEnabled()) {
log.trace("Attempting to retrieve the AuthorizationInfo from cache.");
}
key = this.getAuthorizationCacheKey(principals);//先从缓存中查找
info = (AuthorizationInfo)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) {
info = this.doGetAuthorizationInfo(principals);//如果缓存中不存在,则调用自定义的Realm中的doGetAuthorizationInfo()方法
if (info != null && cache != null) {
if (log.isTraceEnabled()) {
log.trace("Caching authorization info for principals: [" + principals + "].");
}
key = this.getAuthorizationCacheKey(principals);
cache.put(key, info);
}
}
return info;
}
}
发现先从缓存中查找,如果缓存中不存在,则调用自定义的Realm中的doGetAuthorizationInfo()方法。
最后调用Realm中的hasRole(),判断授权信息中是否包含该角色
以上是对hasRole()方法的内部分析,其实判断角色和判断权限,在最后都会调用我们Realm中的相应方法。首次调用时会调用我们重写的doGetAuthorizationInfo()方法,如果我们开启缓存,则以后会从缓存红查找。