用户成功登入我们系统之后,下一步就是要根据用户的角色给用户授予相应的权限。
Shiro支持的权限控制范围很广,大到一个模块的权限,小到一个按钮的操作权限都可以通过shiro来进行控制。
一、shiro基于权限的访问控制
主要有三种调用方式:
1、编码:
- Subject subject = SecurityUtils.getSubject();
- if(subject.hasRole(“admin”)) {
- //有权限
- } else {
- //无权限
- }
2、注解:
- @RequiresRoles("admin")
- public void hello() {
- //有权限
- }
3、标签:
"admin">
无论是哪一种访问控制的调用方式,都会从主体(Subject)委托给SecurityManager,最终委托给Realm下的doGetAuthorizationInfo()中来执行授权操作,并把授权结果返回到上层。
二、shiro的授权方式
shiro提供了两种授权方式,一种是基于角色的授权,即根据用户的角色来判定是否有相应的权限,另一种是基于权限字符串的授权,即根据事先约定好的权限字符串来控制权限。
与认证类似,需要实例化一个授权对象:simpleAuthorizationInfo,将角色或权限字符串赋值给它。
1、基于角色的授权:
1.1、单个角色
- //添加角色信息给权限对象
- //单个角色
- simpleAuthorizationInfo.addRole("admin");
1.2、多个角色
- //多个角色
- List
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
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拥有添加的权限,而yuyan2则没有,反映在页面上,也就是yuyan这个用户可以看到添加这个按钮,而yuyan2则不行:
ok,那么这样就完成了对按钮的授权操作了!