授权:也叫访问控制,即在应用中控制谁能访问哪些资源(如访问页面/编辑数据/页面操作等)。
shiro中,主要有主体(Subject)、资源(Resource)、权限(Permission)、 角色(Role)。
一、shiro中的授权方式
(1)编程式
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”))
{ //有权限 }
else
{ //无权限 }
(2)注解式
@RequiresRoles("admin")
public void hello()
{ //有权限 }
(3)标签式
<shiro:hasRole name="admin">
<!— 有权限—>
</shiro:hasRole>
二、shiro中授权的注解
(1)@RequiresAuthentication
要求当前Subject已经在session中验证通过(验证当前用户是否登录:subject.isAuthenticated() 结果为true)
(2)@RequiresUser
验证用户是否被记忆,user有两种含义:
1) 一种是成功登录的(subject.isAuthenticated() 结果为true);
2) 另外一种是被记忆的(subject.isRemembered()结果为true)。
(3)@RequiresGuest
用户没有登录认证或被记住过,验证是否是一个guest的请求,与@RequiresUser完全相反。换言之,RequiresUser == !RequiresGuest。此时subject.getPrincipal() 结果为null.
(4)@RequiresPermissions
验证用户是否具有一个或多个权限,该注解将会经常在项目中使用,如果不满足条件则抛出AuthorizationException异常
1) 是否具有某一权限@RequiresPermission("account:create")
2) 是否具有多个权限@RequiresPermission({"account:create","account:update"})
(5)@RequiresRoles
验证当前用户是否具有某角色,与验证权限类似
三、授权原理
1、首先会获取用户输入的账户/密码等
2、调用Subject.isPermitted*/hasRole*接口,其会委托给SecurityManager,而 SecurityManager接着会委托给Authorizer
3、Authorizer是真正的授权者,如果我们调用如isPermitted(“user:view”),其首先会通过PermissionResolver把字符串转换成相应的Permission实例
4、在进行授权之前,其会调用相应的Realm获取Subject相应的角色/权限用于匹配传入的角色/权限
5、Authorizer会判断Realm的角色/权限是否和传入的匹配,如果有多个Realm,会委托给ModularRealmAuthorizer进行循环判断,如果匹配如isPermitted*/hasRole*会返回true,否则返回false表示授权失败
四、AuthorizationInfo模型体系
AuthorizationInfo是授权信息的数据模型。
public interface AuthorizationInfo extends Serializable {
Collection<String> getRoles(); //获取角色字符串信息
Collection<String> getStringPermissions(); //获取权限字符串信息
Collection<Permission> getObjectPermissions(); //获取Permission对象信息
}
具体实现类分析:
(1)SimpleAuthorizationInfo
AuthorizationInfo
的简单实现,具体来说其内部使用Set
存储角色名称、权限字符串、权限;
protected Set<String> roles;
protected Set<String> stringPermissions;
protected Set<Permission> objectPermissions;
(2)Account
同时继承AuthenticationInfo
和AuthorizationInfo
接口;表示单个Realm
中单个账户的鉴权和授权;
(3)SimpleAccount
Account接口的具体实现。比较简单,直接上源码:
// 鉴权信息,实现AuthenticationInfo接口
private SimpleAuthenticationInfo authcInfo;
// 授权信息,实现AuthorizationInfo接口
private SimpleAuthorizationInfo authzInfo;
// 账户锁定标识
private boolean locked;
// 账户过期标识
private boolean credentialsExpired;
/**
* 构造方法
*/
public SimpleAccount(PrincipalCollection principals, Object credentials) {
// 鉴权信息
this.authcInfo = new SimpleAuthenticationInfo(principals, credentials);
// 授权信息
this.authzInfo = new SimpleAuthorizationInfo();
}
/**
* 获取锁定标识
*/
public boolean isLocked() {
return locked;
}
/**
* 设置锁定标识
*/
public void setLocked(boolean locked) {
this.locked = locked;
}
/**
* 获取过期标识
*/
public boolean isCredentialsExpired() {
return credentialsExpired;
}
/**
* 设置过期标识
*/
public void setCredentialsExpired(boolean credentialsExpired) {
this.credentialsExpired = credentialsExpired;
}
public void merge(AuthenticationInfo info) {
// 合并鉴权信息
authcInfo.merge(info);
// Merge SimpleAccount specific info
if (info instanceof SimpleAccount) {
// 合并SimpleAccount的锁定和过期标识
SimpleAccount otherAccount = (SimpleAccount) info;
if (otherAccount.isLocked()) {
setLocked(true);
}
if (otherAccount.isCredentialsExpired()) {
setCredentialsExpired(true);
}
}
}
五、Realm实现:doGetAuthorizationInfo方法
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
log.debug("权限配置");
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
User user = UserUtil.getCurrentUser();
//获取当前用户对应的角色ids
List<Role> roles = SpringUtil.getBean(RoleDao.class).listByUserId(user.getId());
//获取当前用户对应的角色names
Set<String> roleNames = roles.stream().map(Role::getName).collect(Collectors.toSet());
//一、authorizationInfo设置角色
authorizationInfo.setRoles(roleNames);
//获取当前用户对应的权限permissions
List<Permission> permissionList = SpringUtil.getBean(PermissionDao.class).listByUserId(user.getId());
//为当前用户设置权限,放到session中
UserUtil.setPermissionSession(permissionList);
Set<String> permissions = permissionList.stream().filter(p -> !StringUtils.isEmpty(p.getPermission()))
.map(Permission::getPermission).collect(Collectors.toSet());
//二、authorizationInfo设置权限
authorizationInfo.setStringPermissions(permissions);
return authorizationInfo;
}