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/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/ExternalAuthProcessor.java b/epmet-gateway/src/main/java/com/epmet/auth/ExternalAuthProcessor.java new file mode 100644 index 0000000000..f008fa8eda --- /dev/null +++ b/epmet-gateway/src/main/java/com/epmet/auth/ExternalAuthProcessor.java @@ -0,0 +1,21 @@ +package com.epmet.auth; + +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * 外部应用认证 + */ +@Component +public class ExternalAuthProcessor extends AuthProcessor { + + @Override + public Mono auth(ServerWebExchange exchange, GatewayFilterChain chain) { + + + + return null; + } +} 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..9d530b6c55 100644 --- a/epmet-gateway/src/main/java/com/epmet/filter/CpAuthGatewayFilterFactory.java +++ b/epmet-gateway/src/main/java/com/epmet/filter/CpAuthGatewayFilterFactory.java @@ -1,35 +1,24 @@ 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; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; -import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.util.AntPathMatcher; -import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; @@ -40,16 +29,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 +65,22 @@ 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_UNKNOW: + throw new RenException(EpmetErrorCode.ERR401.getCode(), "无法确定认证方式"); + case AUTH_TYPE_NO_NEED: + break; } return chain.filter(exchange); @@ -150,85 +88,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 +174,4 @@ public class CpAuthGatewayFilterFactory extends AbstractGatewayFilterFactory urls; + /** + * 需要内部认证的url + */ + private List internalAuthUrls; + + /** + * 需要外部认证的url + */ + private List externalAuthUrls; /** * 白名单 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/resources/bootstrap.yml b/epmet-gateway/src/main/resources/bootstrap.yml index 9748b1ca26..32724f1913 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,13 +438,16 @@ epmet: - /resi/home/** - /data/report/** - # url认证白名单,先判断白名单,在白名单中的url直接放行,不再判断上述需要认证的名单 - urlWhiteList: + # 外部应用认证,使用AccessToken等头进行认证 + externalAuthUrls: - /data/report/test/test - /data/report/screen/** - /data/report/kcscreen/** - /epmetuser/customerstaff/customerlist + # url认证白名单,先判断白名单,在白名单中的url直接放行,不再判断上述需要认证的名单 + urlWhiteList: + swaggerUrls: jwt: