diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message.properties index cdda51fd51..fc398b3245 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message.properties @@ -216,6 +216,7 @@ ##业务错误-用户服务、登录服务 1247001=用户不存在或者未登录 +1247403=无该应用访问权限 diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en.properties index a63ebdb2e7..797de2d341 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en.properties @@ -216,6 +216,7 @@ ## Business error - User/Login 1247001=User does not exist or is not logged in +1247403=Do not have access permission for the current application ## Business error - Backup 1249001=Fail to get node info from artifactory diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en_US.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en_US.properties index a63ebdb2e7..797de2d341 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en_US.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en_US.properties @@ -216,6 +216,7 @@ ## Business error - User/Login 1247001=User does not exist or is not logged in +1247403=Do not have access permission for the current application ## Business error - Backup 1249001=Fail to get node info from artifactory diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh.properties index cdda51fd51..fc398b3245 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh.properties @@ -216,6 +216,7 @@ ##业务错误-用户服务、登录服务 1247001=用户不存在或者未登录 +1247403=无该应用访问权限 diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh_CN.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh_CN.properties index cdda51fd51..fc398b3245 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh_CN.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh_CN.properties @@ -216,6 +216,7 @@ ##业务错误-用户服务、登录服务 1247001=用户不存在或者未登录 +1247403=无该应用访问权限 diff --git a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/constant/ErrorCode.java b/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/constant/ErrorCode.java index 0f24a926b6..309a397bc4 100644 --- a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/constant/ErrorCode.java +++ b/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/constant/ErrorCode.java @@ -273,6 +273,8 @@ public class ErrorCode { // 用户服务 start // 用户不存在或者未登录 public static final int USER_NOT_EXIST_OR_NOT_LOGIN_IN = 1247001; + // 用户认证成功,但用户无应用访问权限 + public static final int USER_ACCESS_APP_FORBIDDEN = 1247403; // 用户服务 end // 业务网关 start diff --git a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/model/Response.java b/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/model/Response.java index fae8eed059..e41359abd2 100644 --- a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/model/Response.java +++ b/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/model/Response.java @@ -58,7 +58,7 @@ public class Response { @ApiModelProperty("错误信息") private String errorMsg; - @ApiModelProperty("请求成功返回的数据") + @ApiModelProperty("请求成功/失败返回的数据") private T data; @ApiModelProperty("请求 ID") diff --git a/src/backend/commons/esb-sdk/src/main/java/com/tencent/bk/job/common/esb/sdk/AbstractEsbSdkClient.java b/src/backend/commons/esb-sdk/src/main/java/com/tencent/bk/job/common/esb/sdk/AbstractEsbSdkClient.java index cf1c6c6a00..d960721b41 100644 --- a/src/backend/commons/esb-sdk/src/main/java/com/tencent/bk/job/common/esb/sdk/AbstractEsbSdkClient.java +++ b/src/backend/commons/esb-sdk/src/main/java/com/tencent/bk/job/common/esb/sdk/AbstractEsbSdkClient.java @@ -48,6 +48,10 @@ * ESB API 调用基础实现 */ public abstract class AbstractEsbSdkClient { + + // 请求成功 + protected static final Integer ESB_CODE_OK = 0; + private final Logger log = LoggerFactory.getLogger(this.getClass()); private static final JsonMapper JSON_MAPPER = JsonMapper.nonDefaultMapper(); @@ -150,6 +154,10 @@ private void requestEsbApi(BkApiContext apiContext, } esbResp = JSON_MAPPER.fromJson(respStr, typeReference); + if (esbResp == null) { + log.warn("[AbstractEsbSdkClient] warn:esbResp is null after JSON parse, respStr={}", respStr); + throw new InternalException("Esb api resp unexpected, fail to parse json data", ErrorCode.API_ERROR); + } apiContext.setResp(esbResp); if (!esbResp.getResult()) { log.warn( diff --git a/src/backend/commons/paas-sdk/src/main/java/com/tencent/bk/job/common/paas/exception/AppPermissionDeniedException.java b/src/backend/commons/paas-sdk/src/main/java/com/tencent/bk/job/common/paas/exception/AppPermissionDeniedException.java new file mode 100644 index 0000000000..ba74d68d28 --- /dev/null +++ b/src/backend/commons/paas-sdk/src/main/java/com/tencent/bk/job/common/paas/exception/AppPermissionDeniedException.java @@ -0,0 +1,51 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.common.paas.exception; + +import com.tencent.bk.job.common.constant.ErrorCode; +import com.tencent.bk.job.common.exception.ServiceException; +import com.tencent.bk.job.common.model.error.ErrorType; +import lombok.Getter; +import lombok.ToString; + +/** + * 应用权限不足异常 + */ +@Getter +@ToString +public class AppPermissionDeniedException extends ServiceException { + + // 源于用户管理接口:进一步的操作提示 + private final String message; + + public AppPermissionDeniedException(String message) { + super(ErrorType.PERMISSION_DENIED, ErrorCode.USER_ACCESS_APP_FORBIDDEN); + this.message = message; + } + + public String getMessage() { + return message; + } +} diff --git a/src/backend/commons/paas-sdk/src/main/java/com/tencent/bk/job/common/paas/login/EELoginClient.java b/src/backend/commons/paas-sdk/src/main/java/com/tencent/bk/job/common/paas/login/EELoginClient.java index 4c55d2f528..f7e6979fd3 100644 --- a/src/backend/commons/paas-sdk/src/main/java/com/tencent/bk/job/common/paas/login/EELoginClient.java +++ b/src/backend/commons/paas-sdk/src/main/java/com/tencent/bk/job/common/paas/login/EELoginClient.java @@ -32,6 +32,7 @@ import com.tencent.bk.job.common.exception.InternalUserManageException; import com.tencent.bk.job.common.metrics.CommonMetricNames; import com.tencent.bk.job.common.model.dto.BkUserDTO; +import com.tencent.bk.job.common.paas.exception.AppPermissionDeniedException; import com.tencent.bk.job.common.paas.model.EsbUserDto; import com.tencent.bk.job.common.util.http.HttpMetricUtil; import io.micrometer.core.instrument.Tag; @@ -40,6 +41,14 @@ @Slf4j public class EELoginClient extends AbstractEsbSdkClient implements ILoginClient { + + // 用户认证失败,即用户登录态无效 + private static final Integer ESB_CODE_USER_NOT_LOGIN = 1302100; + // 用户不存在 + private static final Integer ESB_CODE_USER_NOT_EXIST = 1302103; + // 用户认证成功,但用户无应用访问权限 + private static final Integer ESB_CODE_USER_NO_APP_PERMISSION = 1302403; + private static final String API_GET_USER_INFO = "/api/c/compapi/v2/bk_login/get_user/"; public EELoginClient(String esbHostUrl, String appCode, String appSecret, String lang, boolean useEsbTestEnv) { @@ -81,7 +90,13 @@ private BkUserDTO getUserInfo(EsbReq esbReq) { new TypeReference>() { } ); - return convertToBkUserDTO(esbResp.getData()); + Integer code = esbResp.getCode(); + if (ESB_CODE_OK.equals(code)) { + return convertToBkUserDTO(esbResp.getData()); + } else { + handleNotOkResp(esbResp); + return null; + } } catch (Exception e) { String errorMsg = "Get " + API_GET_USER_INFO + " error"; log.error(errorMsg, e); @@ -91,6 +106,17 @@ private BkUserDTO getUserInfo(EsbReq esbReq) { } } + private void handleNotOkResp(EsbResp esbResp) { + Integer code = esbResp.getCode(); + if (ESB_CODE_USER_NO_APP_PERMISSION.equals(code)) { + throw new AppPermissionDeniedException(esbResp.getMessage()); + } else if (ESB_CODE_USER_NOT_LOGIN.equals(code)) { + log.info("User not login, esbResp.code={}, esbResp.message={}", esbResp.getCode(), esbResp.getMessage()); + } else if (ESB_CODE_USER_NOT_EXIST.equals(code)) { + log.info("User not exist, esbResp.code={}, esbResp.message={}", esbResp.getCode(), esbResp.getMessage()); + } + } + private BkUserDTO convertToBkUserDTO(EsbUserDto esbUserDto) { BkUserDTO bkUserDTO = new BkUserDTO(); bkUserDTO.setUsername(esbUserDto.getUsername()); diff --git a/src/backend/job-gateway/src/main/java/com/tencent/bk/job/gateway/filter/web/AuthorizeGatewayFilterFactory.java b/src/backend/job-gateway/src/main/java/com/tencent/bk/job/gateway/filter/web/AuthorizeGatewayFilterFactory.java index faf7edff43..d788830aab 100644 --- a/src/backend/job-gateway/src/main/java/com/tencent/bk/job/gateway/filter/web/AuthorizeGatewayFilterFactory.java +++ b/src/backend/job-gateway/src/main/java/com/tencent/bk/job/gateway/filter/web/AuthorizeGatewayFilterFactory.java @@ -24,8 +24,12 @@ package com.tencent.bk.job.gateway.filter.web; +import com.tencent.bk.job.common.constant.ErrorCode; +import com.tencent.bk.job.common.model.Response; import com.tencent.bk.job.common.model.dto.BkUserDTO; +import com.tencent.bk.job.common.paas.exception.AppPermissionDeniedException; import com.tencent.bk.job.common.util.RequestUtil; +import com.tencent.bk.job.common.util.json.JsonUtils; import com.tencent.bk.job.gateway.config.LoginExemptionConfig; import com.tencent.bk.job.gateway.web.service.LoginService; import lombok.extern.slf4j.Slf4j; @@ -33,12 +37,15 @@ 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.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; +import reactor.core.publisher.Mono; +import java.nio.charset.StandardCharsets; import java.util.List; /** @@ -87,13 +94,11 @@ private GatewayFilter getLoginFilter() { response.getHeaders().add("x-login-url", loginService.getLoginRedirectUrl()); return response.setComplete(); } - BkUserDTO user = null; - // 遍历所有传入token找出当前环境的 - for (String bkToken : bkTokenList) { - user = loginService.getUser(bkToken); - if (user != null) { - break; - } + BkUserDTO user; + try { + user = getUserByTokenList(bkTokenList); + } catch (AppPermissionDeniedException e) { + return getUserAccessAppForbiddenResp(response, e.getMessage()); } if (user == null) { log.warn("Invalid user token"); @@ -108,6 +113,27 @@ private GatewayFilter getLoginFilter() { }; } + private Mono getUserAccessAppForbiddenResp(ServerHttpResponse response, String data) { + Response resp = new Response<>(ErrorCode.USER_ACCESS_APP_FORBIDDEN, data); + response.setStatusCode(HttpStatus.FORBIDDEN); + String body = JsonUtils.toJson(resp); + DataBuffer dataBuffer = response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8)); + response.getHeaders().setContentLength(body.length()); + response.writeWith(Mono.just(dataBuffer)).subscribe(); + return response.setComplete(); + } + + private BkUserDTO getUserByTokenList(List bkTokenList) { + // 遍历所有传入token找出当前环境的 + for (String bkToken : bkTokenList) { + BkUserDTO user = loginService.getUser(bkToken); + if (user != null) { + return user; + } + } + return null; + } + @Override public GatewayFilter apply(Config config) { if (loginExemptionConfig.isEnableLoginExemption()) {