Shiro学习笔记——(5)授权

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/shaohe18362202126/article/details/79778514

一、授权简介

        授权,也叫访问控制,即在应用中控制谁能访问哪些资源(如访问页面/编辑数据/页面操作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)

  • 主体,即访问应用的用户,在Shiro中使用Subject代表该用户。用户只有授权后才允许访问相应的资源。
  • 资源,在应用中用户可以访问的任何东西,比如访问JSP页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。
  • 权限,安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如:访问用户列表页面,查看/新增/修改/删除用户数据(即很多时候都是CRUD(增查改删)式权限控制),打印文档等等。。。
  • 角色,代表了操作集合,可以理解为权限的集合,一般情况下我们会赋予用户角色而不是权限,即这样用户可以拥有一组权限,赋予权限时比较方便。典型的如:项目经理、技术总监、CTO、开发工程师等都是角色,不同的角色拥有一组不同的权限。
  • 隐式角色即直接通过角色来验证用户有没有操作权限,如在应用中CTO、技术总监、开发工程师可以使用打印机,假设某天不允许开发工程师使用打印机,此时需要从应用中删除相应代码;再如在应用中CTO、技术总监可以查看用户、查看权限;突然有一天不允许技术总监查看用户、查看权限了,需要在相关代码中把技术总监角色从判断逻辑中删除掉;即粒度是以角色为单位进行访问控制的,粒度较粗;如果进行修改可能造成多处代码修改。
  • 显示角色:在程序中通过权限控制谁能访问某个资源,角色聚合一组权限集合;这样假设哪个角色不能访问某个资源,只需要从角色代表的权限集合中移除即可;无须修改多处代码;即粒度是以资源/实例为单位的;粒度较细。

二、授权测试

(1)授权方式

1、 编程式:通过写if/else授权代码块完成:

Subject subject = SecurityUtils.getSubject();  
if(subject.hasRole(“admin”)) {  
    //有权限  
} else {  
    //无权限  
}  

2、注解式:通过在执行的Java方法上放置相应的注解完成:

@RequiresRoles("admin")  
public void hello() {  
    //有权限  
}  

3、JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成:

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

(2)shiro-permission.ini配置文件

模拟数据库,存储权限角色数据
#用户
[users]
#用户zhang的密码是123,此用户具有role1和role2两个角色
zhang=123,role1,role2
wang=123,role2

#权限
[roles]
#角色role1对资源user拥有create、update权限
role1=user:create,user:update
#角色role2对资源user拥有create、delete权限
role2=user:create,user:delete
#角色role3对资源user拥有create权限
role3=user:create

权限标识符号规则:资源:操作:实例(中间使用半角:分隔)

user:create:01  表示对用户资源的01实例进行create操作。

user:create:表示对用户资源进行create操作,相当于user:create:*,对所有用户资源实例进行create操作。 

user:*:01  表示对用户资源实例01进行所有操作。

(3)代码实现

public class AuthorizationTest {

	@Test
	public void testAuthorization() {
		// 构建SecurityManager工厂,IniSecurityManagerFactory可以从ini文件中初始化SecurityManager环境
		Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory(
				"classpath:shiro-permission.ini");
		// 通过工厂创建SecurityManager
		org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
		// 将securityManager设置当前的运行环境中
		SecurityUtils.setSecurityManager(securityManager);

		// 从SecurityUtils里边创建一个subject
		Subject subject = SecurityUtils.getSubject();
		// 在认证提交前准备token(令牌)
		UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
		try {
			// 执行认证提交
			subject.login(token);
		} catch (AuthenticationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// 基于角色的授权
		boolean ishasRole = subject.hasRole("role1");
		System.out.println("单角色:" + ishasRole);
		boolean hasAllRoles = subject.hasAllRoles(Arrays.asList("role1", "role2"));
		System.out.println("多角色:" + hasAllRoles);
		// 使用check方法,没有授权会抛出异常
		try {
			subject.checkRole("role4");
		} catch (Exception e) {
			e.printStackTrace();
		}

		// 基于资源的授权
		boolean isPermitted = subject.isPermitted("user:create:1");
		System.out.println("单权限:" + isPermitted);
		boolean isPermittedAll = subject.isPermittedAll("user:create:1", "user:delete");
		System.out.println("多个权限:" + isPermittedAll);
		// 使用check方法,没有授权会抛出异常
		try {
			subject.checkPermission("user:create:1");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

三、自定义Realm进行授权

        上边的程序通过shiro-permission.ini对权限信息进行静态配置,实际开发中从数据库中获取权限数据。就需要自定义realm,由realm从数据库查询权限数据。

            realm根据用户身份查询权限数据,将权限数据返回给authorizer(授权器)。

(1)Realm代码

com.shiro.realm.MyRealm
	/**
	 * 用于授权
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

		// 从principals获取身份信息
		// 将getPrimaryPrincipal方法返回的值转为真实的身份类型
		// (在上面doGetAuthenticationInfo认证通过填充到SimpleAuthenticationInfo中)
		String username = (String) principals.getPrimaryPrincipal();

		// 根据username获取数据库中的权限
		// 查询数据库.....
		// 模拟数据库
		List<String> permissions = new ArrayList<String>();
		permissions.add("user:create");
		permissions.add("user:delete");
		// 查到权限数据,返回授权信息(包括permissions信息)
		SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
		simpleAuthorizationInfo.addStringPermissions(permissions);
		return simpleAuthorizationInfo;
	}

(2)测试代码

com.shiro.authorization.AuthorizationTest

	@Test
	public void testAuthorizationRealm() {
		// 构建SecurityManager工厂,IniSecurityManagerFactory可以从ini文件中初始化SecurityManager环境
		Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory(
				"classpath:shiro-realm.ini");
		// 通过工厂创建SecurityManager
		org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
		// 将securityManager设置当前的运行环境中
		SecurityUtils.setSecurityManager(securityManager);

		// 从SecurityUtils里边创建一个subject
		Subject subject = SecurityUtils.getSubject();
		// 在认证提交前准备token(令牌)
		UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
		try {
			// 执行认证提交
			subject.login(token);
		} catch (AuthenticationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// 是否认证通过
		boolean isAuthenticated = subject.isAuthenticated();
		System.out.println("是否认证通过:" + isAuthenticated);

		// 基于资源的授权
		// isPermitted方法会调用MyRealm中的doGetAuthorizationInfo方法从数据库中查询权限,进行比对
		boolean isPermitted = subject.isPermitted("user:create:1");
		System.out.println("单权限:" + isPermitted);
		boolean isPermittedAll = subject.isPermittedAll("user:create:1", "user:delete");
		System.out.println("多个权限:" + isPermittedAll);
		// 使用check方法,没有授权会抛出异常
		try {
			subject.checkPermission("user:create:1");
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

四、授权流程

  1. 对subject进行授权,调用方法Subject.isPermitted*/hasRole*,其会委托给SecurityManager。
  2. SecurityManager执行授权,通过ModularRealmAuthorizer执行授权
  3. ModularRealmAuthorizer执行realm(自定义的CustomRealm)从数据库查询权限数据。调用realm的授权方法:doGetAuthorizationInfo 。获取Subject相应的角色/权限用于匹配传入的角色/权限。
  4. realm从数据库查询权限数据,返回ModularRealmAuthorizer
  5. ModularRealmAuthorizer调用PermissionResolver进行权限串比对
  6. 如果比对后,isPermitted中"permission串"在realm查询到权限数据中,说明用户访问permission串有权限,否则没有权限,抛出异常。

猜你喜欢

转载自blog.csdn.net/shaohe18362202126/article/details/79778514
今日推荐