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; | package com.epmet.service; | ||||
| 
 | 
 | ||||
| import com.epmet.dto.result.ExternalAppAuthResultDTO; | import com.epmet.dto.result.ExternalAppAuthResultDTO; | ||||
| import com.epmet.dto.result.ExternalAppResultDTO; |  | ||||
| 
 | 
 | ||||
| public interface ExternalAppAuthService { | 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