Shiro的授权
1. 授权
授权,即访问控制。主体通过身份认证之后,需要分配权限。
2. 关键对象
- Who
- 主体,即主体需要访问系统中的资源
- What
- 即资源,如系统菜单、页面、按钮、类方法、系统商品信息等。资源包括资源类型和资源实例,比如商品信息为资源类型,类型为t01的商品为资源实例,编号为001的商品信息也属于资源实例。
- How
- 权限/许可,规定主体对资源的操作访问,权限离开资源乜有意义,如用户查询权限,用户添加权限。通过权限可以知道主体对哪些资源有哪些操作许可。
3. 授权流程
4. 授权方式
-
基于角色的访问控制
-
RBAC基于角色的访问控制(Role-Based Access Control)以角色为中心进行访问控制
-
举例:
if (subject.hasRole("admin")){ // 操作什么资源 }
-
-
基于资源的访问控制
- RBAC基于资源的访问控制(Resource-Based Access Control)以资源为中心进行访问控制
- 举例:
if (subject.isPerssion("user:update:01")){ // 对01用户进行修改 }
-
权限字符串
- 格式:资源标识符:操作:资源实例标识符
- 标识对哪个资源的哪个实例具有什么操作
- *为通配符
- 举例
- 用户创建权限: user:create, 或者 user:create:*
- 用户修改实例001的权限: user:update:001
- 用户筛查001的所有权限: user:*:001
5. 授权编码实现方式
-
编程实现
Subject subject = SecurityUtils.getSubject(); if (subject.hasRole("admin")){ // 有权限,进行相应的操作 }
-
注解实现
@RequiresRoles("admin") public void hello(){ // 有权限,进行相应的操作 }
-
标签实现
JSP/GSP 标签:在JSP/GSP页面通过相应的标签完成 <shiro:hasRole name="admin"> <!- 有权限 -> </shiro:hasRole> 注意:Thymeleaf 中使用shiro需要额外集成
6. 代码实现
自定义Realm类
/**
* @Author: Hjx
* @Date: 2021/8/9 18:03
*/
public class CustomerMd5Realm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
/*
PrincipalCollection 身份的集合
一个主体可以有多个身份,但是只有一个主身份
*/
// 获取主体的主身份
String principal = (String) principals.getPrimaryPrincipal();
System.out.println("主身份信息"+principal);
// 模拟根据用户的身份信息 从数据库中查询其角色信息
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addRole("admin");
simpleAuthorizationInfo.addRole("user");
// 模拟从数据库查询权限信息,赋值给某个对象
simpleAuthorizationInfo.addStringPermission("user:*:01");
simpleAuthorizationInfo.addStringPermission("product:create:01");
return simpleAuthorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 从 token 中获取用户输入的身份凭证
String userName = (String) token.getPrincipal();
// 模拟从数据库查询出的用户名和密码
String name = "hjx";
String pass = "1a0f1aac48cf23ffb080673fd60b5d2d";
String credentialsSalt = "hjx";
if (name.equals(userName)){
/*
SimpleAuthenticationInfo 是 AuthenticationInfo 的实现类
参数1:用户名
参数2:md5加密后的密码
参数3:盐值,类型为ByteSource,需要进行格式转换
参数4:自定义realm的名称
*/
return new SimpleAuthenticationInfo(name, pass, ByteSource.Util.bytes(credentialsSalt), this.getName());
}
return null;
}
}
Test测试类
/**
* @Author: Hjx
* @Date: 2021/8/9 17:57
*/
public class TestMd5Authenticator {
public static void main(String[] args) {
// 创建SecurityManager安全管理器
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
// 给SecurityManager设置自定义Realm
CustomerMd5Realm customerMd5Realm = new CustomerMd5Realm();
// 设置md5 凭证适配器
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// 设置加密方式
hashedCredentialsMatcher.setHashAlgorithmName("md5");
// 设置散列次数
hashedCredentialsMatcher.setHashIterations(1024);
customerMd5Realm.setCredentialsMatcher(hashedCredentialsMatcher);
defaultSecurityManager.setRealm(customerMd5Realm);
// 给安全工具类SecurityUtils 设置 SecurityManager
SecurityUtils.setSecurityManager(defaultSecurityManager);
// 通过安全工具类SecurityUtils获取 主体subject
Subject subject = SecurityUtils.getSubject();
// 创建Token,模拟接收前端用户输入的用户名和密码
UsernamePasswordToken token = new UsernamePasswordToken("hjx", "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("密码错误");
}catch (Exception e){
e.printStackTrace();
}
// 对通过身份认证的是用户进行授权管理
if (subject.isAuthenticated()){
// 基于角色的访问控制
if (subject.hasRole("admin")){
System.out.println("该用户拥有 admin 的权限");
}else {
System.out.println("该用户没有 admin 的权限");
}
// 基于多角色的访问控制
if(subject.hasAllRoles(Arrays.asList("admin","user"))){
System.out.println("该用户拥有 admin user 的权限");
}
// 具有其中一个角色
boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "user", "super"));
for (boolean bool:booleans){
System.out.println(bool);
}
System.out.println("================================================");
// 基于全新字符串的权限控制
System.out.println(subject.isPermitted("user:*:01"));
// 分别具有哪些权限
boolean[] booleans1 = subject.isPermitted("user:*:01", "product:update:01");
for (boolean bool:booleans1){
System.out.println(bool);
}
// 同时具有哪些权限
System.out.println(subject.isPermittedAll("user:*:01", "product:*"));
}
}
}
代码及资料地址:hjx: 知道的越多,越发觉自己的无知 (gitee.com)
视频学习参考:【编程不良人】2020最新版Shiro教程,整合SpringBoot项目实战教程_哔哩哔哩_bilibili