前言:
  我看了下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)--对复杂权限表达式的支持相关推荐

  1. @vaild权限校验框架配合java正则表达式

    样例 正则表达式示例 正则表达式简单笔记 @Data class ReadParam{@Pattern(regexp = "(\\w{1,10}@\\w{1,10}\\.\\w{1,10}) ...

  2. Spring Security太复杂?试试这个轻量、强大、优雅的权限认证框架!

    各位程序猿小伙伴们,中秋快乐~在节日欢快的气氛中大家是不是还在奋笔疾书.沉浸在学习的海洋中呢? 小编这两天休息在家一直在想一个问题,那就是我们在开发SpringBoot项目的时候,该怎么做好权限认证呢 ...

  3. 这可能是史上功能最全的Java权限认证框架!

    点击关注公众号,Java干货及时送达 今天给大家推荐的这个开源项目超级棒,可能是史上功能最全的 Java 权限认证框架! 这个开源项目就是:sa-token . Sa-Token是什么? sa-tok ...

  4. 自定义注解实现RBAC权限校验,不要再说你不会了

    目录 1.前言 2.实现思路 3.编码实战 3.1.准备 3.2.数据库表准备 3.3.自定义注解 3.4.拦截器 3.5.接口使用 3.6.测试 3.7.结论 4.结束语 1.前言 学过Spring ...

  5. 这可能是史上功能最全的 Java 权限认证框架!

    点击关注公众号,回复"1024"获取2TB学习资源! 今天给大家推荐的这个开源项目超级棒,可能是史上功能最全的 Java 权限认证框架! Sa-Token 介绍 Sa-Token是 ...

  6. SpringBoot集成权限认证框架(Sa-Token)

    SpringBoot集成权限认证框架(Sa-Token) 介绍 身份验证又称"验证"."鉴权",是指通过一定的手段,完成对用户身份的确认. 身份验证的目的是确认 ...

  7. 应用框架的设计与实现学习手札系列(持续更新)

    应用框架的设计与实现学习手札 类工厂服务 应用框架的设计与实现学习手札之类工厂服务--反射 转载于:https://www.cnblogs.com/stwyhm/archive/2006/08/14/ ...

  8. java 数据校验框架_自己写的基于java Annotation(注解)的数据校验框架

    JavaEE6中提供了基于java Annotation(注解)的Bean校验框架,Hibernate也有类似的基于Annotation的数据校验功能,我在工作中,产品也经常需要使 用数据校验,为了方 ...

  9. SpringBoot+jwt+shiro实现登录验证及接口权限校验

    SpringBoot+jwt+shiro+token实现对接口权限校验 最近在一个项目上实现登录模块,就想到了权限验证功能,了解了Spring Security和Shiro之后,决定使用Shiro来实 ...

  10. 使用 Shiro 设计基于用户、角色、权限的通用权限管理系统

    一.前言 在大型的信息管理系统中,经常涉及到权限管理系统 下面来个 demo,很多复杂的系统的设计都来自它 代码已经放到github上了,地址:https://github.com/larger5/s ...

最新文章

  1. 百度paddle学习笔记
  2. 过滤器在图纸上的符号_零基础秒懂,看了这份建筑符号清单,就能快速识别建筑图纸,收藏...
  3. Gauss-Seidel迭代求解线性方程组
  4. P1417 烹调方案 (0/1背包+贪心)
  5. Shiro+springboot+mybatis(md5+salt+散列)认证与授权-01
  6. CVPR2021|引入记忆模块,突破长距离依赖视频预测的性能瓶颈
  7. 透过NpetShop 看Web项目开发中的分工合作
  8. java中簇如何表示_聚簇索引和非聚簇索引实际上是什么意思?
  9. phpExcel与jq的ajax
  10. Swift自定义Class实现Hashable
  11. 苹果Mac 桌面下方Dock 的App icon名称出现乱码怎么办?一个简单指令帮你解决
  12. PHP实现单向链表解决约瑟夫环问题
  13. ni软件可以卸载吗_电视盒子自带的软件居然可以这样卸载!
  14. 用 tf.data 加载图片
  15. 用VC++自制王码五笔输入法安装包(转)
  16. git分支拉取develop分支最新代码
  17. 上传word文档显示服务器出错,打开office出错的几种解决方法
  18. 自动更新之安装apk
  19. JavaScript新人总结
  20. 联通雁飞格物云平台,单片机连接MQTT服务器

热门文章

  1. 如何七周成为数据分析师
  2. 统计挖掘那些事(五)--(理论+案例)如何通俗地理解极大似然估计?
  3. Tomcat部署到CentOS7
  4. MongoDB 副本集
  5. [转载]Ubuntu 14.04设置固定ip
  6. delphi相关文件扩展名
  7. 编写build.xml的12个原则
  8. 两年以后重读了一篇文章,写了点东西。
  9. keepalived vrrp_script脚本不执行解决办法
  10. MySQL chartset