forked from rongchao/epmet-cloud-rizhao
				
			
				 28 changed files with 494 additions and 131 deletions
			
			
		| @ -0,0 +1,48 @@ | |||
| package com.epmet.commons.dynamic.datasource.enums; | |||
| 
 | |||
| /** | |||
|  * 服务-数据源flag-数据源名称对应关系 | |||
|  */ | |||
| public enum DataSourceEnum { | |||
| 
 | |||
| 	DATA_STATISTICAL_REAL("data-statistical-server", "real", "stats"), | |||
| 	DATA_STATISTICAL_FAKE("data-statistical-server", "fake", "statsDisplay"), | |||
| 	DATA_REPORT_REAL("data-report-server", "real", "stats"), | |||
| 	DATA_REPORT_FAKE("data-report-server", "fake", "statsDisplay"), | |||
| 	; | |||
| 
 | |||
| 	// 服务名
 | |||
| 	private String serviceName; | |||
| 	// 数据源标记
 | |||
| 	private String flag; | |||
| 	// 数据源,跟yml中的数据源名称保持一致
 | |||
| 	private String dataSourceName; | |||
| 
 | |||
| 	DataSourceEnum(String serviceName, String flag, String dataSourceName) { | |||
| 		this.serviceName = serviceName; | |||
| 		this.flag = flag; | |||
| 		this.dataSourceName = dataSourceName; | |||
| 	} | |||
| 
 | |||
| 	public static DataSourceEnum getEnum(String serviceName, String flag) { | |||
| 		DataSourceEnum[] values = DataSourceEnum.values(); | |||
| 		for (DataSourceEnum value : values) { | |||
| 			if (value.serviceName.equals(serviceName) && value.flag.equals(flag)) { | |||
| 				return value; | |||
| 			} | |||
| 		} | |||
| 		return null; | |||
| 	} | |||
| 
 | |||
| 	public String getServiceName() { | |||
| 		return serviceName; | |||
| 	} | |||
| 
 | |||
| 	public String getFlag() { | |||
| 		return flag; | |||
| 	} | |||
| 
 | |||
| 	public String getDataSourceName() { | |||
| 		return dataSourceName; | |||
| 	} | |||
| } | |||
| @ -0,0 +1,13 @@ | |||
| package com.epmet.commons.dynamic.datasource.util; | |||
| 
 | |||
| import com.epmet.commons.dynamic.datasource.enums.DataSourceEnum; | |||
| 
 | |||
| public abstract class AbstractDataSourceNameFetcher { | |||
| 
 | |||
|     public abstract String fetchDataSourceName(); | |||
| 
 | |||
|     //protected String getDataSourceName(String dataType, String serviceName) {
 | |||
|     //    return DataSourceEnum.getEnum(serviceName, dataType)
 | |||
|     //}
 | |||
| 
 | |||
| } | |||
| @ -0,0 +1,44 @@ | |||
| package com.epmet.commons.dynamic.datasource.util; | |||
| 
 | |||
| import com.epmet.commons.dynamic.datasource.enums.DataSourceEnum; | |||
| import com.epmet.commons.tools.exception.RenException; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.core.env.Environment; | |||
| import org.springframework.stereotype.Component; | |||
| import org.springframework.web.context.request.RequestContextHolder; | |||
| import org.springframework.web.context.request.ServletRequestAttributes; | |||
| 
 | |||
| /** | |||
|  * Http请求中获取数据源名称 | |||
|  */ | |||
| @Component | |||
| public class HttpRequestDataSourceNameFetcher extends AbstractDataSourceNameFetcher { | |||
| 
 | |||
|     protected Logger logger = LoggerFactory.getLogger(getClass()); | |||
| 
 | |||
|     @Autowired | |||
|     private Environment environment; | |||
| 
 | |||
|     @Override | |||
|     public String fetchDataSourceName() { | |||
|         ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); | |||
|         javax.servlet.http.HttpServletRequest request = requestAttributes.getRequest(); | |||
|         String dataType = request.getHeader("Data-Type"); | |||
| 
 | |||
|         logger.info("HttpRequestDataSourceNameFetcher获取到的DataType为:{}", dataType); | |||
|         if (StringUtils.isBlank(dataType)) { | |||
|             return null; | |||
|         } | |||
| 
 | |||
|         String serviceName = environment.getProperty("spring.application.name"); | |||
|         DataSourceEnum dataSourceEnum = DataSourceEnum.getEnum(serviceName, dataType); | |||
|         if (dataSourceEnum == null) { | |||
|             throw new RenException(String.format("根据前端传入的DataType[%s]无法找到对应的数据源。", dataType)); | |||
|         } | |||
|         logger.info("HttpRequestDataSourceNameFetcher根据DataType:[{}]获取到的DataSourceEnum为{}", dataType, dataSourceEnum.getDataSourceName()); | |||
|         return dataSourceEnum.getDataSourceName(); | |||
|     } | |||
| } | |||
| @ -0,0 +1,8 @@ | |||
| package com.epmet.constant; | |||
| 
 | |||
| public interface ExtAppAuthTypeConstant { | |||
| 
 | |||
|     String JWT = "jwt"; | |||
|     String MD5 = "md5"; | |||
| 
 | |||
| } | |||
| @ -1,10 +1,9 @@ | |||
| package com.epmet.service; | |||
| 
 | |||
| import com.epmet.dto.result.ExternalAppAuthResultDTO; | |||
| import com.epmet.dto.result.ExternalAppResultDTO; | |||
| 
 | |||
| public interface ExternalAppAuthService { | |||
| 
 | |||
|     ExternalAppAuthResultDTO auth(String appId, String token); | |||
|     ExternalAppAuthResultDTO auth(String appId, String token, Long ts, String authType); | |||
| 
 | |||
| } | |||
|  | |||
| @ -0,0 +1,73 @@ | |||
| package com.epmet.utils.externalapp; | |||
| 
 | |||
| import com.epmet.commons.tools.redis.RedisKeys; | |||
| import com.epmet.commons.tools.redis.RedisUtils; | |||
| import com.epmet.dao.ExternalAppSecretDao; | |||
| import com.epmet.dto.result.ExternalAppAuthResultDTO; | |||
| import com.epmet.entity.ExternalAppSecretEntity; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| 
 | |||
| 
 | |||
| /** | |||
|  * 外部应用认证处理器父类 | |||
|  */ | |||
| public abstract class ExtAppAuthProcessor { | |||
| 
 | |||
|     @Autowired | |||
|     private RedisUtils redisUtils; | |||
| 
 | |||
|     @Autowired | |||
|     private ExternalAppSecretDao externalAppSecretDao; | |||
| 
 | |||
|     private int diffMillins = 1000 * 60 * 5; | |||
| 
 | |||
|     public abstract ExternalAppAuthResultDTO auth(String appId, String token, Long ts); | |||
| 
 | |||
|     /** | |||
|      * 通过APP ID查询对应的秘钥 | |||
|      * @param appId | |||
|      * @return | |||
|      */ | |||
|     public String getTokenByAppId(String appId) { | |||
|         String secret = (String)redisUtils.get(RedisKeys.getExternalAppSecretKey(appId)); | |||
|         if (StringUtils.isBlank(secret)) { | |||
|             ExternalAppSecretEntity secretEntity = externalAppSecretDao.getSecretsByAppId(appId); | |||
|             if (secretEntity == null) { | |||
|                 return null; | |||
|             } | |||
|             secret = secretEntity.getSecret(); | |||
|             redisUtils.set(RedisKeys.getExternalAppSecretKey(appId), secret); | |||
|         } | |||
|         return secret; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * 时间戳校验 | |||
|      * @param timestamp | |||
|      * @return | |||
|      */ | |||
|     protected boolean validTimeStamp(Long timestamp) { | |||
|         long now = System.currentTimeMillis(); | |||
|         if (Math.abs(now - timestamp) > diffMillins) { | |||
|             return false; | |||
|         } | |||
|         return true; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * 封装结果 | |||
|      * @param result | |||
|      * @param message | |||
|      * @param customerId | |||
|      * @return | |||
|      */ | |||
|     public ExternalAppAuthResultDTO fillAuthResult(Boolean result, String message, String customerId) { | |||
|         ExternalAppAuthResultDTO authResult = new ExternalAppAuthResultDTO(); | |||
|         authResult.setSuccess(result); | |||
|         authResult.setMessage(message); | |||
|         authResult.setCustomerId(customerId); | |||
|         return authResult; | |||
|     } | |||
| 
 | |||
| } | |||
| @ -0,0 +1,61 @@ | |||
| package com.epmet.utils.externalapp; | |||
| 
 | |||
| import com.epmet.commons.tools.exception.ExceptionUtils; | |||
| import com.epmet.dto.result.ExternalAppAuthResultDTO; | |||
| import io.jsonwebtoken.Claims; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.stereotype.Component; | |||
| 
 | |||
| /** | |||
|  * jwt 认证处理器 | |||
|  */ | |||
| @Component | |||
| public class ExtAppJwtAuthProcessor extends ExtAppAuthProcessor { | |||
| 
 | |||
|     private static Logger logger = LoggerFactory.getLogger(ExtAppJwtAuthProcessor.class); | |||
| 
 | |||
|     @Autowired | |||
|     private ExtAppJwtTokenUtils jwtTokenUtils; | |||
| 
 | |||
|     public ExternalAppAuthResultDTO auth(String appId, String token, Long ts) { | |||
|         String secret; | |||
|         if (StringUtils.isBlank(secret = getTokenByAppId(appId))) { | |||
|             return fillAuthResult(false, String.format("根据AppId:%s没有找到对应的秘钥", appId), null); | |||
|         } | |||
| 
 | |||
|         Claims claim; | |||
|         try { | |||
|             claim = jwtTokenUtils.getClaimByToken(token, secret); | |||
|         } catch (Exception e) { | |||
|             String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); | |||
|             logger.error("解析token失败:{}", errorStackTrace); | |||
|             return fillAuthResult(false, "解析token失败", null); | |||
|         } | |||
| 
 | |||
|         String appIdIn = (String)claim.get("appId"); | |||
|         String customerId = (String)claim.get("customerId"); | |||
|         Long timestamp = (Long)claim.get("ts"); | |||
| 
 | |||
|         //校验时间戳,允许5分钟误差
 | |||
|         if (StringUtils.isAnyBlank(appIdIn, customerId) || timestamp == null) { | |||
|             logger.error("access token不完整。{},{},{}", appIdIn, customerId, timestamp); | |||
|             return fillAuthResult(false, "access token不完整。", null); | |||
|         } | |||
| 
 | |||
|         // TODO 暂时去掉时间差判断
 | |||
|         //if (!validTimeStamp(timestamp)) {
 | |||
|         //    logger.error("服务器存在时差过大,请求被拒绝");
 | |||
|         //    return fillAuthResult(false, "服务器存在时差过大,请求被拒绝", null);
 | |||
|         //}
 | |||
| 
 | |||
|         if (!appId.equals(appIdIn)) { | |||
|             logger.error("AppId不对应,token外部的:{}, token内部解析出来的:{}", appId, appIdIn); | |||
|             return fillAuthResult(false, "Header中的AppId不匹配", null); | |||
|         } | |||
| 
 | |||
|         return fillAuthResult(true, "解析成功,认证成功", customerId); | |||
|     } | |||
| } | |||
| @ -0,0 +1,42 @@ | |||
| package com.epmet.utils.externalapp; | |||
| 
 | |||
| import com.epmet.commons.tools.utils.Md5Util; | |||
| import com.epmet.dto.result.ExternalAppAuthResultDTO; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.springframework.stereotype.Component; | |||
| 
 | |||
| /** | |||
|  * md5 认证处理器 | |||
|  */ | |||
| @Component | |||
| public class ExtAppMD5AuthProcessor extends ExtAppAuthProcessor { | |||
| 
 | |||
|     private static Logger logger = LoggerFactory.getLogger(ExtAppMD5AuthProcessor.class); | |||
| 
 | |||
|     public ExternalAppAuthResultDTO auth(String appId, String token, Long ts) { | |||
|         if (ts == null) { | |||
|             return fillAuthResult(false, "需要传入时间戳参数", null); | |||
|         } | |||
|         String secret; | |||
|         if (StringUtils.isBlank(secret = getTokenByAppId(appId))) { | |||
|             return fillAuthResult(false, String.format("根据AppId:%s没有找到对应的秘钥", appId), null); | |||
|         } | |||
| 
 | |||
|         String localDigest = Md5Util.md5(secret.concat(":") + ts); | |||
|         if (!localDigest.equals(token)) { | |||
|             // 调用方生成的摘要跟本地生成的摘要不匹配
 | |||
|             return fillAuthResult(false, "签名不匹配,认证失败", null); | |||
|         } | |||
| 
 | |||
|         // TODO 暂时去掉时间差判断
 | |||
|         //if (!validTimeStamp(ts)) {
 | |||
|         //    logger.error("服务器存在时差过大,请求被拒绝");
 | |||
|         //    return fillAuthResult(false, "服务器存在时差过大,请求被拒绝", null);
 | |||
|         //}
 | |||
| 
 | |||
|         return fillAuthResult(true, "签名匹配,认证成功", null); | |||
|     } | |||
| 
 | |||
| } | |||
| @ -0,0 +1,56 @@ | |||
| SET NAMES utf8mb4; | |||
| SET FOREIGN_KEY_CHECKS = 0; | |||
| 
 | |||
| CREATE TABLE `external_app` ( | |||
|   `ID` varchar(64) NOT NULL COMMENT '主键', | |||
|   `APP_NAME` varchar(64) NOT NULL COMMENT 'APP名字', | |||
|   `CUSTOMER_ID` varchar(64) DEFAULT NULL COMMENT '客户ID', | |||
|   `DEL_FLAG` tinyint(1) DEFAULT NULL COMMENT '是否删除,0:未删除,1:已删除', | |||
|   `REVISION` int(10) DEFAULT NULL COMMENT '乐观锁', | |||
|   `CREATED_BY` varchar(64) DEFAULT NULL COMMENT '创建者id', | |||
|   `CREATED_TIME` datetime DEFAULT NULL COMMENT '创建时间', | |||
|   `UPDATED_BY` varchar(64) DEFAULT NULL COMMENT '更新者id', | |||
|   `UPDATED_TIME` datetime DEFAULT NULL COMMENT '更新时间', | |||
|   PRIMARY KEY (`ID`) | |||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='外部应用列表'; | |||
| 
 | |||
| CREATE TABLE `external_app_secret` ( | |||
|   `ID` varchar(64) NOT NULL COMMENT '主键', | |||
|   `APP_ID` varchar(64) NOT NULL COMMENT 'APP ID', | |||
|   `SECRET` varchar(255) NOT NULL COMMENT '秘钥', | |||
|   `DEL_FLAG` tinyint(1) DEFAULT NULL COMMENT '是否删除,0:未删除,1:已删除', | |||
|   `REVISION` int(10) DEFAULT NULL COMMENT '乐观锁', | |||
|   `CREATED_BY` varchar(64) DEFAULT NULL COMMENT '创建者id', | |||
|   `CREATED_TIME` datetime DEFAULT NULL COMMENT '创建时间', | |||
|   `UPDATED_BY` varchar(64) DEFAULT NULL COMMENT '更新者id', | |||
|   `UPDATED_TIME` datetime DEFAULT NULL COMMENT '更新时间', | |||
|   PRIMARY KEY (`ID`) | |||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='外部应用秘钥列表'; | |||
| 
 | |||
| CREATE TABLE `external_customer` ( | |||
|   `ID` varchar(64) NOT NULL COMMENT '客户ID', | |||
|   `CUSTOMER_NAME` varchar(255) NOT NULL COMMENT '客户名称', | |||
|   `DEL_FLAG` tinyint(1) DEFAULT NULL COMMENT '是否删除,0:未删除,1:已删除', | |||
|   `REVISION` int(10) DEFAULT NULL COMMENT '乐观锁', | |||
|   `CREATED_BY` varchar(64) DEFAULT NULL COMMENT '创建者id', | |||
|   `CREATED_TIME` datetime DEFAULT NULL COMMENT '创建时间', | |||
|   `UPDATED_BY` varchar(64) DEFAULT NULL COMMENT '更新者id', | |||
|   `UPDATED_TIME` datetime DEFAULT NULL COMMENT '更新时间', | |||
|   PRIMARY KEY (`ID`) USING BTREE | |||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | |||
| 
 | |||
| BEGIN; | |||
| INSERT INTO `external_app` VALUES ('227fb75ae4baa820755aaf43bf7f0a69', '便捷通行', '7e07311f4c4a56c65fa1dd5d16e0b743', 0, 0, 'wxz', '2020-08-18 13:39:32', 'wxz', '2020-08-18 13:39:37'); | |||
| INSERT INTO `external_app` VALUES ('acc4ad66c82a7b46e741364b4c62dce2', '市北大屏', 'b09527201c4409e19d1dbc5e3c3429a1', 0, 0, 'wxz', '2020-08-18 13:39:32', 'wxz', '2020-08-18 13:39:37'); | |||
| INSERT INTO `external_app` VALUES ('dbfad3110c124c89948d16e8b06a8888', '数据采集', 'b09527201c4409e19d1dbc5e3c3429a1', 0, 0, 'wxz', '2020-08-18 13:39:32', 'wxz', '2020-08-18 13:39:37'); | |||
| 
 | |||
| INSERT INTO `external_app_secret` VALUES ('44ed58fd256ae51b473b6ff8555c7131', '227fb75ae4baa820755aaf43bf7f0a69', 'a44a4fc41eb513cd93a0f957db3ef764e189e6aebb2369471396a8c3b32f61ed', 0, 0, 'wxz', '2020-08-18 13:40:03', 'xz', '2020-08-18 13:40:07'); | |||
| INSERT INTO `external_app_secret` VALUES ('95d16f5fe76d1139023107476871a077', 'dbfad3110c124c89948d16e8b06a8888', '0f7e983b017ac180b0da1877abe11bab22ab6288580e64d39b5e415dbb0fcc8f', 0, 0, 'wxz', '2020-08-18 13:40:03', 'xz', '2020-08-18 13:40:07'); | |||
| INSERT INTO `external_app_secret` VALUES ('9ca67b7b02dc2e80e9ba6ba4793aea54', 'acc4ad66c82a7b46e741364b4c62dce2', '612d304095c50369c3ef06e490f05779eeb8f19ff16566c73aeafafc5fa01970', 0, 0, 'wxz', '2020-08-18 13:40:03', 'xz', '2020-08-18 13:40:07'); | |||
| 
 | |||
| INSERT INTO `external_customer` VALUES ('7e07311f4c4a56c65fa1dd5d16e0b743', '外挂功能', 0, 0, 'wxz', '2020-08-19 14:21:52', 'APP_USER', '2020-08-21 15:23:35'); | |||
| INSERT INTO `external_customer` VALUES ('b09527201c4409e19d1dbc5e3c3429a1', '市北党建', 0, 0, 'wxz', '2020-08-19 14:21:52', 'wxz', '2020-08-19 14:21:58'); | |||
| 
 | |||
| COMMIT; | |||
| 
 | |||
| SET FOREIGN_KEY_CHECKS = 1; | |||
					Loading…
					
					
				
		Reference in new issue