生活随意,笔记随记,随意参考,多多指正!
1、shiro安全框架大家都很了解,很轻便很好用,但是不管是重量级的还是轻量级的安全框架,主要是根据项目和实际场景来选择最合适的,不用无脑的去选一个最牛逼最强大的,就比如有一句话:最适合自己的才是最好的
2、最近一个项目中,由于需要写一个权限管理系统服务,这个不用像以往独立项目那样需要全方位做好防御措施,毕竟做过微服务的人都应该清楚,每一个服务各司其职,做好自己的本职工作即可,外界的请求统一交给gateway网关去管理
3、目前这个系统,我只是需要一个接口权限控制,当时想着引入shiro,后面觉得没必要,因为自身业务场景的原因,大部分shiro框架的东西是用不上的,为了用它其中某一个去把它搞出来没必要。于是就想到了JAVA AOP面向切面这一层的原理去实现,我目前只想通过权限码控制接口,单个或多个。
4、首先自定义一个注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PersmissionCode {
String[] value();
/**
* 默认为OR,多个值最少需要有一个符合
* 可选AND,多个值需要同时符合
*/
Logical logical() default Logical.OR;
}
@Target({ElementType.METHOD})说明了Annotation所修饰的对象范围,这里类型很多,不了解的就自行百度了,我只是需要用到接口上,所以用了method用于描述方法
@Retention(RetentionPolicy.RUNTIME) 这个是注解的生命周期定义,我需要在运行时去获取注解,所以用的runtime,用这个注解的时候主要看各自业务场景
以上自定义注解类里面value是一个字符串数组,因为我这次要通过权限码控制接口,这个是前后端定义好了的,所以就没去纠结别的方法,就用这个了。
而Logical是个枚举,之所以取这个名不光是它的翻译,更多的是向shiro看齐,有兴趣的可以去看看shiro的实现源码,Logical里面枚举类型为 AND 和 OR
5、现在用@Aspect和@Component做一个切面,类名看自己定义取名
@Aspect
@Component
public class AnnotationAspect {
@Pointcut("@annotation(com.xxx.PersmissionCode)")
public void permissionCodeAspect() {
/*切面*/
}
@Before("permissionCodeAspect()")
public void doBefore(JoinPoint joinPoint) {
....
}
}
@Pointcut 可以取了解一下这个注解,用来定义切点范围或者精确指定某一个切入点的,顾名思义,就是指定从哪里切入进去
@Before 这个注解也可以了解一下,这个表明多久开始执行这个切入的动作
Advice通知增强的相关注解:
@Before 在切点方法之前执行
@After 在切点方法之后执行
@AfterReturning 切点方法返回后执行
@AfterThrowing 切点方法抛异常执行
@Around 属于环绕增强,能控制切点执行前,执行后,,用这个注解后,程序抛异常,会影响@AfterThrowing这个注解
6、切面里面Advice我用before也是因为我这只需要用到before
对于@Pointcut定义的范围,我是直接以我自定义的注解名称当作切入点的,没必要对所有接口进行切入,只切有需要的部分。
7、除了@annotation指定注解,还有一种写法是定义范围,用到execution,比如execution(public * com. admin.abc.controller.*(…)) 表示匹配com. admin.abc.controller下面所有的公有方法,还有很多其他表达式写法,可以去百度了解一下。
8、在before方法里面,我们将从joinPoint里面拿到请求方法的注解,以下这个写法比较常见通用,从method当中就可以拿到注解,以及注解的内容
Object target = joinPoint.getTarget();
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = null;
try {
method = target.getClass().getMethod(signature.getName(),signature.getMethod().getParameterTypes());
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
9、这个isAnnotationPresent是判断注解是否存在的方法,当拿到method后可以拿来判断,在这里可以不用,因为切点就是从指定注解进去的,如果定义的切点是controller层所有接口,那么这个得判断一下,有指定注解的再做相应处理。还是那句话,结合项目实际场景,选择最适合的方法,没有最好的处理方法,只有适合的和更适合的!
boolean annotationPresent = method.isAnnotationPresent(PersmissionCode.class);
可以通过以下方式拿到注解的值
PersmissionCode persmissionCode = method.getAnnotation(PersmissionCode.class);
/*
*persmissionCode .value() 和 persmissionCode.logical()便是拿到的值了
*/
10、最后,加上了权限注解的相关接口拦截到了,注解值也能获取,剩下的就是看自己需要怎么去判断是否匹配权限了。比如我会拿当前用户的权限code与给接口加上的权限code作比较。Logical 在这里也能派上用场,和shiro一样,一个接口的权限注解定义了多个值,如果是AND,那么用户必须匹配上接口定义的所有权限才能访问接口,如果是OR,满足其中一个code即可访问接口。