拦截选型:过滤器、拦截器、ControllerAdvice和AOP
我是采用Aop 只针对部分需要的接口进行进行过滤和拦截
直接上代码
注解 因为想控制 拦截还是过滤
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveAnnotation {
//开启过滤还是拦截 默认开启拦截功能 改为false开启过滤
boolean isStatus() default true;
}
Aop类
@Component
@Aspect
public class SensitiveAspect {
public static final Logger logger = LoggerFactory.getLogger(SensitiveAspect.class);
@Autowired
private SensitiveFilter sensitiveFilter;
/**
* @Description: 环绕通知包含此注解的
* @param: ProceedingJoinPoint joinPoint
* @return: Object
*/
@Around(value = "@annotation("注解地址")")
public Object repeatSub(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = null;
//获取传入对象
Object pointArg = joinPoint.getArgs()[0];
List<Field> fieldList = getFieldList(pointArg.getClass());
//获取拦截状态
boolean status = this.getAnnotation(joinPoint, SensitiveAnnotation.class).isStatus();
boolean isBack = false;
if (!CollectionUtils.isEmpty(fieldList)){
//获取成员变量
for (Field field : fieldList) {
if (field.get(pointArg) != null){
String str = (String) field.get(pointArg);
if (status){
//此处为拦截
isBack = sensitiveFilter.intercept(str);
//发现敏感词结束循环
if (isBack){
break;
}
}else {
//此处为过滤
String filter = sensitiveFilter.filter(str);
//重新写入
field.set(pointArg,filter);
}
}
}
}
if (isBack){
//返回前端,提示信息(自定义响应类)
return RestApiResponse.error("您输入的内容包含敏感词,请重新输入");
}else {
//执行原方法
result = joinPoint.proceed();
return result;
}
}
/**
* @Description: 获取指定注解属性
* @param: ProceedingJoinPoint
* @return: getAnnotation
*/
private <T extends Annotation> T getAnnotation(ProceedingJoinPoint pjp,
Class<T> annotation) {
//1、获取到当前方法的完整签名
MethodSignature signature = (MethodSignature) pjp.getSignature();
//2、拿到当前方法
Method method = signature.getMethod();
//3、拿到方法指定注解
return method.getDeclaredAnnotation(annotation);
}
/**
* @Description: 获取成员属性
* @param: Class<?>
* @return: List<Field>
*/
private List<Field> getFieldList(Class<?> clazz){
if(null == clazz){
return null;
}
List<Field> fieldList = new ArrayList<>();
//递归查找
while (clazz!=null){
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
if (field != null){
//设置属性的可访问性
field.setAccessible(true);
//过滤静态
if(Modifier.isStatic(field.getModifiers())){
continue;
}
//过滤非字符串类型
Class<?> type = field.getType();
if (type.isAssignableFrom(String.class)){
fieldList.add(field);
}
}
}
clazz = clazz.getSuperclass();
}
return fieldList;
}
}
前缀树类
@Component
public class SensitiveFilter {
public static final Logger logger = LoggerFactory.getLogger(SensitiveFilter.class);
// 替换符
private static final String REPLACEMENT = "***";
// 根节点
private TrieNode rootNode = new TrieNode();
@PostConstruct
public void init(){
//初始加载敏感词
try {
//数据源 使用自己定义的
//systemSensitiveWords(数据源)
if (!CollectionUtils.isEmpty(systemSensitiveWords)){
systemSensitiveWords.forEach(words->{
this.addKeyword(words.getKeyWord());
});
}
}catch (Exception e){
logger.error("加载敏感词初始化失败:"+e.getMessage());
}
}
// 将一个敏感词添加到前缀树中
private void addKeyword(String keyword){
TrieNode tempNode = rootNode;
for (int i = 0; i < keyword.length(); i++){
char c = keyword.charAt(i);
TrieNode subNode = tempNode.getSubNode(c);
if (subNode == null){
// 初始化子节点
subNode = new TrieNode();
tempNode.addSubNode(c, subNode);
}
// 指向子节点,进入下一轮循环
tempNode = subNode;
// 设置结束标识
if (i == keyword.length() - 1){
tempNode.setKeywordEnd(true);
}
}
}
/**
* 过滤敏感词
* @param text 待过滤的文本
* @return 过滤后的文本
*/
public String filter(String text){
if (StringUtils.isBlank(text)){
return null;
}
// 指针1 指向前缀树
TrieNode tempNode = rootNode;
// 指针2 指向文本
int begin = 0;
// 指针3 指针文本
int position = 0;
// 结果
StringBuilder sb = new StringBuilder();
while (position < text.length()){
char c = text.charAt(position);
// 跳过符号
if (isSymbol(c)){
// 若指针1 处于根节点,将此符号计入结果,让指针2向下走一步
if (tempNode == rootNode){
sb.append(c);
begin++;
}
// 无论符号在开头或中间,指针3都向下走一步
position++;
continue;
}
// 检查下级节点
tempNode = tempNode.getSubNode(c);
if (tempNode == null){
// 以begin开头的字符串不是敏感词
sb.append(text.charAt(begin));
// 进入下一个位置
position = ++begin;
// 重新指向根节点
tempNode = rootNode;
} else if (tempNode.isKeywordEnd()){
// 发现敏感词,将 begin ~ position 字符串替换掉
sb.append(REPLACEMENT);
// 进入下一个位置
begin = ++position;
// 重新指向根节点
tempNode = rootNode;
} else {
// 检查下一个字符
++position;
}
}
// 将最后一批字符计入结果
sb.append(text.substring(begin));
return sb.toString();
}
/**
* 拦截敏感词
* @param text 待过滤的文本
* @return 拦截结果
*/
public boolean intercept(String text){
if (StringUtils.isBlank(text)){
return false;
}
// 指针1 指向前缀树
TrieNode tempNode = rootNode;
// 指针2 指向文本
int begin = 0;
// 指针3 指针文本
int position = 0;
while (position < text.length()){
char c = text.charAt(position);
// 跳过符号
if (isSymbol(c)){
// 若指针1 处于根节点,将此符号计入结果,让指针2向下走一步
if (tempNode == rootNode){
begin++;
}
// 无论符号在开头或中间,指针3都向下走一步
position++;
continue;
}
// 检查下级节点
tempNode = tempNode.getSubNode(c);
if (tempNode == null){
// 进入下一个位置
position = ++begin;
// 重新指向根节点
tempNode = rootNode;
} else if (tempNode.isKeywordEnd()){
//发现敏感词
return true;
} else {
// 检查下一个字符
++position;
}
}
return false;
}
// 判断是否是特殊符号
private boolean isSymbol(Character c){
// 0x2E80 ~ 0x9FFF 是东亚文字范围
return !CharUtils.isAsciiAlphanumeric(c) && (c < 0x2E80 || c > 0x9FFF);
}
// 前缀树
private static class TrieNode{
// 子节点(key是下级字符, value是下级节点)
private Map<Character, TrieNode> subNodes = new HashMap<>();
// 关键词结束标识
private boolean isKeywordEnd = false;
// 添加子节点
public void addSubNode(Character c, TrieNode node){
subNodes.put(c, node);
}
// 获取子节点
public TrieNode getSubNode(Character c){
return subNodes.get(c);
}
public boolean isKeywordEnd() {
return isKeywordEnd;
}
public void setKeywordEnd(boolean keywordEnd) {
isKeywordEnd = keywordEnd;
}
}
}
使用
@SensitiveAnnotation//默认拦截
@SensitiveAnnotation(isStatus = false)//开启过滤 将敏感词进行过滤为"***"
注解在方法上面