授权,也称访问控制,在应用中控制谁能访问哪些资源(如访问页面、编辑数据、等)。在授权中需了解的几个关键词:主体(subject)、资源(resource)、权限(permission)、角色(role)。
主体
主体,访问应用的用户,在 Shiro 中使用 Subject 代表该用户。用户只有授权后才允许访问相应的资源。
资源
在应用中用户可以访问的任何东西,比如访问 JSP 页面、CRUD数据、访问接口、打印等等都是资源。用户只有通过授权后才能访问。
权限
通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如: 访问用户列表页面、CRUD数据、打印文档等。
角色
角色代表了用户操作权限集合。一般情况下我们会赋予用户角色而不是权限,即这样用户可以拥有多个权限。如:项目经理、技术总监、部门经理、主管等都是角色,不同的角色拥有多个不同的权限。
隐式角色:
直接通过角色来验证用户有没有操作权限,在实际应用中技术总监、主管可以使用打印机,假设某天不允许主管使用打印机,此时需要从应用中删除相应逻辑代码。粒度较粗。
显示角色:
在程序中通过权限控制谁能访问某个资源,即角色拥有多个权限,这样假设哪个角色不能访问某个资源,只需要从角色拥有权限中移除即可。无须修改多处代码,即粒度是以资源-实例为单位的;粒度较细。
2、 授权方式Shiro 支持三种方式的授权。
1)编程式:通过写 if/else 授权代码块完成。
//1. 从 PrincipalCollection 中来获取登录用户的信息
Object principal = arg0.getPrimaryPrincipal();
//2. 利用登录的用户的信息来获取当前用户的角色或权限
Set<String> roles = new HashSet<>();
roles.add("user");
if("admin".equals(principal)){
roles.add("admin");
}
/
2)注解式:通过在执行的方法上注解完成。
@RequiresRoles("user")
public void user() {
//有权限
}
3)JSP标签:在 JSP页面通过相应的标签完成。(jsp已过时了,现在是前后端分离)
<shiro:hasRole name="user">
<!— 有权限 —>
</shiro:hasRole>
3、 授权1) 基于角色的访问控制。
在 ini 配置文件配置用户拥有的角色,格式:用户名=密码,角色1,角色2。
[users]
zhang=123456,role1,role2
wang=123456,role1
Shiro 提供了 hasRole/hasAllRoles用于判断用户是否拥有某个角色。
// 1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
factory<org.apache.shiro.mgt.SecurityManager> factory =
new IniSecurityManagerFactory("classpath:shiro.ini");
// 2、得到SecurityManager实例 并绑定给SecurityUtils
org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
// 3、得到Subject及创建用户名/密码身份验证Token
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123456");
try {
// 4、登录,身份验证
subject.login(token);
} catch (AuthenticationException e) {
// 5、身份验证失败
logger.info("用户名 、密码错误");
}
if(subject.hasRole("role1")){
logger.info(token.getUsername() " has role1");
}else{
logger.info(token.getUsername() " hasn't role1");
}
2) 基于资源(权限)的访问控制
在 ini 配置文件配置用户拥有的角色及角色-权限关系,格式:"角色=权限 1,权限 2"。
首先根据用户名找到角色,然后根据角色再找到权限,即角色是权限的组合。
[users]
zhang=123456,role1,role2
wang=123456,role1
[roles]
role1=user:create,user:update
role2=user:create,user:delete
Shiro 提供了 isPermitted 和 isPermittedAll 用于判断用户是否拥有某个权限或所有权限。
if(subject.isPermitted("user:create")){
logger.info(token.getUsername() " has user:create");
}else{
logger.info(token.getUsername() " hasn't user:create");
}
4、 授权流程
1)首先调用 subject.isPermitted*/hasRole*接口,其会委托给 SecurityManager,而 SecurityManager 接着会委托给 Authorizer。
2)Authorizer 是真正的授权者,如果我们调用如 isPermitted("user:view"),其首先会通过 PermissionResolver 把字符串转换成相应的 Permission 实例;
在进行授权之前,其会调用相应的 Realm 获取 Subject 相应的角色、权限用于匹配传入的角色、权限;
3) Authorizer 会判断 Realm 的角色、权限是否和传入的匹配,如果有多个 Realm,会委托给 ModularRealmAuthorizer 进行循环判断,如果匹配如 isPermitted*/hasRole* 会返回 true,否则返回 false 表示授权失败。
ModularRealmAuthorizer 进行多 Realm 匹配流程
1)首先检查相应的 Realm 是否实现了实现了 Authorizer;
2)如果实现了 Authorizer,那么接着调用其相应的 isPermitted*/hasRole* 接口进行匹配;
3)如果有一个 Realm 匹配那么将返回 true,否则返回 false。
5、Realm 授权1、自定义类JdbcRealm继续父AuthorizingRealm,重写doGetAuthorizationInfo授权方法,根据授权的用户在数据库中查询所拥有的角色、权限。
2、 当我们调用hasRole("普通员工")方法时,Authorizer 会调用doGetAuthorizationInfo()方法进行授权,判断 Realm 的角色、权限是否和传入的匹配,如果匹配,hasRole("普通员工")返回true,否则返回false。
3、 测试结果
,