1.自定义注解Per
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Per {
String value();
}
2.创建工具类,第三步要用
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.util.ClassUtils;
import org.springframework.web.method.HandlerMethod;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* 类工具类
*/
public class ClassUtil extends ClassUtils {
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
/**
* 获取方法参数信息
*
* @param constructor 构造器
* @param parameterIndex 参数序号
* @return {MethodParameter}
*/
public static MethodParameter getMethodParameter(Constructor<?> constructor, int parameterIndex) {
MethodParameter methodParameter = new SynthesizingMethodParameter(constructor, parameterIndex);
methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER);
return methodParameter;
}
/**
* 获取方法参数信息
*
* @param method 方法
* @param parameterIndex 参数序号
* @return {MethodParameter}
*/
public static MethodParameter getMethodParameter(Method method, int parameterIndex) {
MethodParameter methodParameter = new SynthesizingMethodParameter(method, parameterIndex);
methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER);
return methodParameter;
}
/**
* 获取Annotation
*
* @param method Method
* @param annotationType 注解类
* @param <A> 泛型标记
* @return {Annotation}
*/
public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType) {
Class<?> targetClass = method.getDeclaringClass();
// The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = ClassUtil.getMostSpecificMethod(method, targetClass);
// If we are dealing with method with generic parameters, find the original method.
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
// 先找方法,再找方法上的类
A annotation = AnnotatedElementUtils.findMergedAnnotation(specificMethod, annotationType);
;
if (null != annotation) {
return annotation;
}
// 获取类上面的Annotation,可能包含组合注解,故采用spring的工具类
return AnnotatedElementUtils.findMergedAnnotation(specificMethod.getDeclaringClass(), annotationType);
}
/**
* 获取Annotation
*
* @param handlerMethod HandlerMethod
* @param annotationType 注解类
* @param <A> 泛型标记
* @return {Annotation}
*/
public static <A extends Annotation> A getAnnotation(HandlerMethod handlerMethod, Class<A> annotationType) {
// 先找方法,再找方法上的类
A annotation = handlerMethod.getMethodAnnotation(annotationType);
if (null != annotation) {
return annotation;
}
// 获取类上面的Annotation,可能包含组合注解,故采用spring的工具类
Class<?> beanType = handlerMethod.getBeanType();
return AnnotatedElementUtils.findMergedAnnotation(beanType, annotationType);
}
}
3…创建切面
import com.example.study.annotation.Per;
import com.example.study.util.ClassUtil;
import io.micrometer.common.util.StringUtils;
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.context.ApplicationContext;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.MethodParameter;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
public class PerAspect {
@Autowired
private ApplicationContext applicationContext;
private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
//切点
@Pointcut("@annotation(com.example.study.annotation.Per) || @within(com.example.study.annotation.Per)")
private void cutMethod() {
}
@Around("cutMethod()")
public Object preAuth(ProceedingJoinPoint point) throws Throwable {
if (handleAuth(point)) {
return point.proceed();
}
throw new Exception("200");
}
private boolean handleAuth(ProceedingJoinPoint point) {
MethodSignature ms = point.getSignature() instanceof MethodSignature ? (MethodSignature) point.getSignature() : null;
Method method = ms.getMethod();
// 读取权限注解,优先方法上,没有则读取类
Per preAuth = ClassUtil.getAnnotation(method, Per.class);//hasRole('admin') -》 hasRole(入参)
// 判断表达式
String condition = preAuth.value();
if (StringUtils.isNotBlank(condition)) {
Expression expression = EXPRESSION_PARSER.parseExpression(condition);
// 方法参数值
Object[] args = point.getArgs();
StandardEvaluationContext context = getEvaluationContext(method, args);
//获取解析计算的结果
return expression.getValue(context, Boolean.class);
}
return false;
}
/**
* 获取方法上的参数
*
* @param method 方法
* @param args 变量
* @return {SimpleEvaluationContext}
*/
private StandardEvaluationContext getEvaluationContext(Method method, Object[] args) {
// 初始化Spel表达式上下文,并设置 AuthFun
StandardEvaluationContext context = new StandardEvaluationContext(applicationContext.getBean("ss"));
// 设置表达式支持spring bean
context.setBeanResolver(new BeanFactoryResolver(applicationContext));
for (int i = 0; i < args.length; i++) {
// 读取方法参数
MethodParameter methodParam = ClassUtil.getMethodParameter(method, i);
// 设置方法 参数名和值 为spel变量
context.setVariable(methodParam.getParameterName(), args[i]);
}
return context;
}
}
4.创建PermissionService,这部分完全照抄的若依的框架,删除改改
import org.springframework.stereotype.Service;
@Service("ss")
public class PermissionService
{
/**
* 验证用户是否具备某权限
*
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
public boolean hasPermi(String permission) {
System.out.println(permission+"======================------------------");
return true;
}
public boolean ces(String permission) {
System.out.println(permission+"********************************");
return true;
}
}
5.创建测试controller
//会走hasPermi方法
//打印‘入参的user的name的值======================----------------’
@Per("@ss.hasPermi(#user.name)")
@PostMapping("demoTest2")
public Object demoTest2(@Valid @RequestBody User user){
return user;
}
//会走hasPermi方法
//打印‘入参name的值======================------------------’
@Per("@ss.hasPermi(#name)")
@GetMapping("demoTest3")
public Object demoTest3(String name){
return name;
}
//会走hasPermi方法
//打印‘入参name的值********************************’
//打印‘true======================------------------’
@Per("@ss.hasPermi(@ss.ces(#name))")
@GetMapping("demoTest4")
public Object demoTest4(String name){
return name;
}