在我们的web开发过程中,经常需要用到功能权限校验,验证用户是否有某个角色或者权限,目前有很多框架,如Shiro

Shiro有基于自定义登录界面的版本,也有基于CAS登录的版本,目前我们的系统是基于CAS单点登录,各个公司的单点登录机制略有差异,和Shiro CAS的标准单点登录校验方式也自然略有不同。

在尝试将自定义登录的普通版Shiro改造失败后,在系统登录、校验角色、权限我认为相对简单后,觉得模仿Shiro自己实现一个权限校验小框架,说是框架,其实就是一个aop advisor,几个注解(Shiro不就是这个功能吗)

RequiresRoles和RequiresPermissions相似,不截图了

接下来是实现aop拦截功能了,先来说下注解的用法,和Shiro的注解一样,都是放在class或者method上

上述配置的意思是需要角色user和admin,需要权限home:*,上面仅是一个例子,实际业务中的角色已经确定有哪些权限了。

以下是aop拦截方式

/**
* 权限校验advisor,负责对RequiresRoles、RequiresPermissions标记的controller进行权限校验
* 他执行的时机是interceptor之后、方法执行之前
* Created by Administrator on 2015/2/9.
*/
@Aspect
@Component
public class AuthExecuteAdvisor implements PointcutAdvisor, Pointcut { private static final Logger logger = LoggerFactory.getLogger(AuthExecuteAdvisor.class); @Autowired
private UserService userService; /**
* 生成一个始终匹配class的Filter对象,任何类都可以通过这个过滤器
*/
private static final ClassFilter TrueClassFilter = new ClassFilter() {
@Override
public boolean matches(Class<?> clazz) {
return true;
}
}; /**
* 生成根据特定注解进行过滤的方法匹配器
* 如果class上有注解、或方法上有注解、或接口方法上有注解,都能通过
*/
private MethodMatcher annotationMethodMatcher = new StaticMethodMatcher() {
@Override
public boolean matches(Method method, Class<?> targetClass) { if (targetClass.isAnnotationPresent(RequiresRoles.class) || targetClass.isAnnotationPresent(RequiresPermissions.class)) {
return true;
} if (method.isAnnotationPresent(RequiresRoles.class) || method.isAnnotationPresent(RequiresPermissions.class)) {
return true;
}
// The method may be on an interface, so let's check on the target class as well.
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); return (specificMethod != method && (specificMethod.isAnnotationPresent(RequiresRoles.class)
|| specificMethod.isAnnotationPresent(RequiresPermissions.class)));
} }; @Override
public ClassFilter getClassFilter() {
//只执行注解的类
return TrueClassFilter;
} @Override
public MethodMatcher getMethodMatcher() {
return annotationMethodMatcher;
} /**
* 当前对象就是切面对象,根据classFilter、MethodFilter定义执行范围
*
* @return
*/
@Override
public Pointcut getPointcut() {
return this;
} /**
* 切面对应的处理策略
*
* @return
*/
@Override
public Advice getAdvice() {
//这个MethodInterceptor相当于MethodBeforeAdvice
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable { LoginContext loginContext = ActionContext.getLoginContext();
if (loginContext == null) {
throw new AuthFailException("校验用户权限失败,用户未登录");
} // TODO: 2017/11/21 管理员全权限
if (loginContext.getUserType().equals(0)) {
return invocation.proceed();
} Set<String> alreadyRoles = new HashSet<>(userService.getRolesByUserId(loginContext.getUserId()));
Set<String> alreadyPermissions = new HashSet<>(userService.getPermissionByUserId(loginContext.getUserId())); List<String> requireRole = getRequiredRole(invocation);
List<String> requirePermission = getRequiredPermission(invocation); if (!CollectionUtils.isEmpty(requireRole) && !alreadyRoles.containsAll(requireRole)) {
//role权限不足
logger.error("校验用户权限失败,role角色不足"); throw new AuthFailException("校验用户权限失败,role角色不足");
} if (!CollectionUtils.isEmpty(requirePermission) && !alreadyPermissions.containsAll(requirePermission)) {
//permission不足
logger.error("校验用户权限失败,permission权限不足");
throw new AuthFailException("校验用户权限失败,权限不足");
} return invocation.proceed();
} };
} /**
* 获取方法需要的角色
*
* @param invocation
* @return
*/
private List<String> getRequiredRole(MethodInvocation invocation) {
List<String> requiredRoles = new ArrayList<>();
Class<?> clazz = invocation.getThis().getClass(); if (clazz.isAnnotationPresent(RequiresRoles.class)) {
Collections.addAll(requiredRoles, clazz.getAnnotation(RequiresRoles.class).value());
} if (invocation.getMethod().isAnnotationPresent(RequiresRoles.class)) {
Collections.addAll(requiredRoles, invocation.getMethod().getAnnotation(RequiresRoles.class).value());
} return requiredRoles;
} /**
* 获取方法需要的权限
*
* @param invocation
* @return
*/
private List<String> getRequiredPermission(MethodInvocation invocation) {
List<String> requirePermission = new ArrayList<>();
Class<?> clazz = invocation.getThis().getClass(); if (clazz.isAnnotationPresent(RequiresPermissions.class)) {
Collections.addAll(requirePermission, clazz.getAnnotation(RequiresPermissions.class).value());
} if (invocation.getMethod().isAnnotationPresent(RequiresPermissions.class)) {
Collections.addAll(requirePermission, invocation.getMethod().getAnnotation(RequiresPermissions.class).value());
} return requirePermission;
} /**
* 每个切面的通知一个实例,或可分享的实例
* 此处使用分享的实例即可,分享的实例在内存中只会创建一个Advice对象
*
* @return
*/
@Override
public boolean isPerInstance() {
return false;
}
}

  

此处主要有以下几个关键点:
当前类既是一个切入点Pointcut(一群方法),也是一个通知持有者Advisor

Pointcut接口通过ClassFilter、MethodMatcher锁定哪些方法会用到Advisor

Advisor接口通过getAdvice获取Pointcut需要进行的拦截操作

 

ClassFilter是一个始终返回True的类过滤器,因为我们拦截执行的最小单位是method,所以即使class上没有注解,也要让他通过拦截
MethodMatcher则会判断method所在的class是否有注解,如果没有,则判断method是否有注解,二者满足其一就能通过校验 Advice的具体拦截过程为:
获取登录上下文
获取当前method需要的权限和角色(这里实际可以再优化,因为method可能只需要Role或Permission其中一种权限)
判断当前用户是否具有这些权限

  

 

开启spring的aop功能,权限拦截开始生效

 

基于Spring Aop实现类似shiro的简单权限校验功能的更多相关文章

  1. 基于spring的quartz定时框架,实现简单的定时任务功能

    在项目中,经常会用到定时任务,这就需要使用quartz框架去进行操作. 今天就把我最近做的个人主页项目里面的定时刷新功能分享一下,很简单. 首先需要配置一个配置文件,因为我是基于spring框架的,所 ...

  2. 基于spring security 实现前后端分离项目权限控制

    前后端分离的项目,前端有菜单(menu),后端有API(backendApi),一个menu对应的页面有N个API接口来支持,本文介绍如何基于spring security实现前后端的同步权限控制. ...

  3. 基于Spring Cloud、JWT 的微服务权限系统设计

    基于Spring Cloud.JWT 的微服务权限系统设计 https://gitee.com/log4j/pig https://github.com/kioyong/spring-cloud-de ...

  4. 基于Spring aop写的一个简单的耗时监控

    前言:毕业后应该有一两年没有好好的更新博客了,回头看看自己这一年,似乎少了太多的沉淀了.让自己做一个爱分享的人,好的知识点拿出来和大家一起分享,一起学习. 背景: 在做项目的时候,大家肯定都遇到对一些 ...

  5. spring aop 使用xml方式的简单总结

    spring aop的 xml的配置方式的简单实现: 1.编写自己的切面类:配置各个通知类型 /** * */ package com.lilin.maven.service.aop; import ...

  6. 基于Spring AOP的JDK动态代理和CGLIB代理

    一.AOP的概念  在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的 ...

  7. 基于Spring AOP实现的权限控制

    1.AOP简介 AOP,面向切面编程,往往被定义为促使软件系统实现关注点的分离的技术.系统是由许多不同的组件所组成的,每一个组件负责一块特定的功能.除了实现自身核心功能之外,这些组件还经常承担着额外的 ...

  8. s2sh框架搭建(基于spring aop)

    对于spring aop 是如何管理事务的,请看一下:http://bbs.csdn.net/topics/290021423 1.applicationContext.xml <?xml ve ...

  9. 利用Spring AOP自定义注解解决日志和签名校验

    转载:http://www.cnblogs.com/shipengzhi/articles/2716004.html 一.需解决的问题 部分API有签名参数(signature),Passport首先 ...

随机推荐

  1. iOS之ViewController的多层presentViewController的dismiss问题

    今天在Q群里有人问了个这么个问题: A,B,C,D 都是Viewcontroller.A.B.C.D使用present去切的 他是这么搞的: A -> B, B ->C, C->D, ...

  2. R语言高性能编程,优化(一)

    这段时间学习了<R高性能编程>这本书,基于这段时间做的项目实践,总结了一些自己的体会,和大家分享 一.为什么R程序有时候会很慢?1.计算性能的三个限制条件 cpu ram io R代码本身 ...

  3. Problem M

    Problem Description Accounting for Computer Machinists (ACM) has sufferred from the Y2K bug and lost ...

  4. 【JAVA零基础入门系列】Day14 Java对象的克隆

    今天要介绍一个概念,对象的克隆.本篇有一定难度,请先做好心理准备.看不懂的话可以多看两遍,还是不懂的话,可以在下方留言,我会看情况进行修改和补充. 克隆,自然就是将对象重新复制一份,那为什么要用克隆呢 ...

  5. css基础语法三

    []伪类选择器] 1.写法: 伪类选择器,在选择器后面,用:分隔,紧接伪类状态: eg : .a:link 2. 超链接的伪类状态: :link - 未访问状态 :visited - 已访问状态 :h ...

  6. 0_Simple__asyncAPI

    关于CPU - GPU交互的简单接口函数. ▶ 源代码: // includes, system #include <stdio.h> // includes CUDA Runtime # ...

  7. .Net 上传图片之前获取图片的宽高

    Stream st = Request.Files[0].InputStream;                  Byte[] buffer = new Byte[st.Length];      ...

  8. 疑似CPU或者内存故障导致进程崩溃

    我们有一个服务跑在微软云的所有宿主机上.最近发现某一台机器上该服务进程持续崩溃.崩溃原因是访问了一个无效指针,对应的代码如下 serviceListIniBuffer.AppendF("Se ...

  9. salesforce零基础学习(八十二)审批邮件获取最终审批人和审批意见

    项目中,审批操作无处不在.配置审批流时,我们有时候会用到queue,related user设置当前步骤的审批人,审批人可以一个或者多个.当审批人有多个时,邮件中获取当前记录的审批人和审批意见就不能随 ...

  10. Python 解决面试题47 不用加减乘除做加法

    在看<剑指Offer>过程中,面试题47不用加减乘除做加法,给出的思路是使用二进制的异或以及与运算,总之就是使用二进制.但是在使用Python实现的过程中,对于正整数是没有问题的,但是对于 ...