用户登陆成功之后, 要进行响应的操作就需要有对应的权限; 在进行操作之前对权限进行检查 - 授权.
权限控制通常有两类做法:
不同身份的用户登录,不同的操作菜单(没有权限的菜单不显示)
对所有用户显示所有菜单,当用户点击菜单以后再验证当前用户是否有此权限,如果没有则提示权限不足。
HTML 授权 - shiro 标签
我们使用<shiro:hasRole name="角色名">
,<shiro:hasPermission name="权限名">
, 可以判断当前角色是否具有某种权限, 从而显示在页面上, 例如:
<shiro:hasRole name="kmanager"> <!-- 是否是 kmanager 角色 --><li class="layui-nav-item layui-nav-itemed"><a class="" href="javascript:;">客户管理</a><dl class="layui-nav-child"><shiro:hasPermission name="sys:k:save"> <!-- 是否具有 sys:k:save 权限 --><dd><a href="javascript:;">新增客户</a></dd></shiro:hasPermission><shiro:hasPermission name="sys:k:delete"><dd><a href="javascript:;">删除客户</a></dd></shiro:hasPermission><shiro:hasPermission name="sys:k:update"><dd><a href="javascript:;">修改客户</a></dd></shiro:hasPermission><shiro:hasPermission name="sys:k:find"><dd><a href="">查询客户</a></dd></shiro:hasPermission></dl></li>
</shiro:hasRole>
最终运行效果如图:
可以发现zhaoliu和lisi登陆上显示的功能模块是不同的.
过滤器授权 - 修复越权
当然, 我们上面lisi用户是不存在查询客户权限的, 那么当lisi通过一些手段得到了查询客户的API路径, 则会造成越权漏洞, 我们可以本地模拟一下这个环境.
在PageController
中定义方法:
@RequestMapping("/xFind")
public String kFind() {return "k_find"; // 定位到 /resources/templates/k_find.html 文件
}
定义k_find.html
页面:
<!DOCTYPE html>
<html lang="en" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head><meta charset="UTF-8"><title>查询客户页面</title>
</head>
<body>
<h3>查询客户主页</h3>
<h4>当前用户: <shiro:principal/></h4>
</body>
</html>
随后定义/resources/templates/index.html模板, 在该模板中增加一个iframe, 以及超链接跳转:
<shiro:hasPermission name="sys:k:find"><dd><a href="/kFind" target="MainIframe">查询客户</a></dd><!-- 其他内容... -->
</shiro:hasPermission>
<div class="layui-body"><iframe width="100%" height="780" name="MainIframe"></iframe>
</div>
随后我们查看zhaoliu (具有查询客户权限)以及wangwu (不具有查询客户权限)的两种访问情况:
实际场景 wangwu 也存在查询客户的权限, 因为 <shiro:hasRole> 标签功能被隐藏了, 测试的时候最好使用zhangsan & zhaoliu进行测试.
这样就造成了一个越权漏洞, 而修复方法也很简单, 我们只需要对/kFind这个路由增加权限判断即可, 那么我们配置ShiroFilter如下:
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();shiroFilterFactoryBean.setSecurityManager(securityManager);Map<String, String> hashMap = new HashMap<>();hashMap.put("/", "anon"); // / 支持匿名访问hashMap.put("/login", "anon"); // login 支持匿名访问hashMap.put("/static/**", "anon"); // static 下的内容随便访问hashMap.put("/logout", "logout"); // 访问则退出登录hashMap.put("/kFind", "perms[sys:k:find]"); // 当前用户存在 sys:k:find 权限才允许访问shiroFilterFactoryBean.setFilterChainDefinitionMap(hashMap);shiroFilterFactoryBean.setLoginUrl("/login");shiroFilterFactoryBean.setUnauthorizedUrl("/"); // 当权限不允许时, 跳转的路径return shiroFilterFactoryBean;
}
配置完毕后不存在sys:k:find
权限的用户直接访问/kFind会被拦截.
注解授权 - 修复越权
除了上面的方法, 我们也可以在PageController::kFind上面定义注解:
@RequestMapping("/kFind")
@RequiresPermissions("sys:k:find") // 如果当前用户具备该权限, 那么才让访问.
public String kFind() {return "k_find"; // 定位到 /resources/templates/k_find.html 文件
}
当然定义完毕后, 我们需要在ShiroAutoConfigruation类中进行定义该注解所需要的Bean:
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();advisorAutoProxyCreator.setProxyTargetClass(true);return advisorAutoProxyCreator;
}@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();advisor.setSecurityManager(securityManager);return advisor;
}
如果当前用户不存在sys:k:find
权限, 那么会显示如下内容:
并且IDEA中会收到抛出来的异常信息:
org.apache.shiro.authz.AuthorizationException: Not authorized to invoke method: public java.lang.String com.heihu577.controller.PageController.kFind()
那么这种情况我们应该如何处理呢?这里我们可以通过Spring全局异常来将其跳转到某个页面中去即可.
package com.heihu577.exception;@ControllerAdvice
public class ErrorHandler {@ExceptionHandler(AuthorizationException.class)public ModelAndView handleException(Exception e) {ModelAndView modelAndView = new ModelAndView("index"); // 认证失败跳转到主页modelAndView.addObject("exception", e);System.out.println("进入异常处理...");return modelAndView;}
}
手工授权 - 修复越权
当然我们也可以使用subject进行判断, 代码如下:
@RequestMapping("/kFind")
// @RequiresPermissions("sys:k:find")
public String kFind() {Subject subject = SecurityUtils.getSubject();if (subject.isPermitted("sys:k:find")) {return "k_find"; } else {return "index";}
}