diff --git a/epmet-commons/epmet-commons-security/src/main/java/com/epmet/commons/security/jwt/JwtTokenUtils.java b/epmet-commons/epmet-commons-security/src/main/java/com/epmet/commons/security/jwt/JwtUtils.java similarity index 98% rename from epmet-commons/epmet-commons-security/src/main/java/com/epmet/commons/security/jwt/JwtTokenUtils.java rename to epmet-commons/epmet-commons-security/src/main/java/com/epmet/commons/security/jwt/JwtUtils.java index 8cbae20749..23b738d623 100644 --- a/epmet-commons/epmet-commons-security/src/main/java/com/epmet/commons/security/jwt/JwtTokenUtils.java +++ b/epmet-commons/epmet-commons-security/src/main/java/com/epmet/commons/security/jwt/JwtUtils.java @@ -28,8 +28,8 @@ import java.util.Map; * @since 1.0.0 */ @Component -public class JwtTokenUtils { - private static final Logger logger = LoggerFactory.getLogger(JwtTokenUtils.class); +public class JwtUtils { + private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class); public Claims getClaimByToken(String token, String secret) { try { diff --git a/epmet-commons/epmet-commons-security/src/main/java/com/epmet/commons/security/sign/openapi/OpenApiSignUtils.java b/epmet-commons/epmet-commons-security/src/main/java/com/epmet/commons/security/sign/openapi/OpenApiSignUtils.java index 33b82b1a23..306bb5ab3d 100644 --- a/epmet-commons/epmet-commons-security/src/main/java/com/epmet/commons/security/sign/openapi/OpenApiSignUtils.java +++ b/epmet-commons/epmet-commons-security/src/main/java/com/epmet/commons/security/sign/openapi/OpenApiSignUtils.java @@ -66,7 +66,8 @@ public class OpenApiSignUtils { public static void main(String[] args) { HashMap content = new HashMap<>(); - content.put("appId", "7d98b8af2d05752b4225709c4cfd4bd0"); + content.put("orgId", "aaa"); + content.put("test", ""); String secret = "3209ee9f41704482be1a1fb5873a25376f2899191ca846119d44168316bc3e44"; diff --git a/epmet-commons/epmet-commons-tools/src/main/java/com/epmet/commons/tools/exception/EpmetErrorCode.java b/epmet-commons/epmet-commons-tools/src/main/java/com/epmet/commons/tools/exception/EpmetErrorCode.java index 47546c0e81..e22f5f7983 100644 --- a/epmet-commons/epmet-commons-tools/src/main/java/com/epmet/commons/tools/exception/EpmetErrorCode.java +++ b/epmet-commons/epmet-commons-tools/src/main/java/com/epmet/commons/tools/exception/EpmetErrorCode.java @@ -146,7 +146,8 @@ public enum EpmetErrorCode { TOPIC_IS_CLOSED(9008,"该话题已关闭,无法转为议题"), // open api异常 - OPEN_API_SIGN_ERROR(9100, "签名错误"); + OPEN_API_SIGN_ERROR(9100, "签名错误"), + OPEN_API_SIGN_TOKEN_EXPIRED(9101, "Token过期"); private int code; private String msg; diff --git a/epmet-commons/epmet-commons-tools/src/main/java/com/epmet/commons/tools/redis/RedisKeys.java b/epmet-commons/epmet-commons-tools/src/main/java/com/epmet/commons/tools/redis/RedisKeys.java index 20bde9e05f..5ba36b541b 100644 --- a/epmet-commons/epmet-commons-tools/src/main/java/com/epmet/commons/tools/redis/RedisKeys.java +++ b/epmet-commons/epmet-commons-tools/src/main/java/com/epmet/commons/tools/redis/RedisKeys.java @@ -377,7 +377,7 @@ public class RedisKeys { * @author wxz * @date 2021.03.23 10:25 */ - public static String getOpenApiAccessTokenKey(String appId) { - return rootPrefix.concat("openapi:accesstoken:").concat(appId); + public static String getOpenApiAccessTokenKey(String accessToken) { + return rootPrefix.concat("openapi:accesstoken:").concat(accessToken); } } diff --git a/epmet-gateway/pom.xml b/epmet-gateway/pom.xml index 08eda7167e..560063fe23 100644 --- a/epmet-gateway/pom.xml +++ b/epmet-gateway/pom.xml @@ -13,6 +13,11 @@ jar + + com.epmet + epmet-commons-security + 2.0.0 + com.epmet epmet-commons-tools @@ -329,8 +334,8 @@ lb://epmet-point-server - lb://epmet-ext-server - + + http://127.0.0.1:8113 lb://data-aggregator-server diff --git a/epmet-gateway/src/main/java/com/epmet/auth/ExtAppFetchTokenAuthProcessor.java b/epmet-gateway/src/main/java/com/epmet/auth/ExtAppFetchTokenAuthProcessor.java new file mode 100644 index 0000000000..236914ccb2 --- /dev/null +++ b/epmet-gateway/src/main/java/com/epmet/auth/ExtAppFetchTokenAuthProcessor.java @@ -0,0 +1,84 @@ +package com.epmet.auth; + +import com.epmet.commons.security.jwt.JwtUtils; +import com.epmet.commons.security.sign.openapi.OpenApiSignUtils; +import com.epmet.commons.tools.exception.EpmetErrorCode; +import com.epmet.commons.tools.exception.RenException; +import com.epmet.commons.tools.redis.RedisKeys; +import com.epmet.commons.tools.redis.RedisUtils; +import com.epmet.commons.tools.utils.Result; +import com.epmet.commons.tools.utils.SpringContextUtils; +import com.epmet.feign.EpmetCommonServiceOpenFeignClient; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; + +/** + * 外部应用认证处理器:来平台token的方式 + */ +@Component +public class ExtAppFetchTokenAuthProcessor extends ExtAppAuthProcessor { + + @Autowired + private JwtUtils jwtTokenUtils; + + @Autowired + private RedisUtils redisUtils; + + @Override + public void auth(String appId, String token, Long ts, ServerWebExchange exchange) { + // 这种方式不需要其他平台传appId,因此我们自己从redis中取 + appId = (String) redisUtils.get(RedisKeys.getOpenApiAccessTokenKey(token)); + + // 1.token过期校验 + if (StringUtils.isBlank(appId)) { + throw new RenException(EpmetErrorCode.OPEN_API_SIGN_TOKEN_EXPIRED.getCode(), + EpmetErrorCode.OPEN_API_SIGN_TOKEN_EXPIRED.getMsg()); + } + + String secret = getSecret(appId); + + if (jwtTokenUtils.isTokenExpired(jwtTokenUtils.getExpiration(token, secret))) { + throw new RenException(EpmetErrorCode.OPEN_API_SIGN_TOKEN_EXPIRED.getCode(), + EpmetErrorCode.OPEN_API_SIGN_TOKEN_EXPIRED.getMsg()); + } + + // 2.验签 + // 验签暂时放到具体接口中 + //openApiSignUtils.checkSign(); + + // 2. 获取claims + Claims claims = jwtTokenUtils.getClaimByToken(token, secret); + appId = claims.get("appId", String.class); + + if (!StringUtils.isBlank(appId)) { + ServerHttpRequest.Builder mutate = exchange.getRequest().mutate(); + mutate.header("appId", appId); + exchange.mutate().request(mutate.build()).build(); + } + } + + /** + * @Description 获取秘钥 + * @return + * @author wxz + * @date 2021.03.23 14:12 + */ + private String getSecret(String appId) { + EpmetCommonServiceOpenFeignClient commonService = SpringContextUtils.getBean(EpmetCommonServiceOpenFeignClient.class); + Result result = commonService.getSecret(appId); + if (result == null || !result.success()) { + throw new RenException("fetchToken方式的外部应用认证,获取secret失败"); + } + String secret = result.getData(); + if (StringUtils.isBlank(secret)) { + throw new RenException("fetchToken方式的外部应用认证,获取secret失败"); + } + + return secret; + } +} diff --git a/epmet-gateway/src/main/java/com/epmet/auth/ExternalAuthProcessor.java b/epmet-gateway/src/main/java/com/epmet/auth/ExternalAuthProcessor.java index 8af2cdd5ef..b2ac9f477e 100644 --- a/epmet-gateway/src/main/java/com/epmet/auth/ExternalAuthProcessor.java +++ b/epmet-gateway/src/main/java/com/epmet/auth/ExternalAuthProcessor.java @@ -31,9 +31,15 @@ public class ExternalAuthProcessor extends AuthProcessor { public static final String APP_ID_CUSTOMER_ID_KEY = "CustomerId"; public static final String APP_ID_AUTY_TYPE_KEY = "AuthType"; - // 认证方式 + /** + * 认证方式 + */ + // 调用方生成jwt public static final String APP_AUTH_TYPE_JWT = "jwt"; + // 调用方生成md5 public static final String APP_AUTH_TYPE_MD5 = "md5"; + // 获取token方式 + public static final String APP_AUTH_TYPE_FETCH_TOKEN = "fetchToken"; @Autowired @@ -42,6 +48,9 @@ public class ExternalAuthProcessor extends AuthProcessor { @Autowired private ExtAppMD5AuthProcessor md5AuthProcessor; + @Autowired + private ExtAppFetchTokenAuthProcessor fetchTokenAuthProcessor; + private final AntPathMatcher antPathMatcher = new AntPathMatcher(); @Autowired @@ -68,24 +77,29 @@ public class ExternalAuthProcessor extends AuthProcessor { HttpHeaders headers = request.getHeaders(); String token = headers.getFirst(ACCESS_TOKEN_HEADER_KEY); - String appId = headers.getFirst(APP_ID_HEADER_KEY); String ts = headers.getFirst(APP_ID_TIMESTAMP_KEY); String customerId = headers.getFirst(APP_ID_CUSTOMER_ID_KEY); String authType = headers.getFirst(APP_ID_AUTY_TYPE_KEY); - if (StringUtils.isAnyBlank(token, appId)) { - throw new RenException(EpmetErrorCode.ERR401.getCode(), "请求头中的AccessToken和AppId不能为空"); - } - - logger.info("外部应用请求认证拦截Aspect执行,appId:{}, token:{}, ts:{}, customerId:{}, authType:{}", - appId, token, ts, customerId, authType); + logger.info("外部应用请求认证拦截Aspect执行,token:{}, ts:{}, customerId:{}, authType:{}", + token, ts, customerId, authType); // 没传authType或者传的jwt都用jwtprocessor处理 try { if (StringUtils.isBlank(authType) || APP_AUTH_TYPE_JWT.equals(authType)) { + String appId = headers.getFirst(APP_ID_HEADER_KEY); + if (StringUtils.isAnyBlank(token, appId)) { + throw new RenException(EpmetErrorCode.ERR401.getCode(), "请求头中的AccessToken和AppId不能为空"); + } jwtAuthProcessor.auth(appId, token, StringUtils.isNotBlank(ts) ? new Long(ts) : null, exchange); } else if (APP_AUTH_TYPE_MD5.equals(authType)) { + String appId = headers.getFirst(APP_ID_HEADER_KEY); + if (StringUtils.isAnyBlank(token, appId)) { + throw new RenException(EpmetErrorCode.ERR401.getCode(), "请求头中的AccessToken和AppId不能为空"); + } md5AuthProcessor.auth(appId, token, StringUtils.isNotBlank(ts) ? new Long(ts) : null, exchange); + } else if (APP_AUTH_TYPE_FETCH_TOKEN.equals(authType)) { + fetchTokenAuthProcessor.auth(null, token, StringUtils.isNotBlank(ts) ? new Long(ts) : null, exchange); } else { throw new RenException(EpmetErrorCode.OPER_EXTERNAL_APP_AUTH_ERROR.getCode(), "未知的外部认证类型"); } diff --git a/epmet-gateway/src/main/resources/bootstrap.yml b/epmet-gateway/src/main/resources/bootstrap.yml index 7a6c236128..1e59675b46 100644 --- a/epmet-gateway/src/main/resources/bootstrap.yml +++ b/epmet-gateway/src/main/resources/bootstrap.yml @@ -422,6 +422,10 @@ feign: httpclient: enabled: true +logging: + level: + com.epmet: debug + hystrix: command: default: diff --git a/epmet-module/epmet-common-service/common-service-client/src/main/java/com/epmet/feign/EpmetCommonServiceOpenFeignClient.java b/epmet-module/epmet-common-service/common-service-client/src/main/java/com/epmet/feign/EpmetCommonServiceOpenFeignClient.java index 66a1f46ee7..296489af6e 100644 --- a/epmet-module/epmet-common-service/common-service-client/src/main/java/com/epmet/feign/EpmetCommonServiceOpenFeignClient.java +++ b/epmet-module/epmet-common-service/common-service-client/src/main/java/com/epmet/feign/EpmetCommonServiceOpenFeignClient.java @@ -20,7 +20,7 @@ import java.util.Map; * @date 2020/6/4 10:28 */ @FeignClient(name = ServiceConstant.EPMET_COMMON_SERVICE, fallback = EpmetCommonServiceOpenFeignClientFallback.class) -// @FeignClient(name = ServiceConstant.EPMET_COMMON_SERVICE, fallback = EpmetCommonServiceOpenFeignClientFallback.class, url = "http://localhost:8103") + //@FeignClient(name = ServiceConstant.EPMET_COMMON_SERVICE, fallback = EpmetCommonServiceOpenFeignClientFallback.class, url = "http://192.168.1.132:8103") public interface EpmetCommonServiceOpenFeignClient { /** * @param formDTO diff --git a/epmet-module/epmet-ext/epmet-ext-client/src/main/java/com/epmet/dto/form/openapi/GetOrgDetailFormDTO.java b/epmet-module/epmet-ext/epmet-ext-client/src/main/java/com/epmet/dto/form/openapi/GetOrgDetailFormDTO.java new file mode 100644 index 0000000000..820225682a --- /dev/null +++ b/epmet-module/epmet-ext/epmet-ext-client/src/main/java/com/epmet/dto/form/openapi/GetOrgDetailFormDTO.java @@ -0,0 +1,15 @@ +package com.epmet.dto.form.openapi; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +@Data +public class GetOrgDetailFormDTO extends OpenApiBaseFormDTO { + + @NotBlank(message = "orgId不能为空") + private String orgId; + + private String test; + +} diff --git a/epmet-module/epmet-ext/epmet-ext-client/src/main/java/com/epmet/dto/form/openapi/OpenApiBaseFormDTO.java b/epmet-module/epmet-ext/epmet-ext-client/src/main/java/com/epmet/dto/form/openapi/OpenApiBaseFormDTO.java new file mode 100644 index 0000000000..c9385ece77 --- /dev/null +++ b/epmet-module/epmet-ext/epmet-ext-client/src/main/java/com/epmet/dto/form/openapi/OpenApiBaseFormDTO.java @@ -0,0 +1,13 @@ +package com.epmet.dto.form.openapi; + +import lombok.Data; + +/** + * open api基础类 + */ +@Data +public class OpenApiBaseFormDTO { + + private String sign; + +} diff --git a/epmet-module/epmet-ext/epmet-ext-server/src/main/java/com/epmet/controller/OpenApiOrgController.java b/epmet-module/epmet-ext/epmet-ext-server/src/main/java/com/epmet/controller/OpenApiOrgController.java new file mode 100644 index 0000000000..e3f0c9fdcd --- /dev/null +++ b/epmet-module/epmet-ext/epmet-ext-server/src/main/java/com/epmet/controller/OpenApiOrgController.java @@ -0,0 +1,63 @@ +package com.epmet.controller; + +import com.epmet.commons.security.sign.openapi.OpenApiSignUtils; +import com.epmet.commons.tools.exception.EpmetErrorCode; +import com.epmet.commons.tools.exception.RenException; +import com.epmet.commons.tools.redis.RedisKeys; +import com.epmet.commons.tools.redis.RedisUtils; +import com.epmet.commons.tools.utils.ConvertUtils; +import com.epmet.commons.tools.utils.Result; +import com.epmet.dto.form.openapi.GetOrgDetailFormDTO; +import com.epmet.feign.EpmetCommonServiceOpenFeignClient; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +@RestController +@RequestMapping("open-api") +public class OpenApiOrgController { + + @Autowired + private EpmetCommonServiceOpenFeignClient commonServiceOpenFeignClient; + + @Autowired + private RedisUtils redisUtils; + + @PostMapping("/get-org-detail") + public Result getOrgDetail(@RequestBody GetOrgDetailFormDTO input, + @RequestHeader("appId") String appId) { + // 验签 + Map params = null; + try { + params = ConvertUtils.entityToMap(input); + } catch (Exception e) { + e.printStackTrace(); + } + + if (!OpenApiSignUtils.checkSign(params, getSecret(appId))) { + // 验签失败,抛出异常提示 + throw new RenException(EpmetErrorCode.OPEN_API_SIGN_ERROR.getCode()); + } + + return new Result().ok("测试org"); + } + + private String getSecret(String appId) { + String secret = (String)redisUtils.get(RedisKeys.getExternalAppSecretKey(appId)); + if (StringUtils.isBlank(secret)) { + Result result = commonServiceOpenFeignClient.getSecret(appId); + if (!result.success()) { + throw new RenException("调用common service查询secret失败"); + } + secret = result.getData(); + if (StringUtils.isBlank(secret)) { + throw new RenException(String.format("根据appId%s没有找到对应的secret", appId)); + } + redisUtils.set(RedisKeys.getExternalAppSecretKey(appId), secret); + } + return secret; + } + +} diff --git a/epmet-module/epmet-ext/epmet-ext-server/src/main/java/com/epmet/service/impl/OpenApiAccessTokenServiceImpl.java b/epmet-module/epmet-ext/epmet-ext-server/src/main/java/com/epmet/service/impl/OpenApiAccessTokenServiceImpl.java index 8610eb2ce0..4cdee0c279 100644 --- a/epmet-module/epmet-ext/epmet-ext-server/src/main/java/com/epmet/service/impl/OpenApiAccessTokenServiceImpl.java +++ b/epmet-module/epmet-ext/epmet-ext-server/src/main/java/com/epmet/service/impl/OpenApiAccessTokenServiceImpl.java @@ -1,18 +1,11 @@ package com.epmet.service.impl; -import com.epmet.commons.security.jwt.JwtTokenUtils; -import com.epmet.commons.tools.exception.EpmetErrorCode; -import com.epmet.commons.tools.exception.RenException; +import com.epmet.commons.security.jwt.JwtUtils; import com.epmet.commons.tools.redis.RedisKeys; import com.epmet.commons.tools.redis.RedisUtils; -import com.epmet.commons.tools.utils.Result; -import com.epmet.commons.tools.utils.SpringContextUtils; import com.epmet.config.OpenApiConfig; -import com.epmet.feign.EpmetCommonServiceOpenFeignClient; import com.epmet.service.OpenApiAccessTokenService; -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.util.HashMap; @@ -21,7 +14,7 @@ import java.util.HashMap; public class OpenApiAccessTokenServiceImpl implements OpenApiAccessTokenService { @Autowired - private JwtTokenUtils jwtTokenUtils; + private JwtUtils jwtTokenUtils; @Autowired private OpenApiConfig openApiConfig; @@ -37,7 +30,7 @@ public class OpenApiAccessTokenServiceImpl implements OpenApiAccessTokenService String token = jwtTokenUtils.createToken(claim, openApiConfig.getAccessTokenExpire(), secret); // 缓存token - redisUtils.set(RedisKeys.getOpenApiAccessTokenKey(appId), token, openApiConfig.getAccessTokenExpire()); + redisUtils.set(RedisKeys.getOpenApiAccessTokenKey(token), appId, openApiConfig.getAccessTokenExpire()); return token; }