package com.demo.common;
import java.lang.annotation.*;
/**
* <p>
* <code>TokenCheck</code>
* </p>
* Description: 校验token注解
* <p>
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TokenCheck {
/** 当前登录人账号 */
String account() default "";
/** 当前登录人token */
String token() default "";
/** 是否触发校验,默认true(true:校验,false:不校验),可以是条件表达式 */
String condition() default "true";
/** 其他信息 */
String extra() default "";
}
package com.demo.common;
import com.demo.service.TokenCheckService;
import com.demo.result.RestResponse;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Objects;
/**
* <p>
* <code>TokenCheckAspect</code>
* </p>
* Description: token校验切面
*/
@Slf4j
@Aspect
@Component
public class TokenCheckAspect {
/** 解析方法 */
private final ExpressionParser parser = new SpelExpressionParser();
private final LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
/** 上下文 */
private final EvaluationContext context = new StandardEvaluationContext();
/** 默认解析模板 */
private final TemplateParserContext templateParserContext = new TemplateParserContext();
@Autowired
private TokenCheckService tokenCheckService;
@Pointcut("@annotation(com.demo.common.TokenCheck)")
public void tokenPointCut() {
}
@Around("tokenPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
try {
Method method = ((MethodSignature) point.getSignature()).getMethod();
Object[] args = point.getArgs();
TokenCheck annotation = method.getAnnotation(TokenCheck.class);
// 获取注解参数
String account = executeTemplate(annotation.account(), method, args);
String token = executeTemplate(annotation.token(), method, args);
String condition = executeTemplate(annotation.condition(), method, args);
String extra = executeTemplate(annotation.extra(), method, args);
// 调用业务接口进行相关校验
boolean check = tokenCheckService.tokenCheck(account, token);
if (check) {
// 校验通过后执行原有逻辑
Object result = point.proceed();
return Objects.isNull(result) ? new RestResponse<>(false, "请求失败") : result;
} else {
return check;
}
} catch (Exception e) {
return new RestResponse<>(false, "非法请求");
}
}
/**
* 解析字符串
*
* @param message 待解析字符串
* @param method 方法
* @param args 方法参数
* @return
*/
private String executeTemplate(String message, Method method, Object[] args) {
String[] params = discoverer.getParameterNames(method);
for (int len = 0; len < Objects.requireNonNull(params).length; len++) {
context.setVariable(params[len], args[len]);
}
return parser.parseExpression(message, templateParserContext).getValue(context, String.class);
}
}
使用方法如下:
1、入参为对象:#{#形参.属性}
@Override
@TokenCheck(account = "#{#input.userAccount}", token = "#{#input.token}", extra = "这是其他信息:#{#input.id}")
public RestResponse<String> aa(InputQuery input) {
// 相关业务逻辑
return null;
}
2、入参为单个字符串:#{#形参}
@Override
@TokenCheck(account = "#{#userAccount}", token = "#{#token}", condition = "'#{#userAccount}'!='' && " + "'#{#userAccount}'!=null")
public RestResponse<String> bb(String userAccount, String token) {
// 相关业务逻辑
return null;
}