前言:
  之前简单集成了springmvc和shiro用于后台管理平台的权限控制, 设计思路非常的优美, 而且编程确实非常的方便和简洁. 唯一的不足, 我觉得配置稍有些繁琐. 当时我有个小想法, 觉得可以写个更小巧版的shiro, 用于权限控制.
  因为shiro本身不涉及权限的数据模型, 而且权限控制这块也只是它的一小部分功能点. 因此剥离后的工作量, 预计不是很大.

相关文章列表:
  1. springmvc简单集成shiro 
  2. 利用Aspectj实现Oval的自动参数校验

设计目标
  首先来讲讲定位吧, Shiro本身是一个安全组件, 可以复用. 但是我这个版本, 倾向于半成品. 它属于一种设计思路, 多少和业务有些耦合, 它有一定的借鉴作用, 其他简易项目可以拿去copy/paste, 然后修改下代码, 就能使用上的, ^_^.
  列一下设计目标吧:
  1. 只支持权限校验, 不涉及认证
  2. 支持role/permission的注解校验(包括简单的与或操作)
  就这么简单了, ^_^.

实践:
  整个小框架, 是基于springmvc环境下, 其数据存储于http session中, 但除此以外, 和外界再无瓜葛, ^_^.
  它的核心就一个工具类, 一个切面类, 两个注解, 非常的简洁.
  权限注解类的引入:

// *) 逻辑与或
public enum MyLogic {
AND,
OR;
} // *) 定义权限校验注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRequiresPermissions {
String[] value();
MyLogic logic() default MyLogic.AND;
} // *) 定义角色校验注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRequiresRoles {
String[] value();
MyLogic logic() default MyLogic.AND;
}

  工具类引入:

@Getter
public class MyAuthorizeInfo { private Set<String> roles = new TreeSet<String>(); private Set<String> permissions = new TreeSet<String>(); public void addRole(String role) {
roles.add(role);
} public void addPermission(String permission) {
permissions.add(permission);
} } public class MyShiroHelper { private static final String MY_SHIRO_AUTHRIZE_KEY = "my_shiro_authorize_key"; // 授权权限列表
public static void authorize(MyAuthorizeInfo authorizeInfo) {
HttpSession session = getSession();
session.setAttribute(MY_SHIRO_AUTHRIZE_KEY, authorizeInfo);
} // 验证角色
public static boolean validateRoles(String[] roles, MyLogic logic) throws Exception { if ( roles == null || roles.length == 0 ) {
return true;
}
try {
HttpSession session = getSession();
MyAuthorizeInfo authorizeInfo = (MyAuthorizeInfo)session
.getAttribute(MY_SHIRO_AUTHRIZE_KEY);
if ( authorizeInfo == null ) {
return false;
}
return evaluateExpression(roles, logic, authorizeInfo.getRoles());
} catch (Exception e) {
throw new Exception("permission invalid state");
} finally {
} } // 验证权限
public static boolean validatePermssion(String[] permissions, MyLogic logic) throws Exception { if ( permissions == null || permissions.length == 0 ) {
return true;
}
try {
HttpSession session = getSession();
MyAuthorizeInfo authorizeInfo = (MyAuthorizeInfo)session
.getAttribute(MY_SHIRO_AUTHRIZE_KEY);
if ( authorizeInfo == null ) {
return false;
}
return evaluateExpression(permissions, logic, authorizeInfo.getPermissions());
} catch (Exception e) {
throw new Exception("permission invalid state");
} finally {
} } // *) 与或操作, 布尔表达式的计算
public static boolean evaluateExpression(String[] values, MyLogic logic, Set<String> sets) {
if ( MyLogic.AND == logic ) {
for ( String val : values ) {
if ( !sets.contains(val) ) {
return false;
}
}
return true;
} else if ( MyLogic.OR == logic ) {
for ( String val : values ) {
if ( sets.contains(val) ) {
return true;
}
}
return false;
}
return true;
} // *) 获取Http Session
private static HttpSession getSession() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
return request.getSession();
} }

  最后是切面类的引入:

@Aspect
@Component
public class MyShiroAdvice { /**
* 定义切点, 用于权限的校验
*/
@Pointcut("@annotation(com.springapp.mvc.myshiro.MyRequiresPermissions)")
public void checkPermissions() {
} /**
* 定义切点, 用于角色的校验
*/
@Pointcut("@annotation(com.springapp.mvc.myshiro.MyRequiresRoles)")
public void checkRoles() {
} @Before("checkPermissions()")
public void doCheckPermission(JoinPoint jp) throws Exception { // *) 获取对应的注解
MyRequiresPermissions mrp = extractAnnotation(
(MethodInvocationProceedingJoinPoint)jp,
MyRequiresPermissions.class
); try {
if ( !MyShiroHelper.validatePermssion(mrp.value(), mrp.logic()) ) {
throw new Exception("access disallowed");
}
} catch (Exception e) {
throw new Exception("invalid state");
} } @Before("checkRoles()")
public void doCheckRole(JoinPoint jp) throws Exception { // *) 获取对应的注解
MyRequiresRoles mrp = extractAnnotation(
(MethodInvocationProceedingJoinPoint)jp,
MyRequiresRoles.class
); try {
if ( !MyShiroHelper.validateRoles(mrp.value(), mrp.logic()) ) {
throw new Exception("access disallowed");
}
} catch (Exception e) {
throw new Exception("invalid state");
} } // *) 获取注解信息
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);
} }

  这边为了简洁, 对异常类没有做细分处理.

实战:
  那如何集成, 并使用上述的小shiro框架呢?
  首先激活Aspectj, 同时把注解类纳入到spring容器中.

<aop:aspectj-autoproxy proxy-target-class="true"/>
<context:component-scan base-package="com.xxx.myshiro"/> <!-- 切面类所在的package -->

  而在具体的实战中, 可以写个简单的测试RestController类, 来演示下整个流程.

@Controller
@RequestMapping("/")
public class HelloController { @RequestMapping(value="/login", method={RequestMethod.POST, RequestMethod.GET})
@ResponseBody
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 = "/test1", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
@MyRequiresPermissions(value={"blog:write", "blog:update"}, logic=MyLogic.AND)
public String test1() {
return "test1";
} @RequestMapping(value = "/test2", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
@MyRequiresPermissions(value={"blog:write", "blog:update"}, logic=MyLogic.OR)
public String test2() {
return "test2";
} }

  注: 关注下login接口, 这里完成了登陆和授权工作.
  这个小框架, 把用户登陆和权限数据模型的设计获取交给了业务开发者, 只保留了权限校验.

测试:
  在完成登陆login之后
  1. 调用/test1, 访问受限(需要blog:write && blog:update, 但是只有blog:write权限)
  2. 调用/test2, 访问通过(需要blog:write || blog:update, 有blog:write权限)

总结:
  在抛离认证和账号管理之后, 其实这个权限校验小框架, 其实非常的漂亮和实用. 真正在业务开发时, 遇到小的项目, 其实可以直接使用, 而并非要选择较重的shiro.
  当然在权限校验这块, 都是针对静态资源的权限校验, 若遇到动态权限校验(比如用户对自己编辑的文章有编辑权限, 其他用户没), 还是有所不足的.
  不过, 如果有复杂的权限表达式计算需求的, 这个不算什么难点, 可以借助Groovy轻松实现.

类Shiro权限校验框架的设计和实现的更多相关文章

  1. 类Shiro权限校验框架的设计和实现(2)--对复杂权限表达式的支持

    前言: 我看了下shiro好像默认不支持复杂表达式的权限校验, 它需要开发者自己去做些功能扩展的工作. 针对这个问题, 同时也会为了弥补上一篇文章提到的支持复杂表示需求, 特地尝试写一下解决方法. 本 ...

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

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

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

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

  4. SpringBoot整合Apache Shiro权限验证框架

    比较常见的权限框架有两种,一种是Spring Security,另一种是Apache Shiro,两种框架各有优劣,个人感觉Shiro更容易使用,更加灵活,也更符合RABC规则,而且是java官方更推 ...

  5. Shiro权限控制框架

    Subject:主体,可以看到主体可以是任何可以与应用交互的"用户": SecurityManager:相当于SpringMVC中的DispatcherServlet或者Strut ...

  6. Shiro实现用户对动态资源细粒度的权限校验

    前言 在实际系统应用中,普遍存在这样的一种业务场景,需要实现用户对要访问的资源进行动态权限校验. 譬如,在某平台的商家系统中,存在商家.品牌.商品等业务资源.它们之间的关系为:一个商家可以拥有多个品牌 ...

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

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

  8. SpringBoot整合Shiro权限框架实战

    什么是ACL和RBAC ACL Access Control list:访问控制列表 优点:简单易用,开发便捷 缺点:用户和权限直接挂钩,导致在授予时的复杂性,比较分散,不便于管理 例子:常见的文件系 ...

  9. 权限控制框架Shiro简单介绍及配置实例

    Shiro是什么 http://shiro.apache.org/ Apache Shiro是一个非常易用的Java安全框架,它能提供验证.授权.加密和Session控制.Shiro非常轻量级,而且A ...

随机推荐

  1. weblogic10补丁升级与卸载

    1.首先将补丁包解压放在weblogic的utils/bsu/cache_dir文件夹下,如果没有该文件夹,则手动创建. 2.回到bsu目录,执行安装命令 C:\Oracle\Middleware\u ...

  2. 20175312 2018-2019-2 《Java程序设计》第3周学习总结

    20175312 2018-2019-2 <Java程序设计>第3周学习总结 教材学习内容总结 已依照蓝墨云班课的要求完成了第四章的学习,主要的学习渠道是PPT,和书的课后习题. 总结如下 ...

  3. Java使用Sockt进行通信(2)

    使用TCP协议编写Sockt TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的.可靠的.基于字节流的传输层通信协议,由IETF的RFC 793定义. ...

  4. 浅谈String中的==和对象中引用对象类型的==

    @Test public void test02() { StringBuffer sb = new StringBuffer(); sb.append('a'); sb.append(11); Sy ...

  5. 第一个Python窗体程序

    新建ui_MainWindow.py文件 # encoding: UTF-8 # Qt相关和十字光标 from PyQt4.QtGui import * from PyQt4.QtCore impor ...

  6. pipenv使用总结

    一.pipenv默认虚拟环境路径在用户目录下的.\virtualenvs下,不同虚拟环境目录不同,如果要更改为在当前项目根目录下,可以在项目根目录下手动创建.venv目录. 1.linux环境下设置环 ...

  7. 若依项目利用nginx实现负载均衡及保持会话

    记录一下若依项目利用nginx实现负载均衡及保持会话的步骤. 此次作为试验性的测试,为了方便在本地window的环境上实现. 具体步骤: 1.安装两个tomcat8,可以下载一个后,另一个复制即可,下 ...

  8. DNS及DNS有什么作用

    什么是DNS,DNS有什么作用: DNS(Domain Name System,域名系统),万维网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直 ...

  9. jQuery中$(function(){})与(function($){})(jQuery)、$(document).ready(function(){})等的区别详解

    1.(function($) {…})(jQuery); 1).原理: 这实际上是匿名函数,如下: function(arg){…} 这就定义了一个匿名函数,参数为arg 而调用函数时,是在函数后面写 ...

  10. Pyhon中运算符的使用

    1. a & b python中的&延续了C/C++的含义,表示位运算. 例如 3 & 4:3&5:6&7 3 & 4 = (011)2 & ( ...