用户成功登入我们系统之后,下一步就是要根据用户的角色给用户授予相应的权限。
Shiro支持的权限控制范围很广,大到一个模块的权限,小到一个按钮的操作权限都可以通过shiro来进行控制。
一、shiro基于权限的访问控制
主要有三种调用方式:
1、编码:
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
//有权限
} else {
//无权限
}
2、注解:
@RequiresRoles("admin")
public void hello() {
//有权限
}
3、标签:
<shiro:hasRole name="admin">
<!— 有权限 —>
</shiro:hasRole>
无论是哪一种访问控制的调用方式,都会从主体(Subject)委托给SecurityManager,最终委托给Realm下的doGetAuthorizationInfo()中来执行授权操作,并把授权结果返回到上层。
二、shiro的授权方式
shiro提供了两种授权方式,一种是基于角色的授权,即根据用户的角色来判定是否有相应的权限,另一种是基于权限字符串的授权,即根据事先约定好的权限字符串来控制权限。
与认证类似,需要实例化一个授权对象:simpleAuthorizationInfo,将角色或权限字符串赋值给它。
1、基于角色的授权:
1.1、单个角色
//添加角色信息给权限对象
//单个角色
simpleAuthorizationInfo.addRole("admin");
1.2、多个角色
//多个角色
List<String> roleList = new ArrayList<>();
roleList.add("admin");
roleList.add("user");
simpleAuthorizationInfo.addRoles(roleList);
Subject currentUser = SecurityUtils.getSubject();
//1、基于角色权限控制
System.out.println(currentUser.hasRole("admin"));
//2、基于多角色权限控制
System.out.println(currentUser.hasAllRoles(Arrays.asList("admin", "user")));
同样执行这段代码,1.1 打印出来的结果应该是ture,false,而1.2打印出来的结果应该是true,true。
2、基于权限字符串的授权:
//添加权限字符串给权限对象
simpleAuthorizationInfo.addStringPermission("user:create:01");
simpleAuthorizationInfo.addStringPermission("user:update");
Subject currentUser = SecurityUtils.getSubject();
//1、基于权限字符串的访问控制 资源标识符:操作:资源类型
System.out.println(currentUser.isPermitted("user:create:*"));
//2、分别具有哪些权限
boolean[] permitteds = currentUser.isPermitted("user:create", "user:update");
for (boolean per : permitteds) {
System.out.println(per);
}
shiro权限字符串的规则是:资源标识符:操作:实例,权限字符串也可以使用*,表示全部。shiro字符串通配符规则十分强大同时也很复杂,如果想要详细了解,可以去这里-字符串通配符权限这一段看看。
这里可以简单理解为:"模块\角色:操作:具体实例",比如user:create:01的含义就是,该用户具有user模块\角色的创建权限,但只能操作实例名为01的资源(如编号为01的订单)。而"user:update"则代表拥有user下修改所有资源的权限。也可以写作"user:update:*"。
三、代码实现
1、前台通过shiro标签控制按钮权限:
<shiro:hasPermission name="user:add:*">
<button class="layui-btn layui-btn-normal layui-btn-sm data-add-btn" lay-event="add">
添加 </button>
</shiro:hasPermission>
在顶层标签添加:xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"
还有一点值得注意的是,如果想在html中使用shiro标签还需要在ShiroConfig中添加如下代码:
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
关于在html中引入shiro标签,可以看看这篇博客:基于thymyleaf模板引擎下的html中引入shiro标签
2、从数据库中读取权限信息并授权:
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("进入授权流程:---->doGetAuthorizationInfo");
SysUser user = (SysUser) principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
List<SysResource> resourceList = sysResourceService.findPermissions(user.getRole());
for(SysResource resource : resourceList){
simpleAuthorizationInfo.addStringPermission(resource.getPermission());
}
return simpleAuthorizationInfo;
}
/**
* 认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("进入认证流程:---->doGetAuthenticationInfo");
String userName = (String) token.getPrincipal();
SysUser sysUser = sysUserService.findUserByName(userName);
//用户不存在
if (sysUser == null) {
return null;
}
//验证密码,使用加盐md5进行加密
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(sysUser, sysUser.getPassword(), getName());
authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(userName));
return authenticationInfo;
}
因为我在登录认证的SimpleAuthenticationInfo中传入的是SysUser是一个对象,因此我在授权中可以通过 SysUser user = (SysUser) principalCollection.getPrimaryPrincipal(); 直接拿到这个对象。
sys_user(用户表):
sys_resource(资源权限表):
sys_resource_role(资源-角色表):
这是相关联的几张数据库表的信息,可以看到,这里是通过一张中间表sys_resource_role,将用户和资源关联了起来,用户名yuyan拥有添加(user:add:*)的权限,而yuyan2则没有,反映在页面上,也就是yuyan这个用户可以看到添加这个按钮,而yuyan2则不行:
ok,那么这样就完成了对按钮的授权操作了!