From 2ca2bb8a8e12e409784b16b7fc16de98a3d9e303 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Thu, 20 Jul 2023 07:42:23 +0800 Subject: [PATCH] =?UTF-8?q?feature:=20=E6=94=AF=E6=8C=81=E8=93=9D=E9=B2=B8?= =?UTF-8?q?=E5=BA=94=E7=94=A8=E7=BA=A7=E5=88=AB=E7=9A=84=E6=9D=83=E9=99=90?= =?UTF-8?q?=E6=8E=A7=E5=88=B6=E8=B7=B3=E8=BD=AC=20#2238?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持国际化 --- .../job/common/i18n/locale/LocaleUtils.java | 4 ++ .../i18n/exception/message_en.properties | 2 +- .../i18n/exception/message_en_US.properties | 2 +- .../bk/job/gateway/config/RouteConfig.java | 4 +- .../job/gateway/config/SdkClientConfig.java | 30 +++++++-- .../web/AuthorizeGatewayFilterFactory.java | 23 +++++-- .../job/gateway/web/service/LoginService.java | 3 +- .../web/service/impl/LoginServiceImpl.java | 64 ++++++++++++++++--- .../src/main/resources/application.yml | 14 ++-- 9 files changed, 115 insertions(+), 31 deletions(-) diff --git a/src/backend/commons/common-i18n/src/main/java/com/tencent/bk/job/common/i18n/locale/LocaleUtils.java b/src/backend/commons/common-i18n/src/main/java/com/tencent/bk/job/common/i18n/locale/LocaleUtils.java index 1de0bc1294..9ea4b7ff19 100644 --- a/src/backend/commons/common-i18n/src/main/java/com/tencent/bk/job/common/i18n/locale/LocaleUtils.java +++ b/src/backend/commons/common-i18n/src/main/java/com/tencent/bk/job/common/i18n/locale/LocaleUtils.java @@ -38,6 +38,10 @@ public class LocaleUtils { public static final String LANG_ZH = "zh"; public static final String LANG_EN = "en"; public static final String LANG_EN_US = "en_US"; + /** + * 蓝鲸通用的LANG HEADER + */ + public static final String BLUEKING_LANG_HEADER = "blueking_language"; /** * 定义项目通用的LANG HEADER */ 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 797de2d341..28f4474466 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,7 +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 +1247403=No access permission for this 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 797de2d341..28f4474466 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,7 +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 +1247403=No access permission for this application ## Business error - Backup 1249001=Fail to get node info from artifactory diff --git a/src/backend/job-gateway/src/main/java/com/tencent/bk/job/gateway/config/RouteConfig.java b/src/backend/job-gateway/src/main/java/com/tencent/bk/job/gateway/config/RouteConfig.java index 88a8d0ee7f..f8647f32e6 100644 --- a/src/backend/job-gateway/src/main/java/com/tencent/bk/job/gateway/config/RouteConfig.java +++ b/src/backend/job-gateway/src/main/java/com/tencent/bk/job/gateway/config/RouteConfig.java @@ -46,6 +46,7 @@ import java.util.List; +import static com.tencent.bk.job.common.i18n.locale.LocaleUtils.COMMON_LANG_HEADER; import static org.springframework.web.reactive.function.server.RequestPredicates.GET; @Slf4j @@ -95,6 +96,7 @@ Mono getUserByBkToken(ServerRequest request) { String tokenCookieName = loginService.getCookieNameForToken(); List cookieList = request.headers().header("cookie"); + String lang = request.headers().firstHeader(COMMON_LANG_HEADER); List bkTokenList = RequestUtil.getCookieValuesFromCookies(cookieList, tokenCookieName); if (CollectionUtils.isEmpty(bkTokenList)) { @@ -107,7 +109,7 @@ Mono getUserByBkToken(ServerRequest request) { BkUserDTO user = null; // 遍历所有传入token找出当前环境的 for (String bkToken : bkTokenList) { - user = loginService.getUser(bkToken); + user = loginService.getUser(bkToken, lang); if (user != null) { break; } diff --git a/src/backend/job-gateway/src/main/java/com/tencent/bk/job/gateway/config/SdkClientConfig.java b/src/backend/job-gateway/src/main/java/com/tencent/bk/job/gateway/config/SdkClientConfig.java index 9533ecc21d..7ddd6b0065 100644 --- a/src/backend/job-gateway/src/main/java/com/tencent/bk/job/gateway/config/SdkClientConfig.java +++ b/src/backend/job-gateway/src/main/java/com/tencent/bk/job/gateway/config/SdkClientConfig.java @@ -24,6 +24,7 @@ package com.tencent.bk.job.gateway.config; +import com.tencent.bk.job.common.i18n.locale.LocaleUtils; import com.tencent.bk.job.common.paas.login.CustomLoginClient; import com.tencent.bk.job.common.paas.login.EELoginClient; import com.tencent.bk.job.common.paas.login.ILoginClient; @@ -44,12 +45,31 @@ public ILoginClient innerLoginClient(@Autowired BkConfig bkConfig) { return new CustomLoginClient(bkConfig.getCustomLoginApiUrl()); } - @Bean + @Bean(name = "enLoginClient") + @ConditionalOnProperty(value = "paas.login.custom.enabled", havingValue = "false", matchIfMissing = true) + @Primary + public ILoginClient enLoginClient(@Autowired BkConfig bkConfig) { + log.info("Init standard en login client"); + return new EELoginClient( + bkConfig.getEsbUrl(), + bkConfig.getAppCode(), + bkConfig.getAppSecret(), + LocaleUtils.LANG_EN, + bkConfig.isUseEsbTestEnv() + ); + } + + @Bean(name = "cnLoginClient") @ConditionalOnProperty(value = "paas.login.custom.enabled", havingValue = "false", matchIfMissing = true) @Primary - public ILoginClient standardLoginClient(@Autowired BkConfig bkConfig) { - log.info("Init standard login client"); - return new EELoginClient(bkConfig.getEsbUrl(), bkConfig.getAppCode(), bkConfig.getAppSecret(), - bkConfig.isUseEsbTestEnv()); + public ILoginClient cnLoginClient(@Autowired BkConfig bkConfig) { + log.info("Init standard cn login client"); + return new EELoginClient( + bkConfig.getEsbUrl(), + bkConfig.getAppCode(), + bkConfig.getAppSecret(), + LocaleUtils.LANG_ZH_CN, + bkConfig.isUseEsbTestEnv() + ); } } 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 d788830aab..e69c48106b 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 @@ -25,6 +25,7 @@ package com.tencent.bk.job.gateway.filter.web; import com.tencent.bk.job.common.constant.ErrorCode; +import com.tencent.bk.job.common.i18n.locale.LocaleUtils; 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; @@ -37,8 +38,10 @@ 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.context.i18n.LocaleContextHolder; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; @@ -48,6 +51,8 @@ import java.nio.charset.StandardCharsets; import java.util.List; +import static com.tencent.bk.job.common.i18n.locale.LocaleUtils.BLUEKING_LANG_HEADER; + /** * 用户token校验 */ @@ -81,6 +86,12 @@ private GatewayFilter getLoginFilter() { ServerHttpResponse response = exchange.getResponse(); String tokenCookieName = loginService.getCookieNameForToken(); List bkTokenList = RequestUtil.getCookieValuesFromHeader(request, tokenCookieName); + String lang = RequestUtil.getCookieValue(request, BLUEKING_LANG_HEADER); + if (StringUtils.isBlank(lang)) { + lang = LocaleUtils.LANG_EN; + log.warn("Cannot find blueking_language in cookie, use en"); + } + LocaleContextHolder.setLocale(LocaleUtils.getLocale(lang), true); if (CollectionUtils.isEmpty(bkTokenList)) { log.warn("Fail to parse token from headers, please check"); String bkToken = RequestUtil.getCookieValue(request, tokenCookieName); @@ -96,7 +107,7 @@ private GatewayFilter getLoginFilter() { } BkUserDTO user; try { - user = getUserByTokenList(bkTokenList); + user = getUserByTokenList(bkTokenList, lang); } catch (AppPermissionDeniedException e) { return getUserAccessAppForbiddenResp(response, e.getMessage()); } @@ -117,16 +128,18 @@ private Mono getUserAccessAppForbiddenResp(ServerHttpResponse response, St 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()); + byte[] bodyBytes = body.getBytes(StandardCharsets.UTF_8); + DataBuffer dataBuffer = response.bufferFactory().wrap(bodyBytes); + response.getHeaders().setContentLength(bodyBytes.length); + response.getHeaders().setContentType(MediaType.APPLICATION_JSON); response.writeWith(Mono.just(dataBuffer)).subscribe(); return response.setComplete(); } - private BkUserDTO getUserByTokenList(List bkTokenList) { + private BkUserDTO getUserByTokenList(List bkTokenList, String lang) { // 遍历所有传入token找出当前环境的 for (String bkToken : bkTokenList) { - BkUserDTO user = loginService.getUser(bkToken); + BkUserDTO user = loginService.getUser(bkToken, lang); if (user != null) { return user; } diff --git a/src/backend/job-gateway/src/main/java/com/tencent/bk/job/gateway/web/service/LoginService.java b/src/backend/job-gateway/src/main/java/com/tencent/bk/job/gateway/web/service/LoginService.java index 5931d66f90..650c6a9d21 100644 --- a/src/backend/job-gateway/src/main/java/com/tencent/bk/job/gateway/web/service/LoginService.java +++ b/src/backend/job-gateway/src/main/java/com/tencent/bk/job/gateway/web/service/LoginService.java @@ -41,9 +41,10 @@ public interface LoginService { * 根据token获取用户信息 * * @param bkToken 用户token + * @param lang 语言 * @return 用户信息 */ - BkUserDTO getUser(String bkToken); + BkUserDTO getUser(String bkToken, String lang); /** * 获取登录跳转url diff --git a/src/backend/job-gateway/src/main/java/com/tencent/bk/job/gateway/web/service/impl/LoginServiceImpl.java b/src/backend/job-gateway/src/main/java/com/tencent/bk/job/gateway/web/service/impl/LoginServiceImpl.java index 9b8bd8ac35..8181fffd49 100644 --- a/src/backend/job-gateway/src/main/java/com/tencent/bk/job/gateway/web/service/impl/LoginServiceImpl.java +++ b/src/backend/job-gateway/src/main/java/com/tencent/bk/job/gateway/web/service/impl/LoginServiceImpl.java @@ -30,15 +30,19 @@ import com.google.common.util.concurrent.UncheckedExecutionException; import com.tencent.bk.job.common.constant.ErrorCode; import com.tencent.bk.job.common.exception.InternalException; +import com.tencent.bk.job.common.i18n.locale.LocaleUtils; import com.tencent.bk.job.common.model.dto.BkUserDTO; import com.tencent.bk.job.common.paas.login.ILoginClient; import com.tencent.bk.job.gateway.config.BkConfig; import com.tencent.bk.job.gateway.web.service.LoginService; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; +import java.util.Objects; import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -49,14 +53,22 @@ public class LoginServiceImpl implements LoginService { private final BkConfig bkConfig; private final String tokenName; private final String loginUrl; - private final ILoginClient loginClient; - private LoadingCache> onlineUserCache = CacheBuilder.newBuilder() + private final ILoginClient enLoginClient; + private final ILoginClient cnLoginClient; + private LoadingCache> onlineUserCache = CacheBuilder.newBuilder() .maximumSize(200).expireAfterWrite(10, TimeUnit.SECONDS).build( - new CacheLoader>() { + new CacheLoader>() { @Override - public Optional load(String bkToken) throws Exception { + public Optional load(BkTokenWithLang bkTokenWithLang) throws Exception { try { - BkUserDTO userDto = loginClient.getUserInfoByToken(bkToken); + String lang = bkTokenWithLang.getLang(); + String bkToken = bkTokenWithLang.getBkToken(); + BkUserDTO userDto; + if (LocaleUtils.LANG_ZH_CN.equals(lang)) { + userDto = cnLoginClient.getUserInfoByToken(bkToken); + } else { + userDto = enLoginClient.getUserInfoByToken(bkToken); + } return Optional.ofNullable(userDto); } catch (Exception e) { return Optional.empty(); @@ -66,13 +78,16 @@ public Optional load(String bkToken) throws Exception { ); @Autowired - public LoginServiceImpl(BkConfig bkConfig, ILoginClient loginClient) { + public LoginServiceImpl(BkConfig bkConfig, + @Qualifier("enLoginClient") ILoginClient enLoginClient, + @Qualifier("cnLoginClient") ILoginClient cnLoginClient) { this.bkConfig = bkConfig; - this.loginClient = loginClient; + this.enLoginClient = enLoginClient; + this.cnLoginClient = cnLoginClient; this.loginUrl = getLoginUrlProp(); this.tokenName = bkConfig.isCustomPaasLoginEnabled() ? bkConfig.getCustomLoginToken() : "bk_token"; log.info("Init login service, customLoginEnabled:{}, loginClient:{}, loginUrl:{}, tokenName:{}", - bkConfig.isCustomPaasLoginEnabled(), loginClient.getClass(), loginUrl, tokenName); + bkConfig.isCustomPaasLoginEnabled(), enLoginClient.getClass(), loginUrl, tokenName); } private String getLoginUrlProp() { @@ -96,14 +111,43 @@ public void deleteUser(String bkToken) { onlineUserCache.invalidate(bkToken); } + @Getter + class BkTokenWithLang { + private String bkToken; + private String lang; + + BkTokenWithLang(String bkToken, String lang) { + this.bkToken = bkToken; + this.lang = lang; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof BkTokenWithLang)) return false; + BkTokenWithLang that = (BkTokenWithLang) o; + return Objects.equals(bkToken, that.bkToken) && + Objects.equals(lang, that.lang); + } + + @Override + public int hashCode() { + return Objects.hash(bkToken, lang); + } + } @Override - public BkUserDTO getUser(String bkToken) { + public BkUserDTO getUser(String bkToken, String lang) { if (StringUtils.isBlank(bkToken)) { return null; } + if (StringUtils.isBlank(lang)) { + log.warn("getUser: lang is null or blank, use default en"); + lang = LocaleUtils.LANG_EN; + } try { - Optional userDto = onlineUserCache.get(bkToken); + BkTokenWithLang bkTokenWithLang = new BkTokenWithLang(bkToken, lang); + Optional userDto = onlineUserCache.get(bkTokenWithLang); return userDto.orElse(null); } catch (ExecutionException | UncheckedExecutionException e) { log.warn("Error occur when get user from paas!"); diff --git a/src/backend/job-gateway/src/main/resources/application.yml b/src/backend/job-gateway/src/main/resources/application.yml index b5a15c10c4..cc3e4dd731 100644 --- a/src/backend/job-gateway/src/main/resources/application.yml +++ b/src/backend/job-gateway/src/main/resources/application.yml @@ -26,64 +26,64 @@ spring: predicates: - Path= /job-manage/web/** filters: + - AddWebLangHeader - Authorize - CsrfCheck - StripPrefix=1 - - AddWebLangHeader - id: job-crontab-web uri: lb://job-crontab predicates: - Path= /job-crontab/web/** filters: + - AddWebLangHeader - Authorize - CsrfCheck - StripPrefix=1 - - AddWebLangHeader - id: job-execute-web uri: lb://job-execute predicates: - Path= /job-execute/web/** filters: + - AddWebLangHeader - Authorize - CsrfCheck - StripPrefix=1 - - AddWebLangHeader - id: job-backup-web uri: lb://job-backup predicates: - Path= /job-backup/web/** filters: + - AddWebLangHeader - Authorize - CsrfCheck - StripPrefix=1 - - AddWebLangHeader - id: job-file-gateway-web uri: lb://job-file-gateway predicates: - Path= /job-file-gateway/web/** filters: + - AddWebLangHeader - Authorize - CsrfCheck - StripPrefix=1 - - AddWebLangHeader - id: job-ticket-web uri: lb://job-manage predicates: - Path= /job-ticket/web/** filters: + - AddWebLangHeader - Authorize - CsrfCheck - StripPrefix=1 - - AddWebLangHeader - id: job-analysis-web uri: lb://job-analysis predicates: - Path= /job-analysis/web/** filters: + - AddWebLangHeader - Authorize - CsrfCheck - StripPrefix=1 - - AddWebLangHeader - id: job-file-gateway-remote uri: lb://job-file-gateway