Shiro的授权

发表时间:2018-03-19 11:45:47 浏览量( 11 ) 留言数( 0 )

学习目标:

1、了解Shiro授权

2、掌握Shiro授权的接口


学习过程:

一、授权实现

1  配置权限shiro-permission.ini

    我们还是先使用ini的方式,配置,一般权限信息也还是会保存在数据库中.创建存放权限的配置文件shiro-permission.ini,如下:

[users]
#用户liubao的密码是123,此用户具有role1和role2两个角色
liubao=123,role1,role2
zhangsan=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

    在ini文件中用户、角色、权限的配置规则是:“用户名=密码,角色1,角色2...” “角色=权限1,权限2...”,首先根据用户名找角色,再根据角色找权限,角色是权限集合。权限字符串的规则是:“资源标识符:操作:资源实例标识符”,意思是对哪个资源的哪个实例具有什么操作,“:”是资源/操作/实例的分割符,权限字符串也可以使用*通配符。

例子:

用户创建权限:user:create,或user:create:*

用户修改实例001的权限:user:update:001

用户实例001的所有权限:user:*:001


2、测试代码

Shiro 的使用代码样式由三种:编程方式、注解方式、JSP标签方式。

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

Subject subject = SecurityUtils.getSubject();

if(subject.hasRole(“admin”)) {

//有权限

} else {

//无权限

}

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

@RequiresRoles("admin")

public void hello() {

//有权限

}

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

需要先导入标签

<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>


<shiro:hasRole name="admin">

<!— 有权限—>

</shiro:hasRole>


  测试代码先使用代码的方式进行测试。测试代码同认证代码,注意ini地址改为shiro-permission.ini,主要学习下边授权的方法,注意:在用户认证通过后执行下边的授权代码。

public class ShrioTest2 {
	@Test
	public void testPermission() {

		// 从ini文件中创建SecurityManager工厂
		Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-permission.ini");

		
		// 创建SecurityManager
		SecurityManager securityManager = factory.getInstance();

		// 将securityManager设置到运行环境
		SecurityUtils.setSecurityManager(securityManager);

		// 创建主体对象
		Subject subject = SecurityUtils.getSubject();

		// 对主体对象进行认证
		// 用户登陆
		// 设置用户认证的身份(principals)和凭证(credentials)
		UsernamePasswordToken token = new UsernamePasswordToken("liubao", "123");
		try {
			subject.login(token);
		} catch (AuthenticationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// 用户认证状态
		Boolean isAuthenticated = subject.isAuthenticated();
		System.out.println("用户认证状态:" + isAuthenticated);

		// 用户授权检测 基于角色授权
		// 是否有某一个角色
		System.out.println("用户是否拥有一个角色:" + subject.hasRole("role1"));
		// 是否有多个角色
		System.out.println("用户是否拥有多个角色:" + subject.hasAllRoles(Arrays.asList("role1", "role2")));
		
//		subject.checkRole("role1");
//		subject.checkRoles(Arrays.asList("role1", "role2"));

		// 授权检测,失败则抛出异常
		// subject.checkRole("role22");

		// 基于资源授权
		System.out.println("是否拥有某一个权限:" + subject.isPermitted("user:delete"));
		System.out.println("是否拥有多个权限:" + subject.isPermittedAll("user:create:1",	"user:delete"));
		
		//检查权限
		subject.checkPermission("sys:user:delete");
		subject.checkPermissions("user:create:1","user:delete");

	}
}


二、自定义realm

    与上边认证自定义realm一样,大部分情况是要从数据库获取权限数据,这里直接实现基于资源的授权。在认证章节写的自定义realm类中完善doGetAuthorizationInfo方法,此方法需要完成:根据用户身份信息从数据库查询权限字符串,由shiro进行授权。

public class CustomRealmSaltAndPsermission extends AuthorizingRealm {

	@Override
	public String getName() {
		return "CustomRealmSaltAndPsermission";
	}

	// 支持UsernamePasswordToken
	@Override
	public boolean supports(AuthenticationToken token) {
		return token instanceof UsernamePasswordToken;
	}

	// 认证
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

		// 从token中 获取用户身份信息
		String username = (String) token.getPrincipal();
		// 
		// 以后这里需要根据username从数据库中查询,如果查询不到则返回null
		if (!username.equals("liubao")) {// 这里模拟查询不到
			return null;
		}

		//数据库保存的密码,用户输入的密码还是123
		//按照固定规则加密码结果 ,此密码 要在数据库存储,原始密码 是123,盐是haha
		String password = "01ddae4032e17a1c338baac9c4322b30";
		//盐,随机数,此随机数也在数据库存储
		String salt = "haha";
				
		//返回认证信息
		SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
						username, password, ByteSource.Util.bytes(salt),getName());
		
		return simpleAuthenticationInfo;
	}

	// 授权
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		// 获取身份信息
		String username = (String) principals.getPrimaryPrincipal();

		SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
		// 根据身份信息从数据库中查询权限数据
		List<String> roles = new ArrayList<String>();
		roles.add("role1");
		roles.add("role2");

		for (String role : roles) {
			simpleAuthorizationInfo.addRoles(roles);
		}

		// ....这里使用静态数据模拟
		List<String> permissions = new ArrayList<String>();
		permissions.add("user:create");
		permissions.add("user:delete");

		// 将权限信息封闭为AuthorizationInfo
		for (String permission : permissions) {
			simpleAuthorizationInfo.addStringPermission(permission);
		}

		return simpleAuthorizationInfo;
	}

}


2 shiro-realmsaltandpermission.ini

[main]
#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#散列算法
credentialsMatcher.hashAlgorithmName=md5
#散列次数
credentialsMatcher.hashIterations=1

#将凭证匹配器设置到realm
CustomRealmSaltAndPsermission=com.shiro.CustomRealmSaltAndPsermission
CustomRealmSaltAndPsermission.credentialsMatcher=$credentialsMatcher
securityManager.realms=$CustomRealmSaltAndPsermission


3 测试代码

同上边的授权测试代码,注意修改ini地址一下

	Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-realmsaltandpermission.ini");


4 授权执行逻辑分析

1、执行login方法时调用realm的doGetAuthenticationInfo进行认证。

2、执行subject.hasRole或者isPermitted调用doGetAuthorizationInfo方法获得角色和权限信息。

3、securityManager通过ModularRealmAuthorizer进行授权。

4、ModularRealmAuthorizer调用realm获取权限信息。

5、ModularRealmAuthorizer再通过permissionResolver解析权限字符串,校验是否匹配。

可见每一次都会调用doGetAuthorizationInfo获得授权信息,所以如果不适用缓存,而是直接访问数据库,那么效率会低很多的。