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