|
@@ -0,0 +1,158 @@
|
|
|
+package cn.com.qmth.examcloud.core.basic.service.impl;
|
|
|
+
|
|
|
+import cn.com.qmth.examcloud.core.basic.service.CryptoConfigService;
|
|
|
+import cn.com.qmth.examcloud.core.basic.service.bean.CryptoConfigInfo;
|
|
|
+import cn.com.qmth.examcloud.starters.crypto.CryptoProperties;
|
|
|
+import cn.com.qmth.examcloud.web.redis.RedisClient;
|
|
|
+import org.apache.commons.collections.CollectionUtils;
|
|
|
+import org.apache.commons.lang3.ArrayUtils;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 加密方案组合配置相关接口
|
|
|
+ */
|
|
|
+@Service
|
|
|
+public class CryptoConfigServiceImpl implements CryptoConfigService {
|
|
|
+
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(CryptoConfigServiceImpl.class);
|
|
|
+
|
|
|
+ private static final String CRYPTO_CONFIG_CACHE_KEY = "$CRYPTO_CONFIG";
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private CryptoProperties cryptoProperties;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private RedisClient redisClient;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化默认组合配置
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void initCryptoConfigs() {
|
|
|
+ List<CryptoConfigInfo> configCaches = this.getAllCryptoConfigs();
|
|
|
+ if (CollectionUtils.isNotEmpty(configCaches)) {
|
|
|
+ log.warn("CryptoConfig has initialized.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ String[] groups = cryptoProperties.getGroups();
|
|
|
+ if (ArrayUtils.isEmpty(groups)) {
|
|
|
+ log.warn("CryptoConfig groups is empty.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("CryptoConfig init..");
|
|
|
+
|
|
|
+ List<CryptoConfigInfo> newConfigCaches = new ArrayList<>();
|
|
|
+ for (String group : groups) {
|
|
|
+ newConfigCaches.add(new CryptoConfigInfo(group, true, System.currentTimeMillis()));
|
|
|
+ }
|
|
|
+
|
|
|
+ redisClient.set(CRYPTO_CONFIG_CACHE_KEY, newConfigCaches);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取所有组合配置列表
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public List<CryptoConfigInfo> getAllCryptoConfigs() {
|
|
|
+ return redisClient.get(CRYPTO_CONFIG_CACHE_KEY, List.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 启用、禁用某些组合
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void updateCryptoConfigs(List<CryptoConfigInfo> cryptoConfigs) {
|
|
|
+ if (CollectionUtils.isEmpty(cryptoConfigs)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ List<CryptoConfigInfo> configCaches = this.getAllCryptoConfigs();
|
|
|
+ if (CollectionUtils.isEmpty(configCaches)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Boolean> map = cryptoConfigs.stream().collect(
|
|
|
+ Collectors.toMap(CryptoConfigInfo::getCombination, CryptoConfigInfo::isEnable, (value1, value2) -> value2)
|
|
|
+ );
|
|
|
+
|
|
|
+ boolean needUpdate = false;
|
|
|
+ for (CryptoConfigInfo info : configCaches) {
|
|
|
+ Boolean enable = map.get(info.getCombination());
|
|
|
+ if (enable != null && enable != info.isEnable()) {
|
|
|
+ needUpdate = true;
|
|
|
+ info.setEnable(enable);
|
|
|
+ info.setUpdateTime(System.currentTimeMillis());
|
|
|
+ log.warn("updateCryptoConfigs {}={}", info.getCombination(), enable);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (needUpdate) {
|
|
|
+ redisClient.set(CRYPTO_CONFIG_CACHE_KEY, configCaches);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断某个组合是否启用
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public boolean isEnableCryptoCombination(String combination) {
|
|
|
+ List<CryptoConfigInfo> configCaches = this.getAllCryptoConfigs();
|
|
|
+ if (CollectionUtils.isEmpty(configCaches)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (CryptoConfigInfo info : configCaches) {
|
|
|
+ if (info.getCombination().equals(combination)) {
|
|
|
+ return info.isEnable();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按用户随机获取一个组合
|
|
|
+ * 规则:采用一定机制保证某个用户在“某个时间段”不经常改变,防止恶意频繁刷登录来过滤和锁定组合
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public String randomCryptoCombination(Long userId) {
|
|
|
+ List<CryptoConfigInfo> configCaches = this.getAllCryptoConfigs();
|
|
|
+ if (CollectionUtils.isEmpty(configCaches)) {
|
|
|
+ log.warn("CryptoConfigs is empty. {}", userId);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 启用的组合列表
|
|
|
+ List<String> combinations = new ArrayList<>();
|
|
|
+ for (CryptoConfigInfo info : configCaches) {
|
|
|
+ if (info.isEnable()) {
|
|
|
+ combinations.add(info.getCombination());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (CollectionUtils.isEmpty(combinations)) {
|
|
|
+ log.warn("CryptoCombinations is empty. {}", userId);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("MMddHH");
|
|
|
+ Long timestamp = Long.parseLong(sdf.format(new Date()));
|
|
|
+
|
|
|
+ //(用户ID + MMddHH格式时间戳)% 当前启用的组合数 --> 某个用户在同个小时内所取到的组合一致
|
|
|
+ Long index = (userId + timestamp) % combinations.size();
|
|
|
+ return combinations.get(index.intValue());
|
|
|
+ }
|
|
|
+
|
|
|
+}
|