Shiro学习之权限认证

时间:2022-08-27 15:17:29

权限认证也就是访问控制,即在应用中控制谁能访问哪些资源.
在权限认证中,最核心的是三个要素是:权限,角色和用户.
权限,即操作资源的权力,比如访问某个页面,以及对某个模块的数据的添加,修改,删除,查看的权利(CRUD).
角色,是权限的集合,一个角色可以包含多种权限
用户,在shiro中代表访问系统的用户,即subject


授权

  • 编程式授权,基于角色和权限的访问控制
  • 注解授权,jsp标签授权

1.基于角色的访问控制
配置shiro_role.ini文件

[users]
yyt=123,role1,role2
jack=1234,role1

规则即:“用户名=密码,角色1,角色2”,如果需要在应用中判断用户是否有相应角色,就需要在相应的Realm中返回角色信息,也就是说Shiro不负责维护用户-角色信息,需要应用提供,Shiro只是提供相应的接口方便验证,后续会介绍如何动态的获取用户角色。

首先我将重复的代码封装成一个方法,传入配置文件的名字,用户名,密码

package com.kingsky;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;

public class ShiroUtil {
public static Subject login(String configPath, String userName,
String passWord) {
// 读取配置文件,初始化SecurityManager工厂
Factory<SecurityManager> factory = new IniSecurityManagerFactory(
"classpath:"+configPath);

// 获取到SecurityManager实例
SecurityManager securityManager = factory.getInstance();

// 把SecurityManager绑定到SecurityUtils中

SecurityUtils.setSecurityManager(securityManager);
// 得到当前用户
Subject subject = SecurityUtils.getSubject();

// 创建Token令牌,用户名/密码
UsernamePasswordToken passwordToken = new UsernamePasswordToken(userName,
passWord);
// 验证登录 会抛出异常
try {
subject.login(passwordToken);
System.out.println("身份验证成功!!!");
} catch (AuthenticationException e) {
e.printStackTrace();
System.out.println("身份验证失败!!!!");
}
return subject;
}
}

在测试类中进行测试,我没有使用junit进行测试,我是直接使用main方法测试
1.1测试subject.hasRole(String roleName) 返回值是boolean

public class TestRole {
public static void main(String[] args) {
Subject subject=ShiroUtil.login("shiro_role.ini", "yyt", "123");
//Subject subject=ShiroUtil.login("shiro_role.ini", "jack", "1234");
System.out.println(subject.hasRole("role2")?"有role2这个角色":"没有role2这个角色");
}
}

控制台的输出是:有role2这个角色,
如果是换成了jack,控制台输出的是:没有role2这个角色

1.2测试subject.hasRoles(List roles) 返回值是boolean[]

Subject subject=ShiroUtil.login("shiro_role.ini", "yyt", "123");
//Subject subject=ShiroUtil.login("shiro_role.ini", "jack", "1234");
boolean[] results=subject.hasRoles(Arrays.asList("role1","role2"));
for (boolean b : results) {
System.out.println(b);
}

用户是yyt的时候,控制台输出的是:true,true,代表的是yyt拥有role1,role2这个角色
用户是jack的时候,控制台出去的是:true,false,代表的是jack用户role1这个角色,但是没有role2这个角色

1.3测试subject.hasAllRoles(List roles) 返回值是boolean

Subject subject=ShiroUtil.login("shiro_role.ini", "yyt", "123");
//Subject subject=ShiroUtil.login("shiro_role.ini", "jack", "1234");
System.out.println(subject.hasAllRoles(Arrays.asList("role1","role2"))?"有role2,role1这个角色":"role1,role2这个角色不全有");

用户是yyt的时候,控制台输出的是:有role2,role1这个角色
用户是jack的时候,控制台出去的是:role1,role2这个角色不全有

1.4测试subject.checkRole(String roleName) 没有返回值,假如验证失败,则会报错:
org.apache.shiro.authz.UnauthorizedException: Subject does not have role [role2]

//Subject subject=ShiroUtil.login("shiro_role.ini", "yyt", "123");
Subject subject=ShiroUtil.login("shiro_role.ini", "jack", "1234");
subject.checkRole("role2");

1.5测试subject.checkRoles(String… roleIdentifiers) 可以传入多个角色名称,假如验证失败,则会报错:
org.apache.shiro.authz.UnauthorizedException: Subject does not have role [role2]

Subject subject=ShiroUtil.login("shiro_role.ini", "yyt", "123");
//Subject subject=ShiroUtil.login("shiro_role.ini", "jack", "1234");
subject.checkRoles("role2","role1");

1.6测试subject.checkRoles(String… roleIdentifiers) 可以传入多个角色名称,假如验证失败,则会报错:
org.apache.shiro.authz.UnauthorizedException: Subject does not have role [role2]

Subject subject=ShiroUtil.login("shiro_role.ini", "yyt", "123");
//Subject subject=ShiroUtil.login("shiro_role.ini", "jack", "1234");
subject.checkRoles(Arrays.asList("role1","role2"));

2.基于权限的控制
配置文件内容如下:shiro-permission.ini

[users]
yyt=123,role1,role2
jack=1234,role1
[roles]
role1=user:create,user:update
role2=user:create,user:delete

2.1测试subject.isPermitted(String name) 返回boolean

package com.kingsky;

import org.apache.shiro.subject.Subject;

public class TestPermission {
public static void main(String[] args) {
// Subject subject=ShiroUtil.login("shiro_role.ini", "yyt", "123");
Subject subject = ShiroUtil.login("shiro-permission.ini", "jack","1234");
boolean result= subject.isPermitted("user:create");
System.out.println(result?"有user:create这个权限":"没有user:create这个权限");
}
}

控制台输出:有user:create这个权限,假如权限名称改为:user:delete则输出的没有这个权限

2.2测试subject.isPermittedAll(String… names) 返回boolean,代表是全有或者不全有这些权限

// Subject subject=ShiroUtil.login("shiro_role.ini", "yyt", "123");
Subject subject = ShiroUtil.login("shiro-permission.ini", "jack","1234");
boolean result=subject.isPermittedAll("user:create","user:delete");
System.out.println(result?"有user:create,update这些权限":"这些user:create,update权限不全有");

控制台输出:有user:create,update这些权限
假如修改权限:user:delete,则会返回fase

2.3测试subject.isPermitted(String… names) 返回boolean[],对应的权限是不是有

// Subject subject=ShiroUtil.login("shiro_role.ini", "yyt", "123");
Subject subject = ShiroUtil.login("shiro-permission.ini", "jack","1234");
boolean results[]=subject.isPermitted("user:create","user:delete");
for (boolean b : results) {
System.out.println(b);
}

控制台输出:true(代表user:create有权限),false(代表user:delete没有权限)

2.4测试subject.checkPermission(String name);没有返回值,但是验证不成功则会报错:
org.apache.shiro.authz.UnauthorizedException: Subject does not have permission [user:delete]

// Subject subject=ShiroUtil.login("shiro_role.ini", "yyt", "123");
Subject subject = ShiroUtil.login("shiro-permission.ini", "jack","1234");
subject.checkPermission("user:delete");

2.5测试subject.checkPermissions(Stirng… names);没有返回值,但是验证不成功则会报错:
org.apache.shiro.authz.UnauthorizedException: Subject does not have permission [user:delete]

// Subject subject=ShiroUtil.login("shiro_role.ini", "yyt", "123");
Subject subject = ShiroUtil.login("shiro-permission.ini", "jack","1234");
subject.checkPermissions("user:create","user:update");

3.注解授权,jsp标签授权
在JSP页面通过相应的标签完成:

<shiro:hasRole name="admin">  
<!— 有权限 —>
</shiro:hasRole>

4、Shiro对权限字符串缺失部分的处理
如“user:view”等价于“user:view:”;而“organization”等价于“organization:”或者“organization::”。可以这么理解,这种方式实现了前缀匹配。
另外如“user:”可以匹配如“user:delete”、“user:delete”可以匹配如“user:delete:1”、“user::1”可以匹配如“user:view:1”、“user”可以匹配“user:view”或“user:view:1”等。即可以匹配所有,不加可以进行前缀匹配;但是如“:view”不能匹配“system:user:view”,需要使用“::view”,即后缀匹配必须指定前缀(多个冒号就需要多个来匹配)。