前言:
  我看了下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 Check
script = 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, 实现了一个简易的支持复杂表达式权限验证的功能. 可能还不是特别完善, 但是对于小业务而言, 还是能够满足需求的, ^_^.

  

类Shiro权限校验框架的设计和实现(2)--对复杂权限表达式的支持的更多相关文章

  1. 类Shiro权限校验框架的设计和实现

    前言: 之前简单集成了springmvc和shiro用于后台管理平台的权限控制, 设计思路非常的优美, 而且编程确实非常的方便和简洁. 唯一的不足, 我觉得配置稍有些繁琐. 当时我有个小想法, 觉得可 ...

  2. 基于Spring Aop实现类似shiro的简单权限校验功能

    在我们的web开发过程中,经常需要用到功能权限校验,验证用户是否有某个角色或者权限,目前有很多框架,如Shiro Shiro有基于自定义登录界面的版本,也有基于CAS登录的版本,目前我们的系统是基于C ...

  3. shiro太复杂?快来试试这个轻量级权限认证框架!

    前言 在java的世界里,有很多优秀的权限认证框架,如Apache Shiro.Spring Security 等等.这些框架背景强大,历史悠久,其生态也比较齐全. 但同时这些框架也并非十分完美,在前 ...

  4. abp 取消权限校验

    在abp中,通过ABP_PERMISSIONS表来存储定义appService中的方法权限校验.设置方式如下: [AbpAuthorize(PermissionNames.Pages_Users)] ...

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

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

  6. Shiro + SSM(框架) + Freemarker(jsp)

    Shiro + SSM(框架) + Freemarker(jsp)讲解的权限控制Demo,还不赶快去下载? 我们知道Ajax不能做页面redirect和forward跳转,所以Ajax请求假如没登录, ...

  7. fastDFS shiro权限校验 redis FreeMark页面静态化

    FastDFS是一个轻量级分布式文件系统,   使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传.下载等服务   FastDFS服务端有两个角色:跟踪器(tracker)和存储节点( ...

  8. Shiro 权限校验不通过时,区分GET和POST请求正确响应对应的方式

    引入:https://blog.csdn.net/catoop/article/details/69210140 本文基于Shiro权限注解方式来控制Controller方法是否能够访问. 例如使用到 ...

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

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

随机推荐

  1. UVA11995 I Can Guess the Data Structure!

    思路 简单题,用栈,队列,优先队列直接模拟即可 代码 #include <cstdio> #include <algorithm> #include <cstring&g ...

  2. python 基础知识点一

    基础数据类型初始. 数字:int 12,3,45 + - * / **    int: bit_lenth()转化为2进制的最小位数. % 取余数 ps:type() 字符串转化成数字:int(str ...

  3. msvc命令行cl编译c程序问题及解决

    1.cmd命令行cl提示没有这玩意儿 装上Visual Studio之类 2.cl main.c提示缺dll everything搜dll所在路径,在环境配置PATH增加对应bin.IDE 3.cl ...

  4. Jade入门学习笔记

    jade是超高性能的node JavaScript模板引擎,有着非常强大的API和大量杰出的特性.它主要针对node的服务端.由于商标的原因,改为Pug,哈巴狗.Pug有它本身的缺点--可移植性差,调 ...

  5. JQUERY的属性进行操作

    Jquery方式操作属性(attribute) $().attr(属性名称);   //获得属性信息值 $().attr(属性名称,值);    //设置属性的信息 $().removeAttr(属性 ...

  6. ionicAPP打开第三方APP

    近来,碰到一个问题,需要在ionicAPP中打开第三方APP 然后,就找资料,发现了个比较好的解决方案 可以参考:https://blog.csdn.net/a727911438/article/de ...

  7. 2019清明期间qbxt培训qaq

    4.4下午:矩阵qwq part1矩阵乘法: 概念: 一个m×p的矩阵A 乘 一个p×n的矩阵B 得到一个矩阵一个m×n的矩阵AB 其中: 矩阵乘法满足结合律.分配率,不满足交换律 矩阵乘法—solu ...

  8. bzoj 5068: 友好的生物

    大意: n个生物, 每个生物有k种属性, 友好度通过下式计算. , C为给定非负数组, 求友好度最大值. k比较小, 求的是最大值并且$C_i$非负, 所以可以暴力枚举正负情况去绝对值号. #incl ...

  9. HTML5 FormData方法介绍

    详细见链接 转载说明:转自CSDN上“诗渊”的博客

  10. coursera-斯坦福-机器学习-吴恩达-笔记week4

    1 神经网络的提出 线性回归和逻辑回归能很好的解决特征变量较少的问题,但对于变量数量增加的复杂非线性问题,单纯增加二次项和三次项等特征项的方法计算代价太高. 2 神经网络算法 2.1 神经元 模拟神经 ...