一、Shiro授权:
1、授权与权限:
(1)授权:访问控制,必须具有该资源的访问权限才可以访问该资源。
(2)权限模型:标准权限数据模型包括 :用户、角色、权限(包括资源和权限)、用户角色关系、角色权限关系。
(3)权限分配:通过UI界面方便给用户分配权限,对上边权限模型进行增、删、改、查操作。
(4)权限控制:
第一种:基于角色的权限控制:根据角色判断是否有操作权限,因为角色的变化性较高,如果角色修改需要修改控制代码,系统可扩展性不强。
第二种:基于资源的权限控制:根据资源权限判断是否有操作权限,因为资源较为固定,如果角色修改或角色中权限修改不需要修改控制代码,使用此方法系统可维护性很强。建议使用。
(5)权限管理的解决方案:
①对于粗颗粒权限管理,建议在系统架构层面去解决,写系统架构级别统一代码(基础代码)。
粗颗粒权限:比如对系统的url、菜单、jsp页面、页面上按钮、类方法进行权限管理,即对资源类型进行权限管理。
②对于细颗粒权限管理:细颗粒权限管理是系统的业务逻辑,业务逻辑代码不方便抽取统一代码,建议在系统业务层进行处理。
粗颗粒权限:比如用户id为001的用户信息(资源实例)、类型为t01的商品信息(资源实例),对资源实例进行权限管理,可以理解为对数据级别的权限管理。
2、授权流程:
(1)对subject进行授权,调用方法isPermitted("permission串");
(2)SecurityManager执行授权,通过ModularRealmAuthorizer执行授权;
(3)ModularRealmAuthorizer执行realm(自定义的CustomRealm)从数据库查询权限数据,调用realm的授权方法:doGetAuthorizationInfo;
(4)realm从数据库查询权限数据,返回ModularRealmAuthorizer;
(5)ModularRealmAuthorizer调用PermissionResolver进行权限串比对;
(6)如果比对后,isPermitted中"permission串"在realm查询到权限数据中,说明用户访问permission串有权限,否则 没有权限,抛出异常。
3、Shiro 支持三种方式的授权:
(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>
二、Shiro授权示例程序:
1、入门的Shiro授权程序:
(1)编写shiro-permission.ini文件:
shiro-permission.ini文件里边的内容相当于在数据库查询出来的;
#用户
[users]
#用户zhangsan的密码是123,此用户具有role1和role2两个角色
zhangsan=123456,role1,role2
wangwu=123456,role2
#权限
[roles]
#角色role1对资源user拥有create、update权限
role1=user:create,user:update
role2=user:create,items:delete
role3=user:create
权限标识符号规则:资源:操作:实例(中间使用半角:分隔)
user:create:01 表示对用户资源的01实例进行create操作。
user:create 表示对用户资源进行create操作,相当于user:create:*,对所有用户资源实例进行create操作。
user:*:01 表示对用户资源实例01进行所有操作。
(2)程序编写:
//shiro的授权测试:
public class AuthorizationTest {
//角色授权、资源授权测试:
@Test
public void testAuthorization(){
Factory<SecurityManager> factory = new IniSecurityManagerFactory(
"classpath:shiro-permission.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan",
"123456");
try {
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
}
System.out.println("认证状态:" + subject.isAuthenticated());
//认证通过之后执行授权:
//基于角色的授权
//hasRole传入角色标识:
boolean ishasRole = subject.hasRole("role1");
System.out.println("单个角色判断" + ishasRole);
//hasAllRoles是否拥有多个角色:
boolean hasAllRoles = subject.hasAllRoles(Arrays.asList("role1","role2","role3"));
System.out.println("多个角色判断" + hasAllRoles);
// 使用check方法进行授权,如果授权不通过会抛出异常
subject.checkRole("role2");
//基于资源的授权:
//isPermitted传入权限标识符
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方法进行授权,如果授权不通过会抛出异常
subject.checkPermission("items:delete:1");
}
}
(3)执行结果:
至此,Shiro授权入门程序就完成了。在这个程序中,我们读取的是在shiro.ini配置文件中静态配置好的权限内容,但是在实际开发,我们通常是从数据库中查询用户所拥有的权限信息,因此,我们需要使用自定义的Realm进行从数据库中查询用户的权限信息。
2、自定义realm进行授权:
上面的程序是通过shiro-permission.ini对权限文件进行静态配置,在实际开发中,都是从数据库中获取权限数据。就需要自定义realm,由realm从数据库查询权限数据。
realm根据用户身份查询权限数据,将权限数据返回给authorizer(授权器)。
(1)自定义realm:
//自定义的Realm,需要继承AuthorizingRealm,并且重写这个类的两个方法
public class CustomRealm extends AuthorizingRealm{
// 设置realm的名称
@Override
public void setName(String name) {
super.setName("customRealm");
}
// 用于授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
//从principals获取身份信息
//将getPrimaryPrincipal方法返回值转为真实身份(在上边的doGetAuthenticationInfo认证通过填充到SimpleAuthenticationInfo中的身份类型)
String userCode = (String) principals.getPrimaryPrincipal();
//根据身份信息获取权限信息
//连接数据库
//模拟从数据库获取到数据
List<String> permissions = new ArrayList<String>();
permissions.add("user:create");//用户的创建
permissions.add("items:add");//商品添加权限
//查到权限数据,返回授权信息(要包括上边的permissions)
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//将上边查询到授权信息填充到simpleAuthorizationInfo对象中
simpleAuthorizationInfo.addStringPermissions(permissions);
return simpleAuthorizationInfo;
}
}
(2)编写shiro-realm.ini文件:
在shiro-realm.ini文件中配置自定义的realm,将realm设置到securityManager中。
[main]
#自定义的realm
customRealm=com.zwp.shiro.realm.CustomRealm
#将realm设置到securityManager,相当于spring中的注入
securityManager.realms=$customRealm
(3)测试:
// 自定义realm进行资源授权测试
@Test
public void testAuthorizationCustomRealm() {
Factory<SecurityManager> factory = new IniSecurityManagerFactory(
"classpath:shiro-realm.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
// 创建token令牌
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan",
"123456");
// 执行认证
try {
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
}
System.out.println("认证状态:" + subject.isAuthenticated());
// 认证通过后执行授权
// 基于资源的授权,调用isPermitted方法会调用CustomRealm从数据库查询正确权限数据
// isPermitted传入权限标识符,判断user:create:1是否在CustomRealm查询到权限数据之内
boolean isPermitted = subject.isPermitted("user:create:1");
System.out.println("单个权限判断" + isPermitted);
boolean isPermittedAll = subject.isPermittedAll("user:create:1",
"user:create");
System.out.println("多个权限判断" + isPermittedAll);
// 使用check方法进行授权,如果授权不通过会抛出异常
subject.checkPermission("items:add:1");
}
(4)测试结果:
至此,Shiro使用自定义Realm进行授权的程序就完成了。