类Shiro权限校验框架的设计和实现(2)--对复杂权限表达式的支持
前言:
我看了下shiro好像默认不支持复杂表达式的权限校验, 它需要开发者自己去做些功能扩展的工作. 针对这个问题, 同时也会为了弥补上一篇文章提到的支持复杂表示需求, 特地尝试写一下解决方法.
本文主要借助groovy脚本来实现复杂表达式的计算, 其思想是借鉴了Oval支持复杂表达式(groovy/javascript/ruby)的实现方式.
文章系列:
1. springmvc简单集成shiro
2. 类Shiro权限校验框架的设计和实现
3. 权限系统(RBAC)的数据模型设计
目标设定:
引入注解@MyEvaluateExpression, 其支持Groovy语法的布尔表达式(&&, ||, !), 用于复杂的权限计算评估.
表达式内部定义了两个函数:
// *) 判断是否拥有该角色
boolean hasRole(String role);
// *) 判断是否拥有该权限
boolean hasPermission(String permission);
举例如下:
case 1:
@MyEvaluateExpression(expr="hasRole('developer') || hasPermission('blog:write')")
表示了要么角色为developer, 要么该 用户有blog的写权限, 这个接口数据就能访问.
case 2:
@MyEvaluateExpression(expr="!hasRole('developer') && hasPermission('blog:write')")
表示了要么角色不能是developer, 同时该用户要有blog的写权限, 这个接口数据才能访问.
实现:
目标设定了, 选型也明确了, 那一切就好办了, ^_^.
对自定义函数hasRole, hasPermission, 其实是个伪函数, 这个表达式在使用groovy执行引擎执行前, 会被代码替换.
比如表达式: hasRole('developer') || hasPermission('blog:write')
会被替换为: rolesSet.contains('developer') || permissionSet.contains('blog:write')
其中rolesSet/permissionSet分别是角色/权限集合的set变量, 它们是外部注入脚本的bind变量.
然后在groovy引擎中执行时, 就很容易了.
完整代码:
参考先前的一篇文章: Groovy实现代码热载的机制和原理.
public class MyShiroGroovyHelper {private static ConcurrentHashMap<String, Class<Script>> zlassMaps= new ConcurrentHashMap<String, Class<Script>>();// *) 具体执行groovy代码public static Object invoke(String scriptText, Map<String, Object> params) {String key = fingerKey(scriptText);Class<Script> script = zlassMaps.get(key);if ( script == null ) {synchronized (key.intern()) {// Double Checkscript = zlassMaps.get(key);if ( script == null ) {GroovyClassLoader classLoader = new GroovyClassLoader();script = classLoader.parseClass(scriptText);zlassMaps.put(key, script);}}}Binding binding = new Binding();for ( Map.Entry<String, Object> ent : params.entrySet() ) {binding.setVariable(ent.getKey(), ent.getValue());}Script scriptObj = InvokerHelper.createScript(script, binding);return scriptObj.run();}// *) 为脚本代码生成md5指纹private static String fingerKey(String scriptText) {try {MessageDigest md = MessageDigest.getInstance("MD5");byte[] bytes = md.digest(scriptText.getBytes("utf-8"));final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();StringBuilder ret = new StringBuilder(bytes.length * 2);for (int i=0; i<bytes.length; i++) {ret.append(HEX_DIGITS[(bytes[i] >> 4) & 0x0f]);ret.append(HEX_DIGITS[bytes[i] & 0x0f]);}return ret.toString();} catch (Exception e) {throw new RuntimeException(e);}}}
在类Shiro权限校验框架的设计和实现, 继续做扩充
定义注解@MyEvaluateExpression:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyEvaluateExpression {String expr();
}
扩展MyShiroHelper类
public class MyShiroHelper {private static final String MY_SHIRO_AUTHRIZE_KEY = "my_shiro_authorize_key";// *) 评估复杂表达式public static boolean validateExpression(String expr) throws Exception {if ( expr == null ) {throw new Exception("invalid expression");}try {HttpSession session = getSession();MyAuthorizeInfo authorizeInfo = (MyAuthorizeInfo)session.getAttribute(MY_SHIRO_AUTHRIZE_KEY);if ( authorizeInfo == null ) {return false;}String scriptText = expr.replaceAll("hasRole", "roleSet.contains");scriptText = scriptText.replaceAll("hasPermission", "permissionSet.contains");Map<String, Object> params = new TreeMap<String, Object>();params.put("roleSet", authorizeInfo.getRoles());params.put("permissionSet", authorizeInfo.getPermissions());return (Boolean) MyShiroGroovyHelper.invoke(scriptText, params);} catch (Exception e) {throw new Exception("permission invalid state");} finally {}}private static HttpSession getSession() {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();return request.getSession();}}
扩展MyShiroAdvice类
@Aspect
@Component
public class MyShiroAdvice {/*** 定义切点*/@Pointcut("@annotation(com.springapp.mvc.myshiro.MyEvaluateExpression)")public void checkExprs() {}@Before("checkExprs()")public void doCheckExprs(JoinPoint jp) throws Exception {// *) 获取对应的注解MyEvaluateExpression mrp = extractAnnotation((MethodInvocationProceedingJoinPoint)jp,MyEvaluateExpression.class);boolean res = true;try {res = MyShiroHelper.validateExpression(mrp.expr());} catch (Exception e) {throw new Exception("invalid state");}if ( !res ) {throw new Exception("access disallowed");}}// *) 获取注解信息private static <T extends Annotation> T extractAnnotation(MethodInvocationProceedingJoinPoint mp, Class<T> clazz) throws Exception {Field proxy = mp.getClass().getDeclaredField("methodInvocation");proxy.setAccessible(true);ReflectiveMethodInvocation rmi = (ReflectiveMethodInvocation) proxy.get(mp);Method method = rmi.getMethod();return (T) method.getAnnotation(clazz);}}
测试代码:
在之前的Controller类上添加方法.
@RestController
@RequestMapping("/")
public class HelloController {@RequestMapping(value="/login", method={RequestMethod.POST, RequestMethod.GET})public String login() {// 1) 完成登陆验证// TODO// 2) 查询权限信息// TODO// 3) 注册权限信息MyAuthorizeInfo authorizeInfo = new MyAuthorizeInfo();authorizeInfo.addRole("admin");authorizeInfo.addPermission("blog:write");authorizeInfo.addPermission("blog:read");// *) 授权MyShiroHelper.authorize(authorizeInfo);return "ok";}@RequestMapping(value="/test3", method={RequestMethod.GET, RequestMethod.POST})@MyEvaluateExpression(expr="hasRole('admin') && !hasPermission('blog:write')")public String test3() {return "test3";}}
测试符合预期.
总结:
本文利用了Aspectj和Groovy, 实现了一个简易的支持复杂表达式权限验证的功能. 可能还不是特别完善, 但是对于小业务而言, 还是能够满足需求的, ^_^.
转载于:https://www.cnblogs.com/mumuxinfei/p/9355843.html
类Shiro权限校验框架的设计和实现(2)--对复杂权限表达式的支持相关推荐
- @vaild权限校验框架配合java正则表达式
样例 正则表达式示例 正则表达式简单笔记 @Data class ReadParam{@Pattern(regexp = "(\\w{1,10}@\\w{1,10}\\.\\w{1,10}) ...
- Spring Security太复杂?试试这个轻量、强大、优雅的权限认证框架!
各位程序猿小伙伴们,中秋快乐~在节日欢快的气氛中大家是不是还在奋笔疾书.沉浸在学习的海洋中呢? 小编这两天休息在家一直在想一个问题,那就是我们在开发SpringBoot项目的时候,该怎么做好权限认证呢 ...
- 这可能是史上功能最全的Java权限认证框架!
点击关注公众号,Java干货及时送达 今天给大家推荐的这个开源项目超级棒,可能是史上功能最全的 Java 权限认证框架! 这个开源项目就是:sa-token . Sa-Token是什么? sa-tok ...
- 自定义注解实现RBAC权限校验,不要再说你不会了
目录 1.前言 2.实现思路 3.编码实战 3.1.准备 3.2.数据库表准备 3.3.自定义注解 3.4.拦截器 3.5.接口使用 3.6.测试 3.7.结论 4.结束语 1.前言 学过Spring ...
- 这可能是史上功能最全的 Java 权限认证框架!
点击关注公众号,回复"1024"获取2TB学习资源! 今天给大家推荐的这个开源项目超级棒,可能是史上功能最全的 Java 权限认证框架! Sa-Token 介绍 Sa-Token是 ...
- SpringBoot集成权限认证框架(Sa-Token)
SpringBoot集成权限认证框架(Sa-Token) 介绍 身份验证又称"验证"."鉴权",是指通过一定的手段,完成对用户身份的确认. 身份验证的目的是确认 ...
- 应用框架的设计与实现学习手札系列(持续更新)
应用框架的设计与实现学习手札 类工厂服务 应用框架的设计与实现学习手札之类工厂服务--反射 转载于:https://www.cnblogs.com/stwyhm/archive/2006/08/14/ ...
- java 数据校验框架_自己写的基于java Annotation(注解)的数据校验框架
JavaEE6中提供了基于java Annotation(注解)的Bean校验框架,Hibernate也有类似的基于Annotation的数据校验功能,我在工作中,产品也经常需要使 用数据校验,为了方 ...
- SpringBoot+jwt+shiro实现登录验证及接口权限校验
SpringBoot+jwt+shiro+token实现对接口权限校验 最近在一个项目上实现登录模块,就想到了权限验证功能,了解了Spring Security和Shiro之后,决定使用Shiro来实 ...
- 使用 Shiro 设计基于用户、角色、权限的通用权限管理系统
一.前言 在大型的信息管理系统中,经常涉及到权限管理系统 下面来个 demo,很多复杂的系统的设计都来自它 代码已经放到github上了,地址:https://github.com/larger5/s ...
最新文章
- 百度paddle学习笔记
- 过滤器在图纸上的符号_零基础秒懂,看了这份建筑符号清单,就能快速识别建筑图纸,收藏...
- Gauss-Seidel迭代求解线性方程组
- P1417 烹调方案 (0/1背包+贪心)
- Shiro+springboot+mybatis(md5+salt+散列)认证与授权-01
- CVPR2021|引入记忆模块,突破长距离依赖视频预测的性能瓶颈
- 透过NpetShop 看Web项目开发中的分工合作
- java中簇如何表示_聚簇索引和非聚簇索引实际上是什么意思?
- phpExcel与jq的ajax
- Swift自定义Class实现Hashable
- 苹果Mac 桌面下方Dock 的App icon名称出现乱码怎么办?一个简单指令帮你解决
- PHP实现单向链表解决约瑟夫环问题
- ni软件可以卸载吗_电视盒子自带的软件居然可以这样卸载!
- 用 tf.data 加载图片
- 用VC++自制王码五笔输入法安装包(转)
- git分支拉取develop分支最新代码
- 上传word文档显示服务器出错,打开office出错的几种解决方法
- 自动更新之安装apk
- JavaScript新人总结
- 联通雁飞格物云平台,单片机连接MQTT服务器