1.初始化版本,2.引入thirdpart模块,引入canteen模块,初始化菜品分类,spu

This commit is contained in:
zjf committed 2024-12-06 16:54:30 +08:00
commit aff77150d6
1595 files changed
+168833

No files matched your search

@@ -0,0 +1,126 @@
package com.bsd.pjcloud.common.sensitive;
import com.github.houbb.sensitive.word.bs.SensitiveWordBs;
import com.github.houbb.sensitive.word.support.deny.WordDenys;
import com.bsd.pjcloud.common.core.constant.CacheConstants;
import com.bsd.pjcloud.common.core.util.SpringContextHolder;
import com.bsd.pjcloud.common.sensitive.core.SensitiveService;
import com.bsd.pjcloud.common.sensitive.util.RemoteSensitiveService;
import com.bsd.pjcloud.common.sensitive.util.SensitiveWordsProperties;
import com.bsd.pjcloud.common.sensitive.word.SensitiveWordCustomizer;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import java.util.Objects;
import java.util.Optional;
/**
* 脱敏配置类
*
* @author pjcloud
* @date 2024/6/27
*/
@Slf4j
@AutoConfiguration
@RequiredArgsConstructor
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SensitiveWordsProperties.class)
public class SensitiveAutoConfiguration implements InitializingBean {
private final Optional<RedisMessageListenerContainer> redisMessageListenerContainerOptional;
/**
* 注入默认的脱敏权限判断
*
* @return SensitiveService
*/
@Bean
@ConditionalOnMissingBean
public SensitiveService sensitiveService(SensitiveWordsProperties sensitiveWordsProperties) {
return sensitive -> {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (Objects.isNull(authentication)) {
return true;
}
// 判断角色是否包含 no_mask 的权限
return authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority)
.noneMatch(s -> s.equals(sensitiveWordsProperties.getMask()));
};
}
/**
* 初始化敏感词定制
*
* @return SensitiveWordCustomizer
*/
@Bean
@Lazy
public SensitiveWordCustomizer sensitiveWordCustomizer(RemoteSensitiveService remoteSensitiveService) {
return new SensitiveWordCustomizer(remoteSensitiveService);
}
/**
* 懒加载初始化引导类
*
* @return 初始化引导类
*/
@Bean
@Lazy
public SensitiveWordBs sensitiveWordBs(@Lazy SensitiveWordCustomizer sensitiveWordCustomizer
, SensitiveWordsProperties sensitiveWordsProperties) {
return SensitiveWordBs.newInstance()
// 加载白名单数据
.wordAllow(sensitiveWordCustomizer)
// 加载黑名单数据
.wordDeny(WordDenys.chains(WordDenys.defaults(), sensitiveWordCustomizer))
// 忽略大小写
.ignoreCase(sensitiveWordsProperties.isIgnoreCase())
// 忽略半角圆角
.ignoreWidth(sensitiveWordsProperties.isIgnoreWidth())
// 忽略数字的写法
.ignoreNumStyle(sensitiveWordsProperties.isIgnoreNumStyle())
// 忽略中文的书写格式:简繁体
.ignoreChineseStyle(sensitiveWordsProperties.isIgnoreChineseStyle())
// 忽略英文的书写格式
.ignoreEnglishStyle(sensitiveWordsProperties.isIgnoreEnglishStyle())
// 忽略重复词
.ignoreRepeat(sensitiveWordsProperties.isIgnoreRepeat())
// 是否启用数字检测
.enableNumCheck(sensitiveWordsProperties.isEnableNumCheck())
// 是否启用邮箱检测
.enableEmailCheck(sensitiveWordsProperties.isEnableEmailCheck())
// 是否启用链接检测
.enableUrlCheck(sensitiveWordsProperties.isEnableUrlCheck())
// 数字检测,自定义指定长度
.numCheckLen(sensitiveWordsProperties.getNumCheckLen())
.init();
}
@Override
public void afterPropertiesSet() throws Exception {
redisMessageListenerContainerOptional.ifPresent(redisMessageListenerContainer -> {
redisMessageListenerContainer.addMessageListener((message, bytes) -> {
log.info("开始重新加载敏感词库");
SpringContextHolder.getBean(SensitiveWordBs.class).init();
log.info("敏感词库加载完成");
},
new ChannelTopic(CacheConstants.SENSITIVE_REDIS_RELOAD_TOPIC));
});
}
}
@@ -0,0 +1,62 @@
/*
* Copyright (c) 2018-2025, pjcloud All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: pjcloud
*/
package com.bsd.pjcloud.common.sensitive.annotation;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.bsd.pjcloud.common.sensitive.core.SensitiveSerialize;
import com.bsd.pjcloud.common.sensitive.core.SensitiveTypeEnum;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 对象脱敏注解
*
* @author mayee
* @version v1.0
**/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveSerialize.class)
public @interface Sensitive {
/**
* 脱敏数据类型, 非Customer时, 将忽略 refixNoMaskLen 和 suffixNoMaskLen 和 maskStr
*/
SensitiveTypeEnum type() default SensitiveTypeEnum.CUSTOMER;
/**
* 前置不需要打码的长度
*/
int prefixNoMaskLen() default 0;
/**
* 后置不需要打码的长度
*/
int suffixNoMaskLen() default 0;
/**
* 用什么打码
*/
String maskStr() default "*";
}
@@ -0,0 +1,89 @@
/*
* Copyright (c) 2018-2025, pjcloud All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: pjcloud
*/
package com.bsd.pjcloud.common.sensitive.core;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.bsd.pjcloud.common.core.util.SpringContextHolder;
import com.bsd.pjcloud.common.sensitive.annotation.Sensitive;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.util.Objects;
/**
* @author pjcloud
* @date 2019-08-13
* <p>
* 脱敏序列化
*/
@Slf4j
@NoArgsConstructor
@AllArgsConstructor
public class SensitiveSerialize extends JsonSerializer<String> implements ContextualSerializer {
private Sensitive sensitive;
@Override
public void serialize(final String origin, final JsonGenerator jsonGenerator,
final SerializerProvider serializerProvider) throws IOException {
if (StrUtil.isBlank(origin)) {
jsonGenerator.writeString(origin);
return;
}
// 判断是否有可以查看原文
SensitiveService sensitiveService = SpringContextHolder.getBean(SensitiveService.class);
if (!sensitiveService.isSensitive(sensitive)) {
jsonGenerator.writeString(origin);
return;
}
// 根据配置注解获取脱敏类型并处理
String result = sensitive.type().getStrategy().apply(sensitive, origin);
jsonGenerator.writeString(result);
}
@Override
public JsonSerializer<?> createContextual(final SerializerProvider serializerProvider,
final BeanProperty beanProperty) throws JsonMappingException {
if (beanProperty != null) {
if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
Sensitive sensitive = beanProperty.getAnnotation(Sensitive.class);
if (sensitive == null) {
sensitive = beanProperty.getContextAnnotation(Sensitive.class);
}
if (sensitive != null) {
this.sensitive = sensitive;
return this;
}
}
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
}
return serializerProvider.findNullValueSerializer(null);
}
}
@@ -0,0 +1,16 @@
package com.bsd.pjcloud.common.sensitive.core;
import com.bsd.pjcloud.common.sensitive.annotation.Sensitive;
/**
* 脱敏逻辑增强处理
*
* @author pjcloud
* @date 2024/6/27
*/
public interface SensitiveService {
/**
* 是否脱敏
*/
boolean isSensitive(Sensitive sensitive);
}
@@ -0,0 +1,11 @@
package com.bsd.pjcloud.common.sensitive.core;
/**
* @author pjcloud
* @date 2024/6/27
*/
@FunctionalInterface
public interface SensitiveStrategyFunction<T, U, R> {
R apply(T t, U u);
}
@@ -0,0 +1,92 @@
/*
* Copyright (c) 2018-2025, pjcloud All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: pjcloud
*/
package com.bsd.pjcloud.common.sensitive.core;
import cn.hutool.core.util.DesensitizedUtil;
import com.bsd.pjcloud.common.sensitive.annotation.Sensitive;
import com.bsd.pjcloud.common.sensitive.util.DesensitizedUtils;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* 敏感信息枚举类
*
* @author mayee
* @version v1.0
**/
@RequiredArgsConstructor
public enum SensitiveTypeEnum {
/**
* 自定义
*/
CUSTOMER((sensitive, input) -> DesensitizedUtils.desValue(input, sensitive.prefixNoMaskLen(), sensitive.suffixNoMaskLen(), sensitive.maskStr())),
/**
* 用户名, 刘*华, 徐*
*/
CHINESE_NAME((sensitive, input) -> DesensitizedUtils.chineseName(input)),
/**
* 身份证号, 110110********1234
*/
ID_CARD((sensitive, input) -> DesensitizedUtils.idCardNum(input)),
/**
* 座机号, ****1234
*/
FIXED_PHONE((sensitive, input) -> DesensitizedUtils.fixedPhone(input)),
/**
* 手机号, 176****1234
*/
MOBILE_PHONE((sensitive, input) -> DesensitizedUtils.mobilePhone(input)),
/**
* 地址, 北京********
*/
ADDRESS((sensitive, input) -> DesensitizedUtils.address(input)),
/**
* 电子邮件, s*****o@xx.com
*/
EMAIL((sensitive, input) -> DesensitizedUtils.email(input)),
/**
* 银行卡, 622202************1234
*/
BANK_CARD((sensitive, input) -> DesensitizedUtils.bankCard(input)),
/**
* 密码, 永远是 ******, 与长度无关
*/
PASSWORD((sensitive, input) -> DesensitizedUtils.password(input)),
/**
* 密钥, 【密钥】密钥除第一位其他都是***, 与长度无关
*/
KEY((sensitive, input) -> DesensitizedUtils.key(input)),
/**
* IPV4 类型 113.123.198.176 主机部分打码 113.123.*.123
*/
IPV4((sensitive, input) -> DesensitizedUtils.ipv4(input)),
/**
* 中国大陆车牌,包含普通车辆、新能源车辆
*/
CAR_LICENSE((sensitive, input) -> DesensitizedUtil.carLicense(input));
/**
* 获取脱敏策略
*/
@Getter
private final SensitiveStrategyFunction<Sensitive, String, String> strategy;
}
@@ -0,0 +1,175 @@
/*
* Copyright (c) 2018-2025, pjcloud All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: pjcloud
*/
package com.bsd.pjcloud.common.sensitive.util;
import cn.hutool.core.util.StrUtil;
/**
* 脱敏工具类
*
* @author mayee
* @version v1.0
**/
public class DesensitizedUtils {
/**
* 对字符串进行脱敏操作
* @param origin 原始字符串
* @param prefixNoMaskLen 左侧需要保留几位明文字段
* @param suffixNoMaskLen 右侧需要保留几位明文字段
* @param maskStr 用于遮罩的字符串, 如'*'
* @return 脱敏后结果
*/
public static String desValue(String origin, int prefixNoMaskLen, int suffixNoMaskLen, String maskStr) {
if (origin == null) {
return null;
}
StringBuilder sb = new StringBuilder();
for (int i = 0, n = origin.length(); i < n; i++) {
if (i < prefixNoMaskLen) {
sb.append(origin.charAt(i));
continue;
}
if (i > (n - suffixNoMaskLen - 1)) {
sb.append(origin.charAt(i));
continue;
}
sb.append(maskStr);
}
return sb.toString();
}
/**
* 【中文姓名】只显示最后一个汉字,其他隐藏为星号,比如:**梦
* @param fullName 姓名
* @return 结果
*/
public static String chineseName(String fullName) {
if (fullName == null) {
return null;
}
return desValue(fullName, 0, 1, "*");
}
/**
* 【身份证号】显示前六位, 四位,其他隐藏。共计18位或者15位,比如:340304*******1234
* @param id 身份证号码
* @return 结果
*/
public static String idCardNum(String id) {
return desValue(id, 6, 4, "*");
}
/**
* 【固定电话】后四位,其他隐藏,比如 ****1234
* @param num 固定电话
* @return 结果
*/
public static String fixedPhone(String num) {
return desValue(num, 0, 4, "*");
}
/**
* 【手机号码】前三位,后四位,其他隐藏,比如135****6810
* @param num 手机号码
* @return 结果
*/
public static String mobilePhone(String num) {
return desValue(num, 3, 4, "*");
}
/**
* 【地址】只显示到地区,不显示详细地址,比如:北京市海淀区****
* @param address 地址
* @return 结果
*/
public static String address(String address) {
return desValue(address, 6, 0, "*");
}
/**
* 【电子邮箱 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示,比如:d**@126.com
* @param email 电子邮箱
* @return 结果
*/
public static String email(String email) {
if (email == null) {
return null;
}
int index = StrUtil.indexOf(email, '@');
if (index <= 1) {
return email;
}
String preEmail = desValue(email.substring(0, index), 1, 0, "*");
return preEmail + email.substring(index);
}
/**
* 【银行卡号】前六位,后四位,其他用星号隐藏每位1个星号,比如:622260**********1234
* @param cardNum 银行卡号
* @return 结果
*/
public static String bankCard(String cardNum) {
return desValue(cardNum, 6, 4, "*");
}
/**
* 【密码】密码的全部字符都用*代替,比如:******
* @param password 密码
* @return 结果
*/
public static String password(String password) {
if (password == null) {
return null;
}
return "******";
}
/**
* 【密钥】密钥除了最后三位,全部都用*代替,比如:***xdS 脱敏后长度为6,如果明文长度不足三位,则按实际长度显示,剩余位置补*
* @param key 密钥
* @return 结果
*/
public static String key(String key) {
if (key == null) {
return null;
}
int viewLength = 6;
StringBuilder tmpKey = new StringBuilder(desValue(key, 0, 3, "*"));
if (tmpKey.length() > viewLength) {
return tmpKey.substring(tmpKey.length() - viewLength);
}
else if (tmpKey.length() < viewLength) {
int buffLength = viewLength - tmpKey.length();
for (int i = 0; i < buffLength; i++) {
tmpKey.insert(0, "*");
}
return tmpKey.toString();
}
else {
return tmpKey.toString();
}
}
public static String ipv4(String origin) {
return StrUtil.subBefore(origin, '.', true) + ".*";
}
}
@@ -0,0 +1,22 @@
package com.bsd.pjcloud.common.sensitive.util;
import com.bsd.pjcloud.common.core.constant.ServiceNameConstants;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.feign.annotation.NoToken;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.List;
/**
* @author pjcloud
* @date 2024/7/7
*/
@FeignClient(contextId = "remoteSensitiveService", value = ServiceNameConstants.UPMS_SERVICE)
public interface RemoteSensitiveService {
@NoToken
@GetMapping("/sysSensitiveWord/remote/list/{type}")
R<List<String>> list(@PathVariable String type);
}
@@ -0,0 +1,88 @@
package com.bsd.pjcloud.common.sensitive.util;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 敏感词配置类
*
* @author pjcloud
* @date 2024/3/26
* <p>
*/
@Data
@ConfigurationProperties(SensitiveWordsProperties.PREFIX)
public class SensitiveWordsProperties {
public static final String PREFIX = "pjcloud.sensitive.word";
/**
* The sensitive words to detect.
*/
private String mask = "no_mask";
/**
* Whether the sensitive words detection is enabled.
*/
private boolean enabled = true;
/**
* Whether to ignore case when detecting sensitive words.
*/
private boolean ignoreCase = true;
/**
* Whether to ignore width (full-width and half-width characters) when detecting
* sensitive words.
*/
private boolean ignoreWidth = true;
/**
* Whether to ignore number style (Arabic numerals, Roman numerals, etc.) when
* detecting sensitive words.
*/
private boolean ignoreNumStyle = true;
/**
* Whether to ignore Chinese style (simplified and traditional) when detecting
* sensitive words.
*/
private boolean ignoreChineseStyle = true;
/**
* Whether to ignore English style (US and UK spelling) when detecting sensitive
* words.
*/
private boolean ignoreEnglishStyle = true;
/**
* Whether to ignore repeated words when detecting sensitive words.
*/
private boolean ignoreRepeat = true;
/**
* Whether to enable number check when detecting sensitive words.
*/
private boolean enableNumCheck = false;
/**
* Whether to enable email check when detecting sensitive words.
*/
private boolean enableEmailCheck = false;
/**
* Whether to enable URL check when detecting sensitive words.
*/
private boolean enableUrlCheck = true;
/**
* The length of numbers to check when detecting sensitive words.
*/
private int numCheckLen = 8;
/**
* The error message to return when sensitive words are detected.
*/
private String errorMsg = "您的输入包含敏感词,请重新输入";
}
@@ -0,0 +1,44 @@
package com.bsd.pjcloud.common.sensitive.word;
import com.github.houbb.sensitive.word.api.IWordAllow;
import com.github.houbb.sensitive.word.api.IWordDeny;
import com.bsd.pjcloud.common.core.constant.enums.YesNoEnum;
import com.bsd.pjcloud.common.core.util.RetOps;
import com.bsd.pjcloud.common.sensitive.util.RemoteSensitiveService;
import lombok.RequiredArgsConstructor;
import java.util.ArrayList;
import java.util.List;
/**
* @author pjcloud
* @date 2024/7/6
* <p>
* 敏感词定制
*/
@RequiredArgsConstructor
public class SensitiveWordCustomizer implements IWordAllow, IWordDeny {
private final RemoteSensitiveService remoteSensitiveService;
/**
* 白名单列表
*
* @return List
*/
@Override
public List<String> allow() {
return RetOps.of(remoteSensitiveService.list(YesNoEnum.YES.getCode())).getData().orElseGet(ArrayList::new);
}
/**
* 黑名单列表
*
* @return List
*/
@Override
public List<String> deny() {
return RetOps.of(remoteSensitiveService.list(YesNoEnum.NO.getCode())).getData().orElseGet(ArrayList::new);
}
}
@@ -0,0 +1 @@
com.bsd.pjcloud.common.sensitive.SensitiveAutoConfiguration
@@ -0,0 +1 @@
com.bsd.pjcloud.common.sensitive.util.RemoteSensitiveService