- 前言
- 实现登录状态检查的方式
- AOP 定义
- AOP 相关概念
- Advice 通知类型
- 具体实现
- 实现代码
- 测试
- 源码
记录下AOP实现登录状态的检查,文章使用的JWT校验参考:SpringBoot - 集成Auth0 JWT
实现登录状态检查的方式
- Servlet过滤器
- 拦截器
- Spring AOP
AOP 定义
AOP(Aspect Oriented Programming),面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术,在程序开发中主要用来解决一些系统层面上的问题,在不改变原有的逻辑的基础上,增加一些额外的功能,如日志,事务,权限等
AOP 相关概念
术语 | 概念 | 描述 |
---|---|---|
Aspect | 切面 | 通常是一个类,可以定义切入点和通知 |
Joint point | 连接点 | 程序执行过程中明确的点,在Spring中指的是被拦截到的方法(Spring只支持方法类型的连接点) |
Advice | 通知 | AOP在特定的切入点上执行的增强处理,有before(前置),after(后置),afterReturning(最终),afterThrowing(异常),around(环绕) |
Pointcut | 切入点 | 带有通知的连接点,在程序中主要体现为书写切入点表达式 |
Target | 目标对象 | 织入 Advice 的目标对象 |
Weaving | 织入 | 将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程 |
Advice 通知类型
类型 | 描述 |
---|---|
Before | 在目标方法被调用之前做增强处理 |
After | 在目标方法完成之后做增强 |
AfterReturning | 在目标方法正常完成后做增强 |
AfterThrowing | 处理程序中未处理的异常 |
Around | 环绕通知,在目标方法完成前后做增强处理,环绕通知是最重要的通知类型 |
具体实现 实现代码
- 自定义注解CheckLogin
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; @Aspect @Component public class CheckLoginAspect { @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; @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; @RestControllerAdvice @Slf4j public class GlobalExceptionAdvice { @ExceptionHandler(ServerErrorException.class) public ResponseEntityhandleServerErrorException(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; @Data @Builder @AllArgsConstructor @NoArgsConstructor public class UnifyMessage { private int code; private String message; }
测试
- 测试方法TestController
@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