前后端分离的开发中,用户http请求应用服务的接口时, 如果要求检测该用户是否已登录。可以实现的方法有多种, 本示例是通过aop 的方式实现,简单有效。
约定:前端http的post 请求
export async function request(url,data) {
const config = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
}
//每个请求的参数要求附加sessionid, 该sessionid 是登录时生成的
const paramsData = Object.assign(data,{sessionid:globalData.sessionID || ''})
config.body = JSON.stringify(paramsData)
try {
const res = await window.fetch(url, config)
if(res.status!==200){
return {
status: res.status,
data:{},
headers: res.headers,
url: res.url,
statusText:res.statusText
}
}
return {
status: res.status,
data:await res.json(),
headers: res.headers,
url: res.url,
}
} catch (err) {
return {
status: 404,
data:{},
headers: res.headers,
url: res.url,
statusText:'fetch error:'+err.toString()
}
}
}
1、在pom.xml 引用
org.springframework.boot
spring-boot-starter-aop
2、创建插入标记
@Target({ElementType.METHOD}) // 只在对象方法上标记
@Retention(RetentionPolicy.RUNTIME) //运行时反射
public @interface Interceptor {
String additionalMessage() default "";
}
3、实现切入类
@Aspect
@Component
@Slf4j
public class LoggingAspect {
@Autowired
public StringRedisTemplate redisTemplatelocate;
private T getSessionID(Object postData,Class clazz){
return (T)postData;
}
@Around("@annotation(Interceptor)") //有标记的地方将实现以下和切入
public Object logExecutionTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//获取切入方法的参数,就是前部请求的json数据
Object[] args = proceedingJoinPoint.getArgs();
//获取其中的sessionid
// requestBase 实体类只有一个参sessionid , 做为其它实体类的父类,用于接收接口上传的参数。
RequestBase requestBase=getSessionID(args[0],RequestBase.class);
log.info("sessionid:{}",requestBase.getSessionid());
//检测该sessionid 是否存在(redis)
if (requestBase.getSessionid()==null || !redisTemplatelocate.hasKey(requestBase.getSessionid())) {
//用户未登陆
throw new Exception("用户未登陆");
}
//获取 request 和 response
ServletRequestAttributes servletRequestAttributes = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes());
log.info("request:{}",servletRequestAttributes.getRequest());
log.info("response:{}",servletRequestAttributes.getResponse());
MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
String className = methodSignature.getDeclaringType().getSimpleName();
String methodName = methodSignature.getMethod().getName();
Instant startTime = Instant.now();
//实行被切入的方法
Object result = proceedingJoinPoint.proceed();
String additionalMessage = methodSignature.getMethod().getAnnotation(Interceptor.class).additionalMessage();
long elapsedTime = Duration.between(startTime, Instant.now()).toMillis();
log.info("Class Name: {}, Method Name: {}, Additional Message: {}, Elapsed Time: {}ms",
className, methodName, additionalMessage, elapsedTime);
log.info("Result: {}", result);
return result;
}
}
4 建立api接口,在需要检测的方法上加入@Interceptor 就完成切入的检测。
@RestController
@Slf4j
public class ExampleController {
@PostMapping("/t1")
@Interceptor(additionalMessage = "要求检测登录")
@ResponseBody
public ResponseEntity getData(@RequestBody DataRequest req) {
try {
return new ResponseEntity<>(req, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
}
}
}
5 实体类
@Data
public class DataRequest extends RequestBase {
private String name;
}
@Data
public class RequestBase{
private String sessionid;
}
该方法只适用于少部分需要检测,而大部份不需要检测的情况下,如果整个包都需要检测的,利用execution方 法实现
@Pointcut("execution(public * com.example.myapp..*.*(..))")
@Aspect
@Component
@Slf4j
public class LoginExecution {
@Autowired
public StringRedisTemplate redisTemplatelocate;
private T getSessionID(Object postData,Class clazz){
return (T)postData;
}
//切入点: com.aop.ttt 下的所有public 方法
@Pointcut("execution(public * com.aop.ttt..*.*(..))")
public void publicMethods() {}
@Around("publicMethods()")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
//获取切入方法的参数,就是前部请求的json数据
Object[] args = joinPoint.getArgs();
//获取其中的sessionid
// requestBase 实体类只有一个参sessionid , 做为其它实体类的父类,用于接收接口上传的参数。
RequestBase requestBase=getSessionID(args[0],RequestBase.class);
log.info("sessionid:{}",requestBase.getSessionid());
//检测该sessionid 是否存在(redis)
if (requestBase.getSessionid()==null || !redisTemplatelocate.hasKey(requestBase.getSessionid())) {
//用户未登陆
throw new Exception("用户未登陆");
}
//获取 request 和 response
ServletRequestAttributes servletRequestAttributes = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes());
log.info("request:{}",servletRequestAttributes.getRequest());
log.info("response:{}",servletRequestAttributes.getResponse());
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String className = methodSignature.getDeclaringType().getSimpleName();
String methodName = methodSignature.getMethod().getName();
Instant startTime = Instant.now();
//实行被切入的方法
Object result = joinPoint.proceed();
long elapsedTime = Duration.between(startTime, Instant.now()).toMillis();
log.info("Class Name: {}, Method Name: {}, Elapsed Time: {}ms",
className, methodName, elapsedTime);
log.info("Result: {}", result);
return result;
}
}