SpringBoot 中间件设计和开发【统一白名单控制】

需求背景

在互联网这种多数面向C端用户场景下的产品功能研发完成交付后,通常并不会直接发布上线。尤其是在一个原有服务功能已经沉淀了大量用户时,不断的迭代开发新增需求下,更不会贸然发布上线。

虽然在测试环境、预发环境都有了相应功能的验证,但在真实的用户场景下可能还会存在其他隐患问题。那么为了更好的控制系统风险,通常需要研发人员在代码的接口层,提供白名单控制。上线初期先提供可配置的白名单用户进行访问验证,控制整体的交付风险程度。

方案设计

大体流程图

技术实现

自动配置类和类属性

自动配置类

@Configuration
@ConditionalOnClass(WhiteListProperties.class)
@EnableConfigurationProperties(WhiteListProperties.class)
public class WhiteListAutoConfigure {

    private final Logger logger = LoggerFactory.getLogger(WhiteListAutoConfigure.class);

    @Bean("whiteListConfig")
    @ConditionalOnMissingBean
    public String whiteListConfig(WhiteListProperties properties){
        return properties.getUsers();
    }
}

自动配置属性类用来读取加载了本工程的application.yml中配置的属性信息

@ConfigurationProperties(prefix = "whitelist")
public class WhiteListProperties {

    private String users;

    public String getUsers() {
        return users;
    }

    public void setUsers(String users){
        this.users = users;
    }
}

使用时可以配置application.yml为

whitelist:
	users: user001,user002

自定义接口

用于标识哪些接口需要进行白白名单过滤

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface DoWhiteList {
    // 配置当前接口入参需要提取的属性
    String key() default "";

    // 拦截到用户请求后需要给一个返回信息。
    String returnJson() default "";
}

定义切面

package com.rzp.middleware.whitelist;
import com.alibaba.fastjson.JSON;
import com.rzp.middleware.whitelist.annotation.DoWhiteList;
import org.apache.commons.beanutils.BeanUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;

@Aspect
@Component
public class DoJoinPoint {

    private Logger logger = LoggerFactory.getLogger(DoJoinPoint.class);

    // 获取自动配置类中配置的白名单用户id信息
    @Resource
    private String whiteListConfig;

    // 对应的注解切点
    @Pointcut("@annotation(com.rzp.middleware.whitelist.annotation.DoWhiteList)")
    public void aopPoint(){

    }

    @Around("aopPoint()")
    public Object doRouter(ProceedingJoinPoint jp) throws Throwable{
        Method method = getMethod(jp);
        DoWhiteList whiteList = method.getAnnotation(DoWhiteList.class);

        // 拿到注解标注的key对应的值
        String keyValue = getFiledValue(whiteList.key(), jp.getArgs());
        logger.info("middleware whitelist handler method:{} value:{}", method.getName(), keyValue);
        // 如果为空 证明不需要校验直接执行..
        if (null == keyValue || "".equals(keyValue)) return jp.proceed();

        // 拿到白名单用户群体
        String[] split = whiteListConfig.split(",");
        // 白名单过滤
        for (String str : split) {
            if (keyValue.equals(str)) {
                return jp.proceed();
            }
        }
        // 白名单没有当前用户id,返回一个json数据
        return returnObject(whiteList, method);
    }

    private Object returnObject(DoWhiteList whiteList, Method method) throws InstantiationException, IllegalAccessException {
        String returnJson = whiteList.returnJson();
        Class<?> returnType = method.getReturnType();
        if ("".equals(returnJson)) {
            return returnType.newInstance();
        }
        return JSON.parseObject(returnJson, returnType);
    }

    // 拿到当前加了注解的接口方法
    private Method getMethod(JoinPoint jp) throws NoSuchMethodException {
        Signature sig = jp.getSignature();
        MethodSignature methodSignature = (MethodSignature) sig;
        return jp.getTarget().getClass().getMethod(methodSignature.getName(),methodSignature.getParameterTypes());
    }

    // 获取属性值   field代表要获取的字段值, args代表接口的全部字段
    private String getFiledValue(String filed, Object[] args) {
        String filedValue = null;
        for (Object arg : args) {
            try {
                // 一个一个去找到对应的属性值
                if (null == filedValue || "".equals(filedValue)) {
                    filedValue = BeanUtils.getProperty(arg, filed);
                }else{
                    break;
                }
            }catch (Exception e){
                if(args.length == 1){
                    return args[0].toString();
                }
            }
        }
        return filedValue;
    }
}

测试验证

Controller编写

@RestController
public class UserController {
    private Logger logger = LoggerFactory.getLogger(UserController.class);

    @DoWhiteList(key = "userId", returnJson = "{\"code\":\"1111\",\"info\":\"非白名单可访问用户拦截!\"}")
    @RequestMapping(path = "/api/queryUserInfo", method = RequestMethod.GET)
    public UserInfo queryUserInfo(@RequestParam String userId) {
        logger.info("查询用户信息,userId:{}", userId);
        return new UserInfo("小新:" + userId, 19, "上海大别野");
    } 
}

application.yml配置

server:
  port: 8081

whitelist:
  users: user001,user002

测试结果

验证结构
(git地址: https://gitcode.net/weixin_44794897/whitelist-project/)

不足之处

可以在中间件中添加一种注册中心来配置白名单,这样就可以在不重启服务的情况下动态变化白名单列表了。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值