微服务架构,使用spring security,通过PreAuthorize注解控制权限,各个服务读取所有的值,存入数据库

思路:

1、首先将此功能写在公共模板,每一个服务都会依赖公共模板,都将获得此功能。
2、其次,利用springboot的ApplicationListener机制,监听ApplicationStartedEvent事件(springboot启动完成),利用spring来读取所有的注解的值,然后解析出对应我们想要的值
3、然后使用FeignClient调用到远程对应的服务
4、存储的时候进行判断,是否已经存在了,如果存在了,就不在添加了
各个项目的架构不一致,核心还是获取到注解的所有值,只需要核心关注第一步的工具类即可

  1. 第一步,封装一个工具类

import io.swagger.annotations.ApiOperation;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;

import java.util.*;

public class PermissionCollectUtils {

   private static final String VALUE = "value";

   public static <T> List<Map<String, Object>> getAll(ResourcePatternResolver resourcePatternResolver, String classpath, Class<T> annotationClass) throws Exception {
       Resource[] resources = resourcePatternResolver.getResources(classpath);
       MetadataReaderFactory metaReader = new CachingMetadataReaderFactory();
       List<Map<String, Object>> rsMap = new ArrayList<>();
       for (Resource r : resources) {
           MetadataReader reader = metaReader.getMetadataReader(r);
           resolveClass(reader, rsMap, annotationClass);
       }
       return rsMap;
   }

   private static <T> void resolveClass(MetadataReader reader, List<Map<String, Object>> maps, Class<T> annotationClass) throws Exception {
       String tagAnnotationClassCanonicalName = annotationClass.getCanonicalName();
       //获取类注解元数据
       AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
       Set<MethodMetadata> annotatedMethods = annotationMetadata.getAnnotatedMethods(tagAnnotationClassCanonicalName);

       for (MethodMetadata annotatedMethod : annotatedMethods) {
           Map<String, String> rsMap = new HashMap<>();
           //获取当前方法中要扫描注解的属性
           Map<String, Object> targetAttr = annotatedMethod.getAnnotationAttributes(tagAnnotationClassCanonicalName);
           // 这里增加了读取ApiOperation注解的值,如果不需要,则不取即可,
           // 同理,也可以获取其他注解的值
           MultiValueMap<String, Object> allAnnotationAttributes = annotatedMethod.getAllAnnotationAttributes(ApiOperation.class.getCanonicalName());
           if(null != allAnnotationAttributes && allAnnotationAttributes.size() > 0){
               targetAttr.put("name", allAnnotationAttributes.getFirst(VALUE));
           }
           maps.add(targetAttr);
       }
   }
  1. 然后在common中添加一个监听器:监听ApplicationStartedEvent事件

import cn.hutool.core.util.StrUtil;
import com.xfr.api.ResourceInfoFeignClient;
import com.xfr.constants.ResourceTypeConst;
import com.xfr.entity.userserver.ResourceInfo;
import com.xfr.utils.PermissionCollectUtils;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Component
public class ApplicationStartedEventListener implements ApplicationListener<ApplicationStartedEvent> {

    @Autowired
    private ResourcePatternResolver resourcePatternResolver;
    @Autowired
    private ResourceInfoFeignClient resourceInfoFeignClient;


    @SneakyThrows
    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {

		// 这里的classpath:写自己项目的控制层包路径即可
        List<Map<String, Object>> all = PermissionCollectUtils.getAll(resourcePatternResolver, "classpath*:com/xxx/controller/**/*.class", PreAuthorize.class);
        // 自行封装自己对应数据库的实体
        List<ResourceInfo> resourceInfoList = new ArrayList<>(all.size());
        all.forEach(item -> {
            ResourceInfo resourceInfo = new ResourceInfo();
            // 这里获取出来的是权限值
            String value = getPreAuthorizeValue((String) item.get("value"));
            resourceInfo.setPermission(value);
            resourceInfo.setResourceType(ResourceTypeConst.INTERFACE);
            // 这里获取的是注解ApiOperation的值,可以忽略这个值
            Object name =  item.get("name");
            resourceInfo.setName(null == name ? value : (String) name);
            resourceInfoList.add(resourceInfo);
        });
        // 停顿两秒,防止服务没有注册到nacos
        Thread.sleep(2000);
        // 通过feign调用存储资源的服务即可
        resourceInfoFeignClient.batchSaveResourceInfo(resourceInfoList);
    }


    private String getPreAuthorizeValue(String value) {
        if(StrUtil.isEmpty(value)){
            return StrUtil.EMPTY;
        }
        return value.substring(value.indexOf("('") + 2, value.indexOf("')"));
    }


  1. FeignClient代码如下

import com.xfr.config.openfeign.OAuth2FeignConfig;
import com.xfr.constants.FeignPrefixConst;
import com.xfr.constants.ServerNameConst;
import com.xfr.entity.userserver.ResourceInfo;
import com.xfr.model.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import java.util.List;
// value为服务的名称
@FeignClient(contextId = "resourceInfoFeignClient", value = "xxx", path = FeignPrefixConst.PREFIX + "/resourceInfo", configuration = OAuth2FeignConfig.class)
public interface ResourceInfoFeignClient {

   @PostMapping("batchSaveResourceInfo")
   R batchSaveResourceInfo(@RequestBody List<ResourceInfo> list);

}
  1. 批量存储的代码太过简单,这里就不贴出来了
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值