前言
记录下AOP
实现登录状态的检查,文章使用的JWT
校验参考:SpringBoot - 集成Auth0 JWT
实现登录状态检查的方式
Servlet
过滤器- 拦截器
Spring AOP
AOP 定义
AOP(Aspect Oriented Programming)
,面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术,在程序开发中主要用来解决一些系统层面上的问题,在不改变原有的逻辑的基础上,增加一些额外的功能,如日志,事务,权限等
AOP 相关概念
术语 | 概念 | 描述 |
---|---|---|
Aspect | 切面 | 通知和切点的集合 |
Joint point | 连接点 | 应用执行过程中能够插入切面的一个点 |
Advice | 通知 | 定义切面是什么以及何时使用 |
Pointcut | 切点 | 定义切面在何处执行,切点的定义会匹配通知所有要织入的连接点 |
Weaving | 织入 | 把切面应用到目标对象并创建新的代理代理对象的过程 |
Advice 通知类型
术语 | 概念 | 描述 |
---|---|---|
Before | 前置通知 | 在目标方法被调用之前调用通知功能 |
After | 后置通知 | 在目标方法完成之后调用,此时不关心方法的输出是什么 |
AfterReturning | 返回通知 | 在目标方法成功执行之后调用通知 |
AfterThrowing | 异常通知 | 在目标方法抛出异常后调用通知 |
Around | 环绕通知 | 通知包裹了通知的方法,在被通知的方法调用之前和调用之后执行自定义行为 |
具体实现
实现代码
- 自定义注解
CheckLogin
/**
* @Description 登录校验注解
* @author coisini
* @date Oct 14, 2021
* @Version 1.0
*/
public @interface CheckLogin {
}
- 切面
CheckLoginAspect
import com.coisini.aop.util.JwtUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/**
* @Description 登录校验切面
* @author coisini
* @date Oct 14, 2021
* @Version 1.0
*/
@Aspect
@Component
public class CheckLoginAspect {
/**
* 只要加了@CheckLogin的方法都会走到这里
* @param point
* @return
*/
@Around("@annotation(com.coisini.aop.auth.annotation.CheckLogin)")
public Object checkLogin(ProceedingJoinPoint point) {
try {
// 从header中获取token
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
HttpServletRequest request = attributes.getRequest();
String token = request.getHeader("token");
// 校验token是否合法
Boolean valid = JwtUtil.verifyToken(token);
if (!valid) {
throw new ServerErrorException(HttpStatus.UNAUTHORIZED.value(), "Token 不合法");
}
// 执行后续的方法
return point.proceed();
} catch (Throwable throwable) {
throw new ServerErrorException(HttpStatus.UNAUTHORIZED.value(), "Token 不合法");
}
}
}
- 自定义异常
ServerErrorException
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* @Description 自定义异常
* @author coisini
* @date Oct 14, 2021
* @Version 1.0
*/
@Data
@AllArgsConstructor
public class ServerErrorException extends RuntimeException{
public Integer code;
public String message;
}
- 统一异常处理
GlobalExceptionAdvice
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* @Description 统一异常处理
* @author coisini
* @date Oct 14, 2021
* @Version 1.0
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionAdvice {
@ExceptionHandler(ServerErrorException.class)
public ResponseEntity<UnifyMessage> handleServerErrorException(ServerErrorException e) {
log.warn("ServerErrorException 异常", e);
return new ResponseEntity<>(
UnifyMessage.builder()
.code(e.getCode())
.message(e.getMessage())
.build(),
HttpStatus.UNAUTHORIZED
);
}
}
- 统一消息返回
UnifyMessage
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Description 统一消息返回
* @author coisini
* @date Oct 14, 2021
* @Version 1.0
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UnifyMessage {
private int code;
private String message;
}
测试
- 测试方法
TestController
/**
* AOP校验请求头中的Token
* @return
*/
@CheckLogin
@GetMapping(value = "/test")
public String testCheckLogin() {
// TODO 业务
return "Token验证通过";
}
- 获取
Token
- 无
Token
测试
- 传递
Token
源码
GitHub
:https://github.com/maggieq8324/java-learn-demo/tree/master/springboot-aop