spel高级写法,实现权限控制

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;
    }
<think>嗯,用户想了解Spring Cloud中实现权限控制的方法,包括实现方式和示例教程。我需要根据提供的引用内容来组织回答。首先,用户提到的引用[1]提到微服务安全加固,使用Spring Cloud Security进行配置,包括角色和权限定义、授权管理器和SpEL表达式。引用[2]提到用Spring Cloud Gateway作为API网关,结合Security实现统一认证。引用[3]中的代码示例展示了Controller和配置类,可能涉及基本的安全配置。引用[4]提供了Gateway的依赖配置和Nacos服务发现。 用户的问题集中在权限控制实现方法,可能需要分步骤说明,结合代码示例。首先应该介绍核心组件,比如Spring Cloud Security和Gateway的作用。然后分步骤讲解实现方式,比如统一认证、细粒度控制、动态配置等。需要确保每个步骤都有对应的引用支持,比如使用Gateway作为入口点引用[2][4],配置角色和权限引用[1],代码示例引用[3][4]。 可能还需要注意权限控制的层次,比如网关层面的全局控制和各个微服务的细粒度控制。同时,动态配置管理和安全通信也是要点,引用[2]提到Config配置中心,可以在这里提及。示例代码部分需要展示如何配置Security、定义权限规则,以及Gateway的路由配置。最后总结关键点,并生成相关问题,比如不同组件的协作、动态更新权限的方法等。要确保回答结构清晰,步骤明确,引用正确。</think>### Spring Cloud权限控制实现方式及示例 #### 一、核心实现方式 1. **统一认证入口** 使用$Spring\ Cloud\ Gateway$作为API网关统一拦截请求,结合$Spring\ Security$实现身份认证和权限验证[^2][^4]。 ```xml <!--网关依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> ``` 2. **细粒度权限控制** 在微服务内部通过$@PreAuthorize$注解和SpEL表达式实现方法级权限控制[^1]。 ```java @GetMapping("/admin") @PreAuthorize("hasRole('ADMIN')") public String adminEndpoint() { return "Admin Access"; } ``` 3. **动态权限配置** 利用$Spring\ Cloud\ Config$实现权限规则的动态更新,结合Nacos进行配置管理[^2][^4]。 ```yaml # application.yml spring: cloud: gateway: routes: - id: user-service uri: lb://user-service predicates: - Path=/api/users/** filters: - TokenRelay= ``` #### 二、完整实现步骤 1. **基础安全配置** 在启动类添加$@EnableWebSecurity$,配置用户角色和密码[^3]: ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("user").password("{noop}123").roles("USER") .and() .withUser("admin").password("{noop}admin").roles("ADMIN"); } } ``` 2. **网关权限过滤** 在网关层实现JWT令牌验证: ```java public class AuthFilter implements GatewayFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String token = exchange.getRequest().getHeaders().getFirst("Authorization"); // 验证令牌逻辑 if (!validateToken(token)) { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } } ``` 3. **微服务间安全通信** 使用$OAuth2\ Client$实现服务间认证: ```yaml # 服务配置 security: oauth2: client: registration: my-client: client-id: client-id client-secret: client-secret scope: read,write ``` #### 三、关键组件协作架构 $$ \begin{array}{ccc} \text{API Gateway} & \rightarrow & \text{Authentication} \\ (Gateway) & & (Spring Security) \\ \downarrow & & \downarrow \\ \text{Route Filter} & \leftrightarrow & \text{RBAC Config} \\ & \text{Nacos Config Center} & \end{array} $$ #### 四、注意事项 1. 网关层需配置$CORS$规则处理跨域请求 2. 敏感接口应启用$HTTPS$加密传输 3. 权限变更需通过配置中心实时生效
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值