服务治理-统一白名单
# 一、需求背景
现在有业务A和业务B,其中业务A是新业务,业务B是需要修改的业务:
- 新业务需要多方测试、而且上线后不一定面向所有的用户群体,可能针对部分VIP用户开放接口。
- 老业务有大量的用户沉淀,可能需要针对老用户进行一定条件限制。
为了更好的控制系统风险,需要在代码接口层提供白名单控制,但是如果白名单控制放在业务代码里面,如果后续设计到变动,就会有很多代码的修改操作,所以我们需要开发一个白名单中间件服务系统。
# 二、方案设计
白名单服务属于业务系统开发过程中可重复使用的通用功能,所以需要将这样的功能单独形成一个组件,让需要进行验证的请求,统一经过白名单中间件。
- 使用注解、切面技术实现自定义注解
- 通过SpringBoot对配置文件的处理方式,把白名单配置到配置文件中
- 通过指定字段的入参和白名单配置文件进行匹配
- 通过,允许访问接口
- 不通过,直接截断
# 三、实现方案
# 1、自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DoWhiteList {
String key() default "";
String returnJson() default "";
}
Target(ElementType.METHOD)
:针对的目标,这个注解适用于方法。Retention(RetentionPolicy.RUNTIME)
: 信息会被带到JVM运行时,可以通过反射拿到注解信息。
# 2、白名单配置获取
@ConfigurationProperties("demo.whitelist")
public class WhiteListProperties {
private String users;
public String getUsers() {
return users;
}
public void setUsers(String users) {
this.users = users;
}
}
通过@ConfigurationProperties
注解,就可以在配置文件中读取到我们设定的配置信息。
# 3、白名单加载
@Configuration
@ConditionalOnClass(WhiteListProperties.class)
@EnableConfigurationProperties(WhiteListProperties.class)
public class WhiteListAutoConfigure {
@Bean("whiteListConfig")
@ConditionalOnMissingBean
public String whiteListConfig(WhiteListProperties properties) {
return properties.getUsers();
}
}
- 通过
@Configuration
将这个类指定成一个组件 @ConditionalOnClass
是当WhiteListProperties位于当前类路径上,才会实例化一个类@Bean
是声明为一个Bean,这时候会涉及属性注入,注入的过程会涉及到properties
实例化
# 4、切面实现
@Aspect
@Component
public class DoJoinPoint {
private final Logger logger = LoggerFactory.getLogger(DoJoinPoint.class);
@Resource
private String whiteListConfig;
@Pointcut("@annotation(me.xu.whitelist.annotation.DoWhiteList)")
public void aopPoint() {
}
@Around("aopPoint()")
public Object doRouter(ProceedingJoinPoint point) throws Throwable {
// 获取方法
Method method = getMethod(point);
// 注解
DoWhiteList whiteList = method.getAnnotation(DoWhiteList.class);
// 获取字段值
String keyValue = point.getArgs()[0].toString();
logger.info("middleware whitelist handler method:{} value:{}", method.getName(), keyValue);
if (null == keyValue || "".equals(keyValue)) {
return point.proceed();
}
String[] split = whiteListConfig.split(",");
// 白名单过滤
for (String str : split) {
if (keyValue.equals(str)) {
return point.proceed();
}
}
// 拦截
return returnObject(whiteList, method);
}
// 获取方法
private Method getMethod(JoinPoint point) {
Signature sig = point.getSignature();
MethodSignature methodSignature = (MethodSignature) sig;
return methodSignature.getMethod();
}
// 返回对象
private Object returnObject(DoWhiteList whiteList, Method method) throws IllegalAccessException, InstantiationException {
Class<?> returnType = method.getReturnType();
String returnJson = whiteList.returnJson();
if ("".equals(returnJson)) {
return returnType.newInstance();
}
return JSON.parseObject(returnJson, returnType);
}
}
# 四、案例测试
# 1、依赖引入
创建一个新的SpringBoot项目,然后引入白名单依赖:
<dependency>
<groupId>me.xu</groupId>
<artifactId>spring-whitelist</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
# 2、配置文件配置白名单用户
server:
port: 8081
demo:
whitelist:
users: aaa,111,wxx
# 3、接口方法使用注解
@RestController
public class UserController {
private Logger logger = LoggerFactory.getLogger(UserController.class);
/**
* 通过:http://localhost:8081/api/queryUserInfo?userId=aaa
* 拦截:http://localhost:8081/api/queryUserInfo?userId=123
*/
@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("wxx:" + userId, 19, "地址");
}
}
# 参考
上次更新: 2024/06/29, 15:13:44