JavaWeb中的权限控制——Spring AOP

  • Comments Off on JavaWeb中的权限控制——Spring AOP
  • 15 views
  • A+

一、前言

  在实际开发场景中经常需要解决接口权限鉴别问题,能处理的方式有很多种,例如可以直接在controller中和service调用中识别用户身份,或者根据数据库返回的entity判断对应的creator是否为当前user,也可以使用拦截器interceptor配置相关接口的访问权限,也可以使用shiro以及Spring Security等框架来实现权限。可以参考

  在审计过程中,有时候会发现明明Controller/service中没有对应的鉴权措施,也没有使用上述的鉴权方法,但是实际上访问时有对应的安全检查。那么很可能是采用了AOP的方式来实现权限控制。

二、Spring AOP

  AOP(Aspect Oriented Programming)是spring框架中的重要特性 ,意思是面向切面编程。在系统开发中可以提取出共性的东西作为一个Aspect切面。比如日志打印,判断用户是否已登录,判断页面的读写权限等等。

spring aop | Will

2.1 AOP相关内容

  最常见的是通过注解方式来实现,主要包含以下注解:

  • @Aspect : 指定切面类(可以通过检索@Aspect快速定位切面类)
  • @Pointcut :定义了相应的 Advice(具体要做的操作)触发的地方。一般是通过通配符、正则表达式等方式。

  例如如下的案例,代码中只要被@Auth注解标记的方法均会执行当前切面定义的内容:

java
@Pointcut("@annotation(Auth)")
public void requirePermissionAuth() {

  • 通知(Advice)类型:定义要执行的方法,安全中一般是鉴权

  • @Before: 在目标方法执行之前,执行注解标记的内容

  • @After: 在目标方法执行之后,执行注解标记的内容
  • @AfterReturning: 在目标方法返回后,执行注解标记的内容
  • @AfterThrowing: 在目标方法抛出异常后,执行注解标记的内容
  • @Around: 在目标方法执行前后,分别执行对应的内容

  在权限校验中,比较常用的是@Before和@Around。例如@Around,在判断权限之后选择对应的函数是否执行。如果权限满足,那么执行函数,如果不满足直接抛出权限不足的提示。

  SpringMVC还可以通过注入bean使用xml的方式进行配置。bean就是自定义的切面类,然后在<aop:config>配置AOP。<aop:pointcut>定义切面范围。然后对应的注解例如@Before对应xml里相应的节点<aop:before>。

2.2 定位AOP

  • 注解:通过检索@Aspect关键字进行定位。也可以直接检索@Before和@Around,毕竟一般鉴权方法都执行在在业务方法之前。
  • xml配置:通过bean id确定AOP的class,然后同样的通过before和around关键字定位对应的鉴权方法,例如如下例子:

xml
<!-- 配置切面的Bean -->
<bean id="sysAspect" class="com.example.aop.SysAspect"/>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切点表达式:定义生效的范围 -->
<aop:pointcut id="pointcut" expression="execution(public * com.example.controller.*Controller.*(..))"/>
<!-- 配置切面及配置 -->
<aop:aspect ref="sysAspect">
<!-- 前置通知 -->
<aop:before method="beforMethod" pointcut-ref="pointcut" />
<aop:around method="aroundMethod" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>

2.3 相关案例

  审计要点: 主要是鉴权逻辑以及漏网之鱼(需要权限控制的接口但是没有使用aop进行处理)

  例如下面的例子,通过SpringAOP和自定义注解来实现权限控制:

  首先自定义注解@Authorization:

java
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)//- 表示当前注解运行时有效,如果需要配合反射使用,必须是Runtime范围
public @interface Authorization {
String[] menuList() default {};
}

  然后通过创建切面,来实现相关的鉴权操作,在@Pointcut里通过@annotation来配置切点,代表我们的AOP切面会切到所有用@Authorization注解修饰的方法,最后使用@Before前置通知在被注解的方法前后执行一些鉴权代码:

```java
@Component
@Aspect
public class PermissionAspect {

@Pointcut("@annotation(Authorization)")
public void pointCut() {

}

@Before("pointCut()")
public void doBefore(JoinPoint joinPoint) {

    // 1. 防止非登陆访问
    String UserId = UserUtils.getUserId();
    if (UserId == null) {
        throw new AuthException("获取用户信息失败,不能查询数据!");
    }

    List roles = UserUtils.getUserInfo().getRoles();

    if (CollectionUtils.isEmpty(roles)) {
        throw new AuthException("没有查询该数据的权限,请联系管理员处理!");
    }

    ......

    }
}

}
```

  最后在需要权限控制的controller函数上使用注解即可完成对应的鉴权操作:

java
@Authorization
@GetMapping
public List<Info> getDetails(String id){
......
}

  这里补充一点,也可以直接在Advice处直接指定生效的范围。例如@Around("@annotation(@Authorization)"),它的意思是,符合条件的目标方法是带有@Authorization注解的方法。

三、与其他方式的区别

  类似SpringSecurity/shiro框架一般用于较为复杂的认证和权限控制场景,例如一般的后台管理系统中只有管理员,前端,匿名用户三类角色,相对比使用框架,自己实现一个权限控制流程会更加的便捷。同时AOP区别于直接在service和controller上直接编写鉴权方法,AOP的方式更利于与业务代码进行区分,更简洁。

  相比于interceptor/filter的方式,例如下面的例子:

  interceptor配置简单,这里添加了相关的权限白名单和需要鉴权的接口,但是如果接口很多,并且每个接口都需要根据不同角色进行权限划分的时候,整个拦截器配置就会显得十分复杂。AOP能比较灵活的解决这个问题。个人感觉拦截器/过滤器在授权登陆(例如非登陆用户无法访问系统资源)上会使用的更多一点

```java

@Configuration
public class MVCConfig extends WebMvcConfigurerAdapter {
@Bean
public SecurityInterceptor securityInterceptor() {
return new SecurityInterceptor();
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(securityInterceptor()).excludePathPatterns("/static/*")
            .excludePathPatterns("/error").addPathPatterns("/**");
}

```

相关推荐: AoiAWD 系统的安装和配置

最近打了几次AWD比赛,发现手中的工具依然不好用,在github上发现了AoiAWD,AoiAWD 是一个由Aodzip(安恒信息 海特实验室研究员、HAC战队成员)维护的一个针对于CTF AWD模式的开源项目。专为比赛设计,便携性好,低权限运行的EDR系统。…