前言
做业系统在测试过程当中出现了学生修改路由能够到达教师界面而且可使用教师功能的问题,学生不用任何工具就能够修改本身的成绩,真的挺要命的,这就要用权限管理进行控制了。
因为没有接触过权限管理,因此一开始也是有点懵,后来应用到实践中,发现也还能够吧。java
自定义注解@Admin
若是在实现方式上去描述自定义注解,其实就是接口+注解编程
package club.yunzhi.workhome.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Admin {
String value() default "";
}
@TARGET
- 用于标注这个注解放在什么地方,类上,方法上,构造器上
- ElementType.METHOD 用于描述方法
- ElementType.FIELD 用于描述成员变量,对象,属性(包括enum实例)
- ElementType.LOCAL_VARIABLE 用于描述局部变量
- ElementType.CONSTRUCTOR 用于描述构造器
- ElementType.PACKAGE 用于描述包
- ElementType.PARAMETER 用于描述参数
- ElementType.TYPE 用于描述类,接口,包括(包括注解类型)或enum声明
@Retention
-
用于说明这个注解的生命周期安全
- RetentionPolicy.RUNTIME 始终不会丢弃,运行期也保留该注解。所以可使用反射机制来读取该注解信息。
- 咱们自定义的注解一般用这种方式
- RetentionPolicy.CLASS 在类加载的时候丢弃,在字节码文件的处理中有用。注解默认使用这种方式
- RetentionPolicy.SOURCE 在编译阶段丢弃,这些注解在编译结束后就再也不有任何意义,因此他们不会写入字节码中
- @Override,@SuppressWarnings都属于这类注解。
- 咱们自定义使用中通常使用第一种
- java过程为 编译-加载-运行
@Documented
这样就有了一个自定义注解,可是要想定义它的做用,就须要AOP了。app
AOP
基本概念
- AOP(Aspect Oriented Programming)称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,好比日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子。
- 在不改变原有的逻辑的基础上,增长一些额外的功能。代理也是这个功能,读写分离也能用aop来作。
- AOP能够说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来创建一种对象层次结构,用于模拟公共行为的一个集合。不过OOP容许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码每每横向地散布在全部对象层次中,而与它对应的对象的核心功能毫无关系对于其余类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它致使了大量代码的重复,而不利于各个模块的重用。AOP技术偏偏相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减小系统的重复代码,下降模块之间的耦合度,并有利于将来的可操做性和可维护性。
- 使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特色是,他们常常发生在核心关注点的多处,而各处基本类似,好比权限认证、日志、事物。AOP的做用在于分离系统中的各类关注点,将核心关注点和横切关注点分离开来。
相关概念
- 横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
- Aspect(切面):一般是一个类,里面能够定义切入点和通知
- JointPoint(链接点):程序执行过程当中明确的点,通常是方法的调用。被拦截到的点,由于Spring只支持方法类型的链接点,因此在Spring中链接点指的就是被拦截到的方法,实际上链接点还能够是字段或者构造器
- Advice(通知):AOP在特定的切入点上执行的加强处理,有before(前置),after(后置),afterReturning(最终),afterThrowing(异常),around(环绕)
- Pointcut(切入点):就是带有通知的链接点,在程序中主要体现为书写切入点表达式
- weave(织入):将切面应用到目标对象并致使代理对象建立的过程
- introduction(引入):在不修改代码的前提下,引入能够在运行期为类动态地添加一些方法或字段
- AOP代理(AOP Proxy):AOP框架建立的对象,代理就是目标对象的增强。Spring中的AOP代理可使JDK动态代理,也能够是CGLIB代理,前者基于接口,后者基于子类
- 目标对象(Target Object): 包含链接点的对象。也被称做被通知或被代理对象。
通知类型
- Before:在目标方法被调用以前作加强处理,@Before只须要指定切入点表达式便可
- AfterReturning:在目标方法正常完成后作加强,@AfterReturning除了指定切入点表达式后,还能够指定一个返回值形参名returning,表明目标方法的返回值
- AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还能够指定一个throwing的返回值形参名,能够经过该形参名来访问目标方法中所抛出的异常对象
- After:在目标方法完成以后作加强,不管目标方法时候成功完成。@After能够指定一个切入点表达式
- Around:环绕通知,在目标方法完成先后作加强处理,环绕通知是最重要的通知类型,像事务,日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint
因为初次接触AOP,对部分概念还不太理解,也就不展开解释了,之后有机会再写吧。框架
@Aspect
@Component
public class AdminAspect {
private static final Logger logger = LoggerFactory.getLogger(AdminAspect.class);
@Autowired
WorkService workService;
@Pointcut(value = "@annotation(club.yunzhi.workhome.annotation.Admin)")
public void annotationPointCut() {
}
@Before("annotationPointCut()")
public Object doBefore(ProceedingJoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String methodName = signature.getMethod().getName();
System.out.println("方法名:" + methodName);
if(!validate()){
throw new AccessDeniedException("无操做权限");
}
try {
return joinPoint.proceed();
} catch (Throwable throwable) {
return null;
}
}
private boolean validate(){
System.out.println(this.workService.isTeacher());
return this.workService.isTeacher();
}
}
@Aspect : 将当前类标识为一个切面
@Component :让Spring容器扫描到。
@Pointcut :定义切点
这样一来自定义注解就有了灵魂了,验证到当前角色不是教师,那就抛出异常,不然执行加上注解的方法ide
以编辑学生为例:工具
/**
* 更新学生信息
* @param id
* @param student
*/
@PutMapping("{id}")
@Admin
@JsonView(studentJsonView.class)
public void update(@PathVariable Long id, @RequestBody Student student) {
studentService.update(id, student);
}
教师:

学生:
测试
总结:
一开始感受挺难的,后来实践了才发现还能够,仍是不能眼高手低,认为难的不必定难,总会有解决的办法的。this