前面:
正如之前写的认证的过程,说授权跟认证很相似,没有错,只是授权需要手动开启,Shiro才回去进行权限的查询和对比.
流程图:
- 如上图,需要手动开始认证,通过.hasRole(),或者isPermitted(),分别判断用户是否具有这个角色,这个权限.
注意:权限里面的字符不是"角色"+"权限",而仅仅是权限表里面存储的字段
。可以通过三种方法进行开启:(1)编程式,直接在在代码中调用Subject的hasRole(),和isPermitted()方法. (2)注解式,在方法或者类的头上添加注解。(3)JSP的标签 - (1)正如上面图片,调用hasrole(),交给securitymanager处理.
//实现subject接口
public class DelegatingSubject implements Subject {
//交给securitymanager进行处理
protected transient SecurityManager securityManager;
//代码或者注释调用这个方法,getPrincipals()是获取身份集合
public boolean hasRole(String roleIdentifier) {
return hasPrincipals() && securityManager.hasRole(getPrincipals(), roleIdentifier);
}
}
- (2) manager调用自己的hasrole(),这是注意:securitymanager也是一个接口,所以这时候还是有一个类实现了这个接口并去调用hasrole()方法.
//autenticatingSecurityManager的父类的父类的父类CachingSecurityManager实现了securityManager接口
public abstract class AuthorizingSecurityManager extends AuthenticatingSecurityManager {
//通过这个授权器来调用hasRole()方法
private Authorizer authorizer;
//这个就是代码一中所调用的hasRole()方法
public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {
return this.authorizer.hasRole(principals, roleIdentifier);
}
}
- 通过默认的authorizer的实例对象ModularRealmAuthorizer来调用hasRole().
public class ModularRealmAuthorizer implements Authorizer, PermissionResolverAware, RolePermissionResolverAware {
//在此处开始进行处理
public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {
//校验是否存在Realm对象,没有的话就抛出异常
assertRealmsConfigured();
for (Realm realm : getRealms()) {
if (!(realm instanceof Authorizer)) continue;
if (((Authorizer) realm).hasRole(principals, roleIdentifier)) {
return true;
}
}
return false;
}
}
- 如果在securityManager中配置的realm实现了authorizer接口,会使用这个Realm来调用hasRole().
@Bean("securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(SysUserRealm userRealm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
// 使用自定义Realm,同时Shiro里面只承认这一个Realm
defaultWebSecurityManager.setRealm(userRealm);
// 关闭Shiro自带的session
....
return defaultWebSecurityManager;
}
- 下面是自己定义的realm
//虽然它没有实现authorizer接口,但是他的父类实现了这个接口
//所以虽然调用的是父类里面的方法,但是实例对象是这个SysUserRealm 的对象
public class SysUserRealm extends AuthorizingRealm {
}
- 这个就是SysUserRealm 所调用的hasRole()方法.
//可以看到实现了Authorizer接口
public abstract class AuthorizingRealm extends AuthenticatingRealm
implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware {
//通过参数可以看到这个就是调用的方法
public boolean hasRole(PrincipalCollection principal, String roleIdentifier) {
//首先获取到AuthorizationInfo
AuthorizationInfo info = getAuthorizationInfo(principal);
//看这个参数可以知道它调用的是下面的hasRole()方法
return hasRole(roleIdentifier, info);
}
//这个方法是在获取到AuthorizationInfo 之后,所有的准备工作完成之后,开始进行的角色匹配
//也就是真正的判断时候具有这个角色的核心方法
protected boolean hasRole(String roleIdentifier, AuthorizationInfo info) {
return info != null && info.getRoles() != null && info.getRoles().contains(roleIdentifier);
}
}
- 下面是获取AuthorizationInfo 的方法,同时也是AuthorizingRealm里面的方法.
protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {
....
AuthorizationInfo info = null;
....
//获取AuthorizationCatch,这个catch里面存储的是info集合,可以通过key来获取对于的info
Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache();
if (cache != null) {
....
//这个是获取catch中存储信息的key,具体规则看你是自定义了catch还是默认catch
Object key = getAuthorizationCacheKey(principals);
//通过key获取到AuthorizationInfo信息,如果还从没有存储过,则为空
info = cache.get(key);
....
}
//如果没找到对应catch中的信息,就重新查数据库
if (info == null) {
// 可以看到这个方法是我们自定义的Realm里面的方法,并且现在的实例对象也是我们自定义的Realm
info = doGetAuthorizationInfo(principals);
// 将info存储在catch中,下次直接查询使用
if (info != null && cache != null) {
....
Object key = getAuthorizationCacheKey(principals);
cache.put(key, info);
}
}
return info;
}
- 下面是使用自定义的Realm里面的doGetAuthorizationInfo()方法获取info信息
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
String account = JwtUtil.getClaim(principalCollection.toString(), Constant.ACCOUNT);
SysUserVo sysUserVo = new SysUserVo();
sysUserVo.setPhone(account);
// 查询用户角色
List<SysRole> roleDtos = sysUserMapper.findRoleByUser(sysUserVo);
System.out.println(roleDtos.toString());
for (SysRole roleDto : roleDtos) {
if (roleDto != null) {
// 添加角色
simpleAuthorizationInfo.addRole(roleDto.getRoleName());
// 根据用户角色查询权限
List<SysMenuVo> sysMenuVos = SysRoleMapper.selMenuByRole(roleDto.getId());
System.out.println(sysMenuVos.toString());
for (SysMenuVo sysMenuVo : sysMenuVos) {
if (sysMenuVo != null) {
// 添加权限
System.out.println(sysMenuVo.getPermission());
simpleAuthorizationInfo.addStringPermission(sysMenuVo.getPermission());
}
}
}
}
return simpleAuthorizationInfo;
}
- 获取到info就开始进行角色匹配
//这个方法是在获取到AuthorizationInfo 之后,所有的准备工作完成之后,开始进行的角色匹配
//也就是真正的判断时候具有这个角色的核心方法
protected boolean hasRole(String roleIdentifier, AuthorizationInfo info) {
//如果匹配成功就返回True
return info != null && info.getRoles() != null && info.getRoles().contains(roleIdentifier);
}
到此核心代码已经结束,存在角色就代表授权成功,返回true,一次返回上一次,访问继续进行.