专栏文章 生产环境过滤测试账号--解决中

飞狐 · 2018年12月06日 · 最后由 飞狐 回复于 2018年12月10日 · 446 次阅读

引言:

从天美的王者荣耀引发的测试数据展示到生产环境问题,及目前很多实际情况,需要测试在生产环境进行验证,如何把测试账号的数据从生产环境上过滤掉,但测试又可以在线上进行验证,这确实是个比较头疼的事情,我司也存在类似问题,我这边在进行预演,先说下预演情况,实际还是未真正解决(主要是希望优雅的解决)

方案一--AOP切面

策略

采用AOP切面方式,把对应的DAO层文件做切面,把返回结果中过滤掉测试账号ID

优势

可以指定特定DAO文件及特定方法进行处理

劣势

编写规则需要规范

预演代码

package com.finger.test.common.net;

import com.finger.test.pojo.UserAccountDO;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
* @Des: mybatis结果集拦截器
* @Auther: 飞狐
* @Date: 2018/12/6
*/


public class SelectResultInterceptor implements MethodInterceptor {

@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable{

String className = methodInvocation.getClass().getName();
String methodName = methodInvocation.getMethod().getName();

Object result = methodInvocation.proceed();
if(className.equals("UserAccountDOMapper")){
if(methodName.startsWith("select")){

UserAccountDO userAccountDO = (UserAccountDO) result;

System.out.println(userAccountDO.getAccountNo());
}
}

return result;
}
}

拦截器XML配置

<bean id = "selectResultInterceptor" class="com.finger.test.common.net.SelectResultInterceptor" />
<aop:config>
<aop:pointcut id = "
selectResultInterceptorCut" expression="execution(* com.finger.test.dao.*Mapper.*(..))" />
<aop:advisor pointcut-ref="
selectResultInterceptorCut" advice-ref="selectResultInterceptor"/>
</aop:config>

方案二--mybatis拦截器方案

策略

采用mybatis拦截器方案,针对mybatis底层query方法的返回结果进行拦截,过滤测试账号

优势

不区分DAO的命名规范,可随意定义

预演代码

package com.finger.test.common.net;

import com.finger.test.pojo.UserAccountDO;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.util.ArrayList;
import java.util.Properties;

/**
* @Des:
* @Auther: 飞狐
* @Date: 2018/12/6
*/

@Intercepts({ @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class }) })
public class InterceptorQuery implements Interceptor {

@Override
public Object intercept(Invocation invocation) throws Throwable{

MappedStatement statement = (MappedStatement)invocation.getArgs()[0];
// statement.getStatementType().
// System.out.println(statement.getSqlSource());


Object result = invocation.proceed();

if(result instanceof ArrayList){
ArrayList resultList = (ArrayList) result;

for(int i = 0; i < resultList.size(); i++){
if(resultList.get(i) instanceof UserAccountDO){
UserAccountDO userAccountDO = (UserAccountDO) resultList.get(i);
if(userAccountDO.getUserId().equals(16904380000L)){
result = null;
}
}
}

}

return result;
}

public Object plugin(Object target){
return Plugin.wrap(target, this);
}

public void setProperties(Properties properties){

}
}

mybatis-config XML配置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

<!-- 全局配置参数,需要时再设置 -->
<settings>
<!--&lt;!&ndash; 打开延迟加载 的开关 &ndash;&gt;-->
<!--<setting name="lazyLoadingEnabled" value="true"/>-->
<!-- 将积极加载改为消极加载即按需要加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 开启二级缓存 默认也是开启的-->
<setting name="cacheEnabled" value="true"/>
</settings>

<plugins>
<plugin interceptor="com.finger.test.common.net.InterceptorQuery"></plugin>
</plugins>

</configuration>

方案三--自定义注解形式处理

说明

在对应的DAO层添加注解,在执行sql语句前,在注解层添加过滤测试账号的数据到sql,生成新的sql语句进行执行

目前暂未预演此代码

方案四--最不优雅的方式

说明

把测试账号统一写入到redis,业务层把原账号ID输入到该方法中,返回过滤结果的ID列表

弊端

不用说,业务调用的地方太多,代码看起来非常冗余

综合考虑以上四种方式

还是无法解决,部分接口,可能是不想过滤测试账号,部分接口又是要过滤测试账号,又想代码优雅的情况

  1. 前面2种是一刀切方案,但好处是无感知;
  2. 第三种方案可能会让开发重新写一个一摸一样的DAO层方法,从而区别是否过滤测试账号,在已有的业务上增加,也是较大的工作量
  3. 第四种方案就不多说了,谁都看到是最笨的方案了

也想知道下其他同学有没好的方案建议

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 10 条回复 时间 点赞

拦截+黑白名单,黑白名单就是一个列表,放哪里都可以,主要是你开心😃

bauul 回复

陈大大,来点细化思路?如何保证我这边在客户端部分接口要封杀?后台部分不做封杀?

飞狐 回复

周末一起吃饭,慢慢探讨

bauul 回复

没时间,最近连续一个多月10点多回去,圣诞节前太忙了

造测试账号的时候 就打测试标,然后在某一层,根据打标判断下呗。

恒温 回复

阿里童鞋做法,嗯,我去看下

app可以用灰度包,发版的时候灰度包过了,再发生产。
生产环境过滤测试账号,不符合环境分离原则,公司稽查会报这个风险。

KK 回复

可能每个公司的情况不太一样,我们这边有时候一些测试是需要在生产环境做一定的验证,但这些账号希望对用户而言是 屏蔽的,对我们自己后台可以,是可见,可过滤的,等同测试账号要在部分接口完全屏蔽,部分接口非完全屏蔽

我们目前是用的灰/黑名单策略,进行线上功能测试验证,后续有一个小的job,定时根据打的测试标签,清理线上测试数据。

飞狐 #10 · 2018年12月10日 作者
Joo 回复

你讲的是清数据一块,也是很赞的思路,那你们的黑灰名单除了数据清理作用以外,在部分接口是对用户不可见的吗?实现测试是业务层要手动判断一层黑白名单吗

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册