Shiro 授权流程详解
1. 授权
授权即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的。
2. 关键对象
授权可简单理解为 who 对 what(which)进行 How 操作;
Who,即主体(Subject),主体需要访问系统中的资源;
What,即资源(Resource),如系统菜单、页面、按钮、类方法、系统商品信息等。资源包括资源类型和资源实例,比如商品信息为资源类型,类型为 t01 的商品为资源实例,编号 001 的商品也属于资源实例;
How,权限 / 许可(Permission),规定了主体对资源的操作许可, 权限离开资源没有意义,如用户查询权限、用户添加权限、某个类方法的调用权限、编号为 001 用户的修改权限等,通过权限可知主体对哪些资源都有哪些操作许可。
3. 授权流程
4. 授权方式
- 基于角色的访问控制【RBAC 基于角色的访问控制(Role-Based Access Control)是以角色为中心进行访问控制】
if(subject.hasRole("admin")){ //操作什么资源 }
- 基于资源的访问控制【RBAC 基于资源的访问控制(Resource-Based Access Control)是以资源为中心进行访问控制】
if(subject.isPermission("user:update:01")){ //资源实例 //对01用户进行修改 } if(subject.isPermission("user:update:*")){ //资源类型 //对01用户进行修改 }
5. 权限字符串
权限字符串的规则是【资源标识符:操作:资源实例标识符】意思是对哪个资源的哪个实例具有什么操作,“:”是资源/操作/实例的分割符,权限字符串也可以使用 * 通配符。
例子:
-
用户创建权限:user : create,或 user : create : *
-
用户修改实例001的权限:user : update : 001
-
用户实例001的所有权限:user : *:001
6. 开发授权代码
a 自定义 Realm 代码实现
public class CustomerMD5Realm extends AuthorizingRealm { /** * 一个主题可以有多个身份,参数是集合 principals;但是只有一个主身份; * @param principals * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // 获取身份信息 String principal = (String) principals.getPrimaryPrincipal(); System.out.println("身份信息 = " + principal); // 根据身份信息 用户名 获取当前用户的角色信息,以及权限信息【比如:从数据库中获取】 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); // 将数据库中查询角色信息赋值给权限对象 simpleAuthorizationInfo.addRoles(Arrays.asList("admin", "user")); simpleAuthorizationInfo.addStringPermission("user:*:01"); simpleAuthorizationInfo.addStringPermission("product:*:*"); return simpleAuthorizationInfo; } // 认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String principal = (String) token.getPrincipal(); System.out.println("principal = " + principal); return null; } }
b 授权处理【会自动调用自定义 CustomerMD5Realm 的 doGetAuthorizationInfo 获取指定用户的权限信息】
public class TestCustomerMD5Authenticator { public static void main(String[] args) { // 1. 创建安全管理器 DefaultSecurityManager securityManager = new DefaultSecurityManager(); // 2. 给安全管理器设置 realm 【Realm 需要获取用户认证的数据源信息】 CustomerMD5Realm customerMD5Realm = new CustomerMD5Realm(); // 设置 realm 使用的 hash 凭证匹配器 HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); credentialsMatcher.setHashAlgorithmName("md5"); // 设置使用的 Hash 算法 credentialsMatcher.setHashIterations(1024); // 需要散列多少次 customerMD5Realm.setCredentialsMatcher(credentialsMatcher); securityManager.setRealm(customerMD5Realm); // 3. SecurityUtils 设置安全管理器 SecurityUtils.setSecurityManager(securityManager); // 4. 获取关键对象 Subject 主体 Subject subject = SecurityUtils.getSubject(); // 5. 创建令牌 UsernamePasswordToken token = new UsernamePasswordToken("xiaochen", "123"); // 用户认证 try { System.out.println("认证状态: " + subject.isAuthenticated()); subject.login(token); System.out.println("认证状态: " + subject.isAuthenticated()); } catch (UnknownAccountException e) { e.printStackTrace(); System.out.println("认证失败: 用户名不存在~"); } catch (IncorrectCredentialsException e) { e.printStackTrace(); System.out.println("认证失败: 密码错误~"); } // 授权 if (subject.isAuthenticated()) { //基于角色权限控制 System.out.println(subject.hasRole("super")); // 走授权流程 // 基于多角色权限控制 System.out.println(subject.hasAllRoles(Arrays.asList("admin", "user"))); // 走授权流程 // 是否具有其中一个角色 boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "user", "super")); // 走授权流程 for (boolean aBoolean : booleans) { System.out.println(aBoolean); } // 基于权限字符串的访问控制 资源标识符:操作:资源类型 System.out.println(subject.isPermitted("user:update:01")); // 走授权流程 // 分别具有哪些权限 boolean[] permitted = subject.isPermitted("user:*:01", "order:*:02"); // 走授权流程 for (boolean b : permitted) { System.out.println(b); } } } }
注意:深入了解 Shiro 的授权流程,可以 debug 运行程序,查看源代码进行学习深究!