diff --git a/epmet-commons/epmet-commons-tools/src/main/java/com/epmet/commons/tools/constant/Constant.java b/epmet-commons/epmet-commons-tools/src/main/java/com/epmet/commons/tools/constant/Constant.java index 67aced6dbe..3636e2942d 100644 --- a/epmet-commons/epmet-commons-tools/src/main/java/com/epmet/commons/tools/constant/Constant.java +++ b/epmet-commons/epmet-commons-tools/src/main/java/com/epmet/commons/tools/constant/Constant.java @@ -96,6 +96,8 @@ public interface Constant { * authorization header */ String AUTHORIZATION_HEADER = "authorization"; + + String ACCESS_TOKEN_HEADER = "AccessToken"; /** * APP用户标识 */ diff --git a/epmet-gateway/pom.xml b/epmet-gateway/pom.xml index 78fa471715..ed64b82243 100644 --- a/epmet-gateway/pom.xml +++ b/epmet-gateway/pom.xml @@ -58,6 +58,13 @@ 2.0.0 compile + + + + com.epmet + common-service-client + 2.0.0 + diff --git a/epmet-gateway/src/main/java/com/epmet/auth/AuthProcessor.java b/epmet-gateway/src/main/java/com/epmet/auth/AuthProcessor.java new file mode 100644 index 0000000000..4ef47a2eae --- /dev/null +++ b/epmet-gateway/src/main/java/com/epmet/auth/AuthProcessor.java @@ -0,0 +1,26 @@ +package com.epmet.auth; + +import com.alibaba.fastjson.JSON; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.nio.charset.StandardCharsets; + +public abstract class AuthProcessor { + + abstract Mono auth(ServerWebExchange exchange, GatewayFilterChain chain); + + protected Mono response(ServerWebExchange exchange, Object object) { + String json = JSON.toJSONString(object); + DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(json.getBytes(StandardCharsets.UTF_8)); + exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8); + exchange.getResponse().setStatusCode(HttpStatus.OK); + return exchange.getResponse().writeWith(Flux.just(buffer)); + } + +} diff --git a/epmet-gateway/src/main/java/com/epmet/auth/ExtAppAuthProcessor.java b/epmet-gateway/src/main/java/com/epmet/auth/ExtAppAuthProcessor.java new file mode 100644 index 0000000000..4dc7a22c71 --- /dev/null +++ b/epmet-gateway/src/main/java/com/epmet/auth/ExtAppAuthProcessor.java @@ -0,0 +1,30 @@ +package com.epmet.auth; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * 外部应用认证处理器父类 + */ +public abstract class ExtAppAuthProcessor { + + private Logger logger = LoggerFactory.getLogger(getClass()); + + private int diffMillins = 1000 * 60 * 5; + + public abstract void auth(String appId, String token, Long ts); + + /** + * 时间戳校验 + * @param timestamp + * @return + */ + protected boolean validTimeStamp(Long timestamp) { + long now = System.currentTimeMillis(); + if (Math.abs(now - timestamp) > diffMillins) { + return false; + } + return true; + } +} diff --git a/epmet-gateway/src/main/java/com/epmet/auth/ExtAppJwtAuthProcessor.java b/epmet-gateway/src/main/java/com/epmet/auth/ExtAppJwtAuthProcessor.java new file mode 100644 index 0000000000..431907cde5 --- /dev/null +++ b/epmet-gateway/src/main/java/com/epmet/auth/ExtAppJwtAuthProcessor.java @@ -0,0 +1,88 @@ +package com.epmet.auth; + +import com.epmet.commons.tools.exception.EpmetErrorCode; +import com.epmet.commons.tools.exception.ExceptionUtils; +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 com.epmet.jwt.JwtTokenUtils; +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 JwtTokenUtils jwtTokenUtils; + + @Autowired + private RedisUtils redisUtils; + + @Override + public void auth(String appId, String token, Long ts) { + String secret; + if (StringUtils.isBlank(secret = getTokenFromCache(appId))) { + throw new RenException(EpmetErrorCode.OPER_EXTERNAL_APP_AUTH_ERROR.getCode(), String.format("根据AppId:【%s】没有找到对应的秘钥", appId)); + } + + Claims claim; + try { + claim = jwtTokenUtils.getClaimByToken(token, secret); + } catch (Exception e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + logger.error("解析token失败:{}", errorStackTrace); + throw new RenException(EpmetErrorCode.OPER_EXTERNAL_APP_AUTH_ERROR.getCode(), "解析token失败"); + } + + 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); + throw new RenException(EpmetErrorCode.OPER_EXTERNAL_APP_AUTH_ERROR.getCode(), "AccessToken不完整"); + } + + if (!validTimeStamp(timestamp)) { + logger.error("extapp token已经超时,请求被拒绝"); + throw new RenException(EpmetErrorCode.OPER_EXTERNAL_APP_AUTH_ERROR.getCode(), "AccessToken已经超时"); + } + + if (!appId.equals(appIdIn)) { + logger.error("AppId不对应,token外部的:{}, token内部解析出来的:{}", appId, appIdIn); + throw new RenException(EpmetErrorCode.OPER_EXTERNAL_APP_AUTH_ERROR.getCode(), "AppId不匹配"); + } + } + + /** + * 通过APP ID查询对应的秘钥 + * @param appId + * @return + */ + public String getTokenFromCache(String appId) { + String secret = (String)redisUtils.get(RedisKeys.getExternalAppSecretKey(appId)); + if (StringUtils.isBlank(secret)) { + EpmetCommonServiceOpenFeignClient commonService = SpringContextUtils.getBean(EpmetCommonServiceOpenFeignClient.class); + Result result = commonService.getSecret(appId); + if (!result.success()) { + throw new RenException(EpmetErrorCode.OPER_EXTERNAL_APP_AUTH_ERROR.getCode(), result.getInternalMsg()); + } + secret = result.getData(); + redisUtils.set(RedisKeys.getExternalAppSecretKey(appId), secret); + } + return secret; + } +} diff --git a/epmet-gateway/src/main/java/com/epmet/auth/ExtAppMD5AuthProcessor.java b/epmet-gateway/src/main/java/com/epmet/auth/ExtAppMD5AuthProcessor.java new file mode 100644 index 0000000000..c1869cb1ab --- /dev/null +++ b/epmet-gateway/src/main/java/com/epmet/auth/ExtAppMD5AuthProcessor.java @@ -0,0 +1,74 @@ +package com.epmet.auth; + +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.Md5Util; +import com.epmet.commons.tools.utils.Result; +import com.epmet.commons.tools.utils.SpringContextUtils; +import com.epmet.feign.EpmetCommonServiceOpenFeignClient; +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; + +/** + * md5 认证处理器 + */ +@Component +public class ExtAppMD5AuthProcessor extends ExtAppAuthProcessor { + + private static Logger logger = LoggerFactory.getLogger(ExtAppMD5AuthProcessor.class); + + //@Autowired + //private EpmetCommonServiceOpenFeignClient commonServiceOpenFeignClient; + + @Autowired + private RedisUtils redisUtils; + + @Override + public void auth(String appId, String token, Long ts) { + if (ts == null) { + throw new RenException(EpmetErrorCode.OPER_EXTERNAL_APP_AUTH_ERROR.getCode(), "需要传入时间戳参数"); + } + String secret; + if (StringUtils.isBlank(secret = getTokenFromCache(appId))) { + throw new RenException(EpmetErrorCode.OPER_EXTERNAL_APP_AUTH_ERROR.getCode(), String.format("根据AppId:%s没有找到对应的秘钥", appId)); + } + + String localDigest = Md5Util.md5(secret.concat(":") + ts); + if (!localDigest.equals(token)) { + // 调用方生成的摘要跟本地生成的摘要不匹配 + throw new RenException(EpmetErrorCode.OPER_EXTERNAL_APP_AUTH_ERROR.getCode(), "签名不匹配,认证失败"); + } + + if (!validTimeStamp(ts)) { + logger.error("AccessToken已经超时,请求被拒绝"); + throw new RenException(EpmetErrorCode.OPER_EXTERNAL_APP_AUTH_ERROR.getCode(), "AccessToken已经超时,请求被拒绝"); + } + } + + /** + * 通过APP ID查询对应的秘钥 + * + * @param appId + * @return + */ + public String getTokenFromCache(String appId) { + String secret = (String) redisUtils.get(RedisKeys.getExternalAppSecretKey(appId)); + if (StringUtils.isBlank(secret)) { + EpmetCommonServiceOpenFeignClient commonService = SpringContextUtils.getBean(EpmetCommonServiceOpenFeignClient.class); + Result result = commonService.getSecret(appId); + if (!result.success()) { + throw new RenException(EpmetErrorCode.OPER_EXTERNAL_APP_AUTH_ERROR.getCode(), result.getInternalMsg()); + } + + secret = result.getData(); + redisUtils.set(RedisKeys.getExternalAppSecretKey(appId), 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 new file mode 100644 index 0000000000..7b46e870ce --- /dev/null +++ b/epmet-gateway/src/main/java/com/epmet/auth/ExternalAuthProcessor.java @@ -0,0 +1,79 @@ +package com.epmet.auth; + +import com.epmet.commons.tools.exception.EpmetErrorCode; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.epmet.commons.tools.exception.RenException; +import com.epmet.commons.tools.utils.Result; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.http.HttpHeaders; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * 外部应用认证 + */ +@Component +public class ExternalAuthProcessor extends AuthProcessor { + + private Logger logger = LoggerFactory.getLogger(getClass()); + + // 头s + public static final String AUTHORIZATION_TOKEN_HEADER_KEY = "Authorization"; + public static final String ACCESS_TOKEN_HEADER_KEY = "AccessToken"; + public static final String APP_ID_HEADER_KEY = "appId"; + public static final String APP_ID_TIMESTAMP_KEY = "ts"; + public static final String APP_ID_CUSTOMER_ID_KEY = "CustomerId"; + public static final String APP_ID_AUTY_TYPE_KEY = "AuthType"; + + // 认证方式 + public static final String APP_AUTH_TYPE_JWT = "jwt"; + public static final String APP_AUTH_TYPE_MD5 = "md5"; + + + @Autowired + private ExtAppJwtAuthProcessor jwtAuthProcessor; + + @Autowired + private ExtAppMD5AuthProcessor md5AuthProcessor; + + @Override + public Mono auth(ServerWebExchange exchange, GatewayFilterChain chain) { + HttpHeaders headers = exchange.getRequest().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("请求头中的AccessToken和AppId不能为空"); + } + + logger.info("外部应用请求认证拦截Aspect执行,appId:{}, token:{}, ts:{}, customerId:{}, authType:{}", + appId, token, ts, customerId, authType); + + // 没传authType或者传的jwt都用jwtprocessor处理 + try { + if (StringUtils.isBlank(authType) || APP_AUTH_TYPE_JWT.equals(authType)) { + jwtAuthProcessor.auth(appId, token, StringUtils.isNotBlank(ts) ? new Long(ts) : null); + } else if (APP_AUTH_TYPE_MD5.equals(authType)) { + md5AuthProcessor.auth(appId, token, StringUtils.isNotBlank(ts) ? new Long(ts) : null); + } else { + throw new RenException(EpmetErrorCode.OPER_EXTERNAL_APP_AUTH_ERROR.getCode(), "未知的认证类型"); + } + } catch (RenException e) { + return response(exchange, new Result<>().error(e.getCode(), e.getMsg())); + } catch (Exception e) { + logger.error("外部应用请求认证发生未知错误:" + ExceptionUtils.getErrorStackTrace(e)); + return response(exchange, new Result<>().error("外部应用请求认证发生未知错误")); + } + + return chain.filter(exchange); + } +} diff --git a/epmet-gateway/src/main/java/com/epmet/auth/InternalAuthProcessor.java b/epmet-gateway/src/main/java/com/epmet/auth/InternalAuthProcessor.java new file mode 100644 index 0000000000..ee247c843b --- /dev/null +++ b/epmet-gateway/src/main/java/com/epmet/auth/InternalAuthProcessor.java @@ -0,0 +1,175 @@ +package com.epmet.auth; + +import com.epmet.commons.tools.constant.AppClientConstant; +import com.epmet.commons.tools.constant.Constant; +import com.epmet.commons.tools.exception.EpmetErrorCode; +import com.epmet.commons.tools.exception.RenException; +import com.epmet.commons.tools.security.dto.BaseTokenDto; +import com.epmet.commons.tools.security.dto.GovTokenDto; +import com.epmet.commons.tools.security.dto.TokenDto; +import com.epmet.commons.tools.utils.CpUserDetailRedis; +import com.epmet.commons.tools.utils.Result; +import com.epmet.jwt.JwtTokenUtils; +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.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.http.HttpHeaders; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * 内部认证处理器 + */ +@Component +public class InternalAuthProcessor extends AuthProcessor { + + private Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + private JwtTokenUtils jwtTokenUtils; + + @Autowired + private CpUserDetailRedis cpUserDetailRedis; + + @Override + public Mono auth(ServerWebExchange exchange, GatewayFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + String requestUri = request.getPath().pathWithinApplication().value(); + + logger.info("CpAuthGatewayFilterFactory当前requestUri=[" + requestUri + "]CpAuthGatewayFilterFactory拦截成功"); + String token = getTokenFromRequest(request); + //BaseTokenDto baseTokenDto = StringUtils.isNotBlank(token) ? getBaseTokenDto(token, jwtTokenUtils) : null; + BaseTokenDto baseTokenDto; + if(StringUtils.isNotBlank(token)){ + try{ + baseTokenDto = getBaseTokenDto(token, jwtTokenUtils); + }catch(RenException e){ + return response(exchange,new Result<>().error(e.getCode(),e.getMsg())); + } + }else{ + baseTokenDto = null; + } + + String customerId = ""; + + if (baseTokenDto != null) { + if (AppClientConstant.APP_RESI.equals(baseTokenDto.getApp())) { + // 居民端 + TokenDto resiTokenDto = getLoginUserInfoByToken(token, jwtTokenUtils, TokenDto.class); + if (resiTokenDto != null) { + customerId = resiTokenDto.getCustomerId(); + baseTokenDto = resiTokenDto; + } + } else if (AppClientConstant.APP_GOV.equals(baseTokenDto.getApp())) { + // 政府端 + GovTokenDto govTokenDto = getLoginUserInfoByToken(token, jwtTokenUtils, GovTokenDto.class); + if (govTokenDto != null) { + customerId = govTokenDto.getCustomerId(); + baseTokenDto = govTokenDto; + } + } else if(AppClientConstant.APP_OPER.equals(baseTokenDto.getApp())){ + //运营端 + TokenDto resiTokenDto = getLoginUserInfoByToken(token, jwtTokenUtils, TokenDto.class); + if (resiTokenDto != null) { + customerId = resiTokenDto.getCustomerId(); + baseTokenDto = resiTokenDto; + } + } + } + + // 校验token + if (StringUtils.isBlank(token)) { + return response(exchange,new Result<>().error(EpmetErrorCode.ERR10005.getCode(),EpmetErrorCode.ERR10005.getMsg())); + } + try { + validateTokenDto(baseTokenDto, token); + } catch (RenException e) { + return response(exchange,new Result<>().error(e.getCode(),e.getMsg())); + } + + // 添加header + if (baseTokenDto != null) { + String redisKey = baseTokenDto.getApp() + "-" + baseTokenDto.getClient() + "-" + baseTokenDto.getUserId(); + logger.info("redisKey=" + redisKey); + exchange.getRequest().mutate() + .header(Constant.APP_USER_KEY, redisKey) + .header(AppClientConstant.APP,baseTokenDto.getApp()) + .header(AppClientConstant.CLIENT,baseTokenDto.getClient()) + .header(AppClientConstant.USER_ID,baseTokenDto.getUserId()); + + if (StringUtils.equals(baseTokenDto.getApp(), "gov")) {//工作端 + if(StringUtils.isNotBlank(customerId)){ + exchange.getRequest().mutate().header(AppClientConstant.CUSTOMER_ID, customerId); + } + } else if (StringUtils.equals(baseTokenDto.getApp(), "public")) {//公众号端 + exchange.getRequest().mutate().header(AppClientConstant.CUSTOMER_ID, customerId); + } + ServerHttpRequest build = exchange.getRequest().mutate().build(); + return chain.filter(exchange.mutate().request(build).build()); + } + + return chain.filter(exchange); + } + + /** + * 从请求中获取token + * @param request + * @return + */ + private String getTokenFromRequest(ServerHttpRequest request) { + HttpHeaders headers = request.getHeaders(); + String token = headers.getFirst(Constant.AUTHORIZATION_HEADER); + if (StringUtils.isBlank(token)) { + token = headers.getFirst(Constant.TOKEN_HEADER); + } + if (StringUtils.isBlank(token)) { + token = request.getQueryParams().getFirst(Constant.AUTHORIZATION_HEADER); + } + return token; + } + + private BaseTokenDto getBaseTokenDto(String token, JwtTokenUtils jwtTokenUtils) { + //是否过期 + Claims claims = jwtTokenUtils.getClaimByToken(token); + if (claims == null || jwtTokenUtils.isTokenExpired(claims.getExpiration())) { + return null; + } + //获取用户ID + String app = (String) claims.get("app"); + String client = (String) claims.get("client"); + String userId = (String) claims.get("userId"); + return new BaseTokenDto(app, client, userId, token); + } + + private T getLoginUserInfoByToken(String token, JwtTokenUtils jwtTokenUtils, Class clz) { + BaseTokenDto baseTokenDto = getBaseTokenDto(token, jwtTokenUtils); + //查询Redis + return cpUserDetailRedis.get(baseTokenDto.getApp(), baseTokenDto.getClient(), baseTokenDto.getUserId(), clz); + } + + /** + * 校验Token是否异常 + * @param tokenDto + * @param tokenStr + */ + private void validateTokenDto(BaseTokenDto tokenDto, String tokenStr) { + if (null == tokenDto) { + //说明登录状态时效(超时) + throw new RenException(EpmetErrorCode.ERR10006.getCode()); + }else{ + //Redis中存在数据,取出token,进行比对 + if(StringUtils.equals(tokenDto.getToken(),tokenStr)){ + //用户携带token与Redis中一致 + + }else{ + //用户携带token与Redis中不一致,说明当前用户此次会话失效,提示重新登陆 + throw new RenException(EpmetErrorCode.ERR10007.getCode()); + } + } + } +} diff --git a/epmet-gateway/src/main/java/com/epmet/filter/CpAuthGatewayFilterFactory.java b/epmet-gateway/src/main/java/com/epmet/filter/CpAuthGatewayFilterFactory.java index 7c74fa6763..59f52483b2 100644 --- a/epmet-gateway/src/main/java/com/epmet/filter/CpAuthGatewayFilterFactory.java +++ b/epmet-gateway/src/main/java/com/epmet/filter/CpAuthGatewayFilterFactory.java @@ -2,16 +2,12 @@ package com.epmet.filter; import com.alibaba.fastjson.JSON; +import com.epmet.auth.ExternalAuthProcessor; +import com.epmet.auth.InternalAuthProcessor; import com.epmet.commons.tools.constant.AppClientConstant; import com.epmet.commons.tools.constant.Constant; import com.epmet.commons.tools.exception.EpmetErrorCode; -import com.epmet.commons.tools.exception.RenException; -import com.epmet.commons.tools.security.dto.BaseTokenDto; -import com.epmet.commons.tools.security.dto.GovTokenDto; -import com.epmet.commons.tools.security.dto.TokenDto; -import com.epmet.commons.tools.utils.CpUserDetailRedis; import com.epmet.commons.tools.utils.Result; -import com.epmet.jwt.JwtTokenUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,16 +36,25 @@ import java.util.List; * @since 1.0.0 */ @Component("CpAuth") -public class CpAuthGatewayFilterFactory extends AbstractGatewayFilterFactory implements UserTokenFilter { +public class CpAuthGatewayFilterFactory extends AbstractGatewayFilterFactory { + private Logger logger = LoggerFactory.getLogger(getClass()); - @Autowired - private CpProperty cpProperty; + private final AntPathMatcher antPathMatcher = new AntPathMatcher(); + + public static final String AUTH_TYPE_INTERNAL = "internal"; + public static final String AUTH_TYPE_EXTERNAL = "external"; + public static final String AUTH_TYPE_NO_NEED = "no_need"; + public static final String AUTH_TYPE_UNKNOW = "unknow"; + @Autowired - private JwtTokenUtils jwtTokenUtils; + private CpProperty cpProperty; + @Autowired - private CpUserDetailRedis cpUserDetailRedis; + private InternalAuthProcessor internalAuthProcessor; + @Autowired + private ExternalAuthProcessor externalAuthProcessor; @Override public List shortcutFieldOrder() { @@ -67,82 +72,23 @@ public class CpAuthGatewayFilterFactory extends AbstractGatewayFilterFactory().error(e.getCode(),e.getMsg())); - } - }else{ - baseTokenDto = null; - } - - String customerId = ""; - - if (baseTokenDto != null) { - if (AppClientConstant.APP_RESI.equals(baseTokenDto.getApp())) { - // 居民端 - TokenDto resiTokenDto = getLoginUserInfoByToken(token, jwtTokenUtils, cpUserDetailRedis, TokenDto.class); - if (resiTokenDto != null) { - customerId = resiTokenDto.getCustomerId(); - baseTokenDto = resiTokenDto; - } - } else if (AppClientConstant.APP_GOV.equals(baseTokenDto.getApp())) { - // 政府端 - GovTokenDto govTokenDto = getLoginUserInfoByToken(token, jwtTokenUtils, cpUserDetailRedis, GovTokenDto.class); - if (govTokenDto != null) { - customerId = govTokenDto.getCustomerId(); - baseTokenDto = govTokenDto; - } - } else if(AppClientConstant.APP_OPER.equals(baseTokenDto.getApp())){ - //运营端 - TokenDto resiTokenDto = getLoginUserInfoByToken(token, jwtTokenUtils, cpUserDetailRedis, TokenDto.class); - if (resiTokenDto != null) { - customerId = resiTokenDto.getCustomerId(); - baseTokenDto = resiTokenDto; - } - } - } - - //需要认证 - if (needAuth(requestUri)) { - if (StringUtils.isBlank(token)) { - return response(exchange,new Result<>().error(EpmetErrorCode.ERR10005.getCode(),EpmetErrorCode.ERR10005.getMsg())); - } - // 校验token - try { - validateTokenDto(baseTokenDto, token); - } catch (RenException e) { - return response(exchange,new Result<>().error(e.getCode(),e.getMsg())); - } - } //添加流水号 exchange.getRequest().mutate().header(AppClientConstant.TRANSACTION_SERIAL_KEY, new String[]{getTransactionSerial()}); - if (baseTokenDto != null) { - String redisKey = baseTokenDto.getApp() + "-" + baseTokenDto.getClient() + "-" + baseTokenDto.getUserId(); - logger.info("redisKey=" + redisKey); - exchange.getRequest().mutate() - .header(Constant.APP_USER_KEY, redisKey) - .header(AppClientConstant.APP,baseTokenDto.getApp()) - .header(AppClientConstant.CLIENT,baseTokenDto.getClient()) - .header(AppClientConstant.USER_ID,baseTokenDto.getUserId()) - ; - if (StringUtils.equals(baseTokenDto.getApp(), "gov")) {//工作端 - if(StringUtils.isNotBlank(customerId)){ - exchange.getRequest().mutate().header(AppClientConstant.CUSTOMER_ID, customerId); - } - } else if (StringUtils.equals(baseTokenDto.getApp(), "public")) {//公众号端 - exchange.getRequest().mutate().header(AppClientConstant.CUSTOMER_ID, customerId); - } - ServerHttpRequest build = exchange.getRequest().mutate().build(); - return chain.filter(exchange.mutate().request(build).build()); + + ServerHttpRequest request = exchange.getRequest(); + + String authType = getAuthType(request); + + switch (authType) { + case AUTH_TYPE_EXTERNAL: + return externalAuthProcessor.auth(exchange, chain); + case AUTH_TYPE_INTERNAL: + return internalAuthProcessor.auth(exchange, chain); + case AUTH_TYPE_NO_NEED: + break; + default: + return response(exchange, new Result<>().error(EpmetErrorCode.ERR401.getCode(), + EpmetErrorCode.ERR401.getMsg())); } return chain.filter(exchange); @@ -150,85 +96,71 @@ public class CpAuthGatewayFilterFactory extends AbstractGatewayFilterFactory response(ServerWebExchange exchange, Object object) { - String json = JSON.toJSONString(object); - DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(json.getBytes(StandardCharsets.UTF_8)); - exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8); - exchange.getResponse().setStatusCode(HttpStatus.OK); - return exchange.getResponse().writeWith(Flux.just(buffer)); + /** + * 获取请求头 + * @param request + * @return + */ + private String getHeader(ServerHttpRequest request, String headerName) { + HttpHeaders headers = request.getHeaders(); + return headers.getFirst(headerName); } /** - * 是否需要认证 - * @param requestUri + * 获取事务流水号 * @return */ - private boolean needAuth(String requestUri) { - // 优先判断白名单,在白名单中的就直接放行 - for (String url : cpProperty.getUrlWhiteList()) { - if (antPathMatcher.match(url, requestUri)) { - return false; - } - } + public static String getTransactionSerial() { + String[] letterPool = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n" + , "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}; - for (String url : cpProperty.getSwaggerUrls()) { - if (antPathMatcher.match(url, requestUri)) { - return false; - } + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 2; i++) { + sb.append(letterPool[(int) (Math.random() * 25)]); } - for (String url : cpProperty.getUrls()) { - if (antPathMatcher.match(url, requestUri)) { - return true; - } - } - return false; + sb.append(System.currentTimeMillis()); + return sb.toString(); } public static class CpAuthConfig { @@ -250,45 +182,12 @@ public class CpAuthGatewayFilterFactory extends AbstractGatewayFilterFactory response(ServerWebExchange exchange, Object object) { + String json = JSON.toJSONString(object); + DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(json.getBytes(StandardCharsets.UTF_8)); + exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8); + exchange.getResponse().setStatusCode(HttpStatus.OK); + return exchange.getResponse().writeWith(Flux.just(buffer)); } - /** - * 从请求中获取token - * @param request - * @return - */ - private String getTokenFromRequest(ServerHttpRequest request) { - HttpHeaders headers = request.getHeaders(); - String token = headers.getFirst(Constant.AUTHORIZATION_HEADER); - if (StringUtils.isBlank(token)) { - token = headers.getFirst(Constant.TOKEN_HEADER); - logger.info("token=" + token); - } else { - logger.info("authorization=" + token); - } - if (StringUtils.isBlank(token)) { - token = request.getQueryParams().getFirst(Constant.AUTHORIZATION_HEADER); - logger.info("params token:" + token); - } - return token; - } } diff --git a/epmet-gateway/src/main/java/com/epmet/filter/CpProperty.java b/epmet-gateway/src/main/java/com/epmet/filter/CpProperty.java index f52d8f13a2..84872c0fb2 100644 --- a/epmet-gateway/src/main/java/com/epmet/filter/CpProperty.java +++ b/epmet-gateway/src/main/java/com/epmet/filter/CpProperty.java @@ -17,12 +17,15 @@ import java.util.List; @ConfigurationProperties(prefix = "epmet") public class CpProperty { - private List urls; + /** + * 需要内部认证的url + */ + private List internalAuthUrls; /** - * 白名单 + * 需要外部认证的url */ - private List urlWhiteList; + private List externalAuthUrls; /** * 不处理token,直接通过 diff --git a/epmet-gateway/src/main/java/com/epmet/filter/UserTokenFilter.java b/epmet-gateway/src/main/java/com/epmet/filter/UserTokenFilter.java deleted file mode 100644 index f66b0e6707..0000000000 --- a/epmet-gateway/src/main/java/com/epmet/filter/UserTokenFilter.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.epmet.filter; - -import com.epmet.commons.tools.exception.EpmetErrorCode; -import com.epmet.commons.tools.exception.RenException; -import com.epmet.commons.tools.security.dto.BaseTokenDto; -import com.epmet.commons.tools.utils.CpUserDetailRedis; -import com.epmet.jwt.JwtTokenUtils; -import io.jsonwebtoken.Claims; - -/** - * 用户token的过滤器接口,提供通用的默认方法 - */ -public interface UserTokenFilter { - - default BaseTokenDto getBaseTokenDto(String token, JwtTokenUtils jwtTokenUtils) { - //是否过期 - Claims claims = jwtTokenUtils.getClaimByToken(token); - if (claims == null || jwtTokenUtils.isTokenExpired(claims.getExpiration())) { -// throw new RenException(EpmetErrorCode.ERR401.getCode()); - return null; - } - //获取用户ID - String app = (String) claims.get("app"); - String client = (String) claims.get("client"); - String userId = (String) claims.get("userId"); - return new BaseTokenDto(app, client, userId, token); - } - - default T getLoginUserInfoByToken(String token, JwtTokenUtils jwtTokenUtils, CpUserDetailRedis cpUserDetailRedis, Class clz) { - BaseTokenDto baseTokenDto = getBaseTokenDto(token, jwtTokenUtils); - //查询Redis - return cpUserDetailRedis.get(baseTokenDto.getApp(), baseTokenDto.getClient(), baseTokenDto.getUserId(), clz); - } - -} diff --git a/epmet-gateway/src/main/java/com/epmet/jwt/JwtTokenUtils.java b/epmet-gateway/src/main/java/com/epmet/jwt/JwtTokenUtils.java index 33baf31c52..452627b9a3 100644 --- a/epmet-gateway/src/main/java/com/epmet/jwt/JwtTokenUtils.java +++ b/epmet-gateway/src/main/java/com/epmet/jwt/JwtTokenUtils.java @@ -62,6 +62,18 @@ public class JwtTokenUtils { } } + public Claims getClaimByToken(String token, String secret) { + try { + return Jwts.parser() + .setSigningKey(secret) + .parseClaimsJws(token) + .getBody(); + } catch (Exception e) { + logger.debug("validate is token error, token = " + token, e); + return null; + } + } + /** * @return java.util.Date * @param token diff --git a/epmet-gateway/src/main/resources/bootstrap.yml b/epmet-gateway/src/main/resources/bootstrap.yml index 9748b1ca26..750440f69f 100644 --- a/epmet-gateway/src/main/resources/bootstrap.yml +++ b/epmet-gateway/src/main/resources/bootstrap.yml @@ -415,8 +415,8 @@ ribbon: ConnectTimeout: 300000 epmet: - # 党群e事通(校验是否登录) - urls: + # 内部认证,需要Authorization请求头 + internalAuthUrls: - /oper/customize/** - /oper/crm/** - /epmetuser/** @@ -438,8 +438,8 @@ epmet: - /resi/home/** - /data/report/** - # url认证白名单,先判断白名单,在白名单中的url直接放行,不再判断上述需要认证的名单 - urlWhiteList: + # 外部应用认证,使用AccessToken等头进行认证 + externalAuthUrls: - /data/report/test/test - /data/report/screen/** - /data/report/kcscreen/** 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 caa9bd6301..7ed9ff3f18 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 @@ -46,4 +46,11 @@ public interface EpmetCommonServiceOpenFeignClient { */ @PostMapping("/commonservice/externalapp/getcustomerids") Result> getExternalCustomerIds(); + + /** + * 查询秘钥(仅限内部使用) + * @return + */ + @PostMapping("/commonservice/externalapp/get-secret") + Result getSecret(@RequestBody String appId); } diff --git a/epmet-module/epmet-common-service/common-service-client/src/main/java/com/epmet/feign/fallback/EpmetCommonServiceOpenFeignClientFallback.java b/epmet-module/epmet-common-service/common-service-client/src/main/java/com/epmet/feign/fallback/EpmetCommonServiceOpenFeignClientFallback.java index f21808fc8c..4640f13b45 100644 --- a/epmet-module/epmet-common-service/common-service-client/src/main/java/com/epmet/feign/fallback/EpmetCommonServiceOpenFeignClientFallback.java +++ b/epmet-module/epmet-common-service/common-service-client/src/main/java/com/epmet/feign/fallback/EpmetCommonServiceOpenFeignClientFallback.java @@ -34,4 +34,9 @@ public class EpmetCommonServiceOpenFeignClientFallback implements EpmetCommonSer public Result> getExternalCustomerIds() { return ModuleUtils.feignConError(ServiceConstant.EPMET_COMMON_SERVICE, "getExternalCustomerIds", null); } + + @Override + public Result getSecret(String appId) { + return ModuleUtils.feignConError(ServiceConstant.EPMET_COMMON_SERVICE, "getSecret", appId); + } } diff --git a/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/controller/ExternalAppController.java b/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/controller/ExternalAppController.java index ffab33bbbe..f1229c7902 100644 --- a/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/controller/ExternalAppController.java +++ b/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/controller/ExternalAppController.java @@ -2,6 +2,7 @@ package com.epmet.controller; import com.epmet.commons.tools.exception.EpmetErrorCode; import com.epmet.commons.tools.exception.RenException; +import com.epmet.commons.tools.exception.ValidateException; import com.epmet.commons.tools.page.PageData; import com.epmet.commons.tools.utils.Result; import com.epmet.commons.tools.validator.ValidatorUtils; @@ -10,6 +11,7 @@ import com.epmet.dto.form.ExternalAppFormDTO; import com.epmet.dto.result.ExternalAppAuthResultDTO; import com.epmet.dto.result.ExternalAppResultDTO; import com.epmet.service.ExternalAppAuthService; +import com.epmet.service.ExternalAppSecretService; import com.epmet.service.ExternalAppService; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -34,6 +36,9 @@ public class ExternalAppController { @Autowired private ExternalAppService externalAppService; + @Autowired + private ExternalAppSecretService externalAppSecretService; + /** * 外部请求认证 * @param formDTO @@ -128,4 +133,17 @@ public class ExternalAppController { return new Result().ok(newSecret); } + /** + * 查询秘钥(仅限内部使用) + * @return + */ + @PostMapping("/get-secret") + public Result getSecret(@RequestBody String appId) { + if (StringUtils.isBlank(appId)) { + throw new ValidateException(EpmetErrorCode.CUSTOMER_VALIDATE_ERROR.getCode(), "缺少应用ID参数"); + } + String secret = externalAppSecretService.getSecretByAppId(appId); + return new Result().ok(secret); + } + } diff --git a/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/dao/ExternalAppSecretDao.java b/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/dao/ExternalAppSecretDao.java index fd2342c7c6..7faeae8ed4 100644 --- a/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/dao/ExternalAppSecretDao.java +++ b/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/dao/ExternalAppSecretDao.java @@ -41,4 +41,6 @@ public interface ExternalAppSecretDao extends BaseDao { ExternalAppSecretEntity getSecretsByAppId(@Param("appId") String appId); int updateSecret(@Param("appId") String appId, @Param("secret") String secret); + + String getSecretByAppId(String appId); } \ No newline at end of file diff --git a/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/service/ExternalAppSecretService.java b/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/service/ExternalAppSecretService.java index 7f6be4bd5a..3a68251684 100644 --- a/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/service/ExternalAppSecretService.java +++ b/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/service/ExternalAppSecretService.java @@ -25,4 +25,5 @@ package com.epmet.service; * @since v1.0.0 2020-08-18 */ public interface ExternalAppSecretService { + String getSecretByAppId(String appId); } \ No newline at end of file diff --git a/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/service/impl/ExternalAppSecretServiceImpl.java b/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/service/impl/ExternalAppSecretServiceImpl.java index 567baf3fb2..da89e8ce1c 100644 --- a/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/service/impl/ExternalAppSecretServiceImpl.java +++ b/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/service/impl/ExternalAppSecretServiceImpl.java @@ -17,7 +17,9 @@ package com.epmet.service.impl; +import com.epmet.dao.ExternalAppSecretDao; import com.epmet.service.ExternalAppSecretService; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * 外部应用秘钥列表 @@ -28,4 +30,11 @@ import org.springframework.stereotype.Service; @Service public class ExternalAppSecretServiceImpl implements ExternalAppSecretService { + @Autowired + private ExternalAppSecretDao externalAppSecretDao; + + @Override + public String getSecretByAppId(String appId) { + return externalAppSecretDao.getSecretByAppId(appId); + } } \ No newline at end of file diff --git a/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/utils/externalapp/ExtAppJwtTokenUtils.java b/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/utils/externalapp/ExtAppJwtTokenUtils.java index 490b2445e8..0f1674b319 100644 --- a/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/utils/externalapp/ExtAppJwtTokenUtils.java +++ b/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/utils/externalapp/ExtAppJwtTokenUtils.java @@ -80,14 +80,13 @@ public class ExtAppJwtTokenUtils { //String appId = "acc4ad66c82a7b46e741364b4c62dce2"; // String customrId = "b09527201c4409e19d1dbc5e3c3429a1"; //孔村 - String secret = "657cd46d385a4c2ba6d9355aee24654ac3951deab7e6436e91201561b94969b5"; - String appId = "5efcfb775125d656f39583b8110a3d7d"; + String secret = "c4096eb0497943c78327c5192621b209c38f20592f6a49cc8c79e8b77f3bd5c8"; + String appId = "f358d63a89f3670c197c62ca4c3a0366"; String customrId = "2fe0065f70ca0e23ce4c26fca5f1d933"; claim.put("customerId", customrId); claim.put("appId", appId); - claim.put("customerId", customrId); - long ts = System.currentTimeMillis() - 1000 * 60 * 4; + long ts = System.currentTimeMillis() + 1000 * 60 * 1; System.out.println("时间戳:" + ts); claim.put("ts", ts); diff --git a/epmet-module/epmet-common-service/common-service-server/src/main/resources/mapper/ExternalAppSecretDao.xml b/epmet-module/epmet-common-service/common-service-server/src/main/resources/mapper/ExternalAppSecretDao.xml index a192df36b1..f363e43d7c 100644 --- a/epmet-module/epmet-common-service/common-service-server/src/main/resources/mapper/ExternalAppSecretDao.xml +++ b/epmet-module/epmet-common-service/common-service-server/src/main/resources/mapper/ExternalAppSecretDao.xml @@ -40,5 +40,13 @@ AND DEL_FLAG = 0 + + + \ No newline at end of file