上一篇讲解了shiro的认证流程以及实现。这一篇,笔者要详细讲解shiro的另一重要部分就是权限控制,简称授权。
授权流程跟认证流程很相似,大致过程为当前用户subject调用isPermitted*/hasRole* 接口时,将会委派给security manager,security manager委派给Authorizer(授权器)。比如调用的是isPermitted("user:create"),其中PermissionResolver 把字符串转换成相应的 Permission 实例。在进行授权之前,其会调用相应的 Realm 获取 Subject 相应的角色/权限用于匹配传入的角色/权限;Authorizer 会判断 Realm 的角色/权限是否和传入的匹配,如果有多个Realm,会委托给 ModularRealmAuthorizer 进行循环判断, 如果匹配如 isPermitted*/hasRole* 会返回true,否则返回false表示授权失败。
Shiro支持三种授权方式,分别如下:
编程式:通过写if/else 授权代码块完成 (不推荐)
注解式:通过在执行的Java方法上放置相应的注解完成,没有权限将抛出相 应的异常 (需要在springmvgc配置文件加一段配置才能注解生效)
<!--
4. 配置 LifecycleBeanPostProcessor. 可以自定的来调用配置在 Spring IOC 容器中 shiro bean 的生命周期方法.
-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- Enable Shiro Annotations for Spring-configured beans. Only run after
the lifecycleBeanProcessor has run: -->
<!--
5. 启用 IOC 容器中使用 shiro 的注解. 但必须在配置了 LifecycleBeanPostProcessor 之后才可以使用.
-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true" />
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成(需要引入标签库<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> )
例如
<shiro:principal></shiro:principal>
<shiro:hasRole name="admin">
shiro的权限规则
规则:资源标识符:操作:对象实例 ID 。 即对哪个资源的哪个 实例可以进行什么操作.。其默认支持通配符权限字符串,: 表 示资源/操作/实例的分割;, 表示操作的分割,* 表示任意资 源/操作/实例。例如:user:create 表示可以拥有对user模块(领域)进行新增操作的权限。
接下来进行具体演示,准备好数据表
1.用户表
2.角色表
3.权限表
4.用户-角色表
5.角色-权限表
6.service接口
实际开发中,我们常常将用户按角色来划分,不同角色拥有不同权限,而不是直接按用户具有哪些权限来操作。
现在开始编码实现,实现realm中的授权方法,具体如下
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取唯一标识principal
String principal = (String) principalCollection.getPrimaryPrincipal();
//从数据库获取用户的具体权限信息
Set<String> permissions = userService.getPermissions(principal);
//从数据库获取用户的具体角色信息
Set<String> roles = userService.getRoles(principal);
//创建具有permissions、roles信息的SimpleAuthorizationInfo返回
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
info.setStringPermissions(permissions);
return info;
}
UserServcie接口方法的具体实现如下
@Override
public Set<String> getRoles(String loginName) {
SysUser user = getUserByLoginNmae(loginName);
SysUserRoleExample userRoleExample = new SysUserRoleExample();
userRoleExample.createCriteria().andUserCodeEqualTo(user.getUserCode());
List<SysUserRole> rolesCode = userRoleMapper.selectByExample(userRoleExample);
Set<String> roles = new HashSet<>();
SysRoleExample roleExample = new SysRoleExample();
for (SysUserRole userRole : rolesCode) {
roleExample.clear();
roleExample.createCriteria().andRoleCodeEqualTo(userRole.getRoleCode());
List<SysRole> roles1 = roleMapper.selectByExample(roleExample);
for (SysRole role : roles1) {
roles.add(role.getRoleCode());
}
}
return roles;
}
@Override
public Set<String> getPermissions(String loginName) {
Set<String> permissions = new HashSet<>();
Set<String> roles = getRoles(loginName);
SysRolePermissionExample example = new SysRolePermissionExample();
for (String role : roles) {
example.clear();
example.createCriteria().andRoleCodeEqualTo(role);
List<SysRolePermission> rolePermissions = rolePermissionMapper.selectByExample(example);
for (SysRolePermission permission : rolePermissions) {
permissions.add(permission.getPermissionCode());
}
}
return permissions;
}
@Override
public SysUser getUserByLoginNmae(String loginName) {
SysUserExample example = new SysUserExample();
example.createCriteria().andLoginNameEqualTo(loginName);
List<SysUser> users = userMapper.selectByExample(example);
return users.get(0);
}
这代码还没有优化,有点又臭又长。。。
这里要补充一点就是,在shiro认证的时候,在realm里就会把先认证和后授权一次性执行完毕,也就是说,在登陆成功后,该用户的角色和权限信息就已经得到并保存起来了,而不是认证的时候就只执行doGetAuthenticationInfo,授权的时候就再跳到realm只执行doGetAuthorizationInfo。这也很符合逻辑。在接下来的一些权限访问控制时,就会取出来进行匹配。
后台controller代码如下
//只有admin角色才能行执行controller
@RequiresRoles(value = {"admin"})
//只有具有"user:delete"权限才能执行此controller
@RequiresPermissions({"user:delete"})
@RequestMapping("/UserCreate.action")
public String userCreate(){
Subject subject = SecurityUtils.getSubject();
//用户已经认证过
if (subject.isAuthenticated()){
if (subject.isPermitted("user:delete")) System.out.println("拥有创建用户权限!!!");
if (subject.hasRole("admin")) System.out.println("拥有角色");
userService.createUser(new SysUser());
System.out.println("controller创建user");
}
return "list";
}
注意:要想在springmvc让shiro的注解生效,必须在spring的配置文件配置让注解生效的配置
笔者试了一下,在service层加注解,注解会失效,因为配置了spring的事务管理的原因,让shiro注解失效了,所以最好就是加在controller里,也比较符合我们的实际开发。
如果没有权限或者角色访问的话,shiro会报错,如下所示