自定义注解动态获取接口参数
需求:
用户访问企业详情时候,校验用户剩余查看次数,剩余次数为0则不能访问详情接口。
实现方案:
自定义注解+切面实现对用户权限的校验。
假设存在如下访问企业详情的接口:
@GetMapping("/getDetail")
// 配合SpEL 表达式 "#id"
@AuthUserPermission(key="#id")
public ResponseData getCompanyDetail(@RequestParam("id") Long id){
// todo someThing
}
自定义注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthUserPermission {
// 用来接受接口上的动态参数
String key();
}
自定义切面:
@Component
@Slf4j
@Aspect
public class AuthUserPermissionAspect {
// 切入点
@Pointcut("@annotation(com.boshiyun.application.anotation.AuthUserPermission)")
public void pointcut(){}
@Around("pointcut()")
public Object around(ProceedingJoinPoint point) {
// 校验用户权限
beforeAround(point);
try {
Object proceed = point.proceed();
return proceed;
} catch (Throwable e) {
throw new RunTimeException(e);
}
}
public void beforeAround(JoinPoint joinPoint){
// 在这获取接口的参数
String id= getId(joinPoint);
if(StringUtils.isBlank(id)){
throw new RunTimeException("参数错误")
}
// 并校验用户权限,如果用户无权限则抛出异常
}
public String getId(JoinPoint joinPoint){
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
AuthUserPermission annotation = method.getAnnotation(AuthUserPermission.class);
if(annotation ==null){
return "";
}
if(StringUtils.isBlank(annotation.key())){
return "";
}
String strValue = generateKeyBySpEL(annotation.key(), joinPoint);
if(StringUtils.isBlank(strValue)){
return "";
}
return strValue ;
}
// 通过SpEL表达式获取到传入的 id
private SpelExpressionParser parserSpel = new SpelExpressionParser();
private DefaultParameterNameDiscoverer parameterNameDiscoverer= new DefaultParameterNameDiscoverer();
public String generateKeyBySpEL(String key, JoinPoint pjp) {
Expression expression = parserSpel.parseExpression(key);
EvaluationContext context = new StandardEvaluationContext();
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
Object[] args = pjp.getArgs();
String[] paramNames = parameterNameDiscoverer.getParameterNames(methodSignature.getMethod());
for(int i = 0 ; i < args.length ; i++) {
context.setVariable(paramNames[i], args[i]);
}
return expression.getValue(context).toString();
}
}
以上获取参数方法亲测可行,如果是其他接口传入的是对象,则接口上注解修改为
@AuthUserPermission(key="#c.id")
public ResponseData getCompanyDetail(Compnay c){
}
当然也可以使用在其切面中直接通过JoinPoint直接 获取到参数,不过应对诸多接口 有些地方 企业id的字段名称为 id 或者 companyId,这样如果使用 JoinPoint 获取参数,我们不知道某个接口企业id对应的是 id,或者 companyId, 所以 通过注解和 sepl 表达式,只需要在接口注解上指定我需要获取的是哪个字段名称对应的值就可以了