From 37b646f70822fc1dc15f13863cfc01e6856d7751 Mon Sep 17 00:00:00 2001 From: zengxiaobo Date: Sun, 28 Apr 2024 16:03:44 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E7=99=BB=E9=99=86?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web-support-lib/pom.xml | 5 ++ .../support/config/DefaultWebMvcConfig.java | 2 + .../web/support/context/AxContext.java | 89 +++++++++++++++++++ .../support/context/AxContextInterceptor.java | 86 ++++++++++++++++++ .../web/support/context/AxUser.java | 31 +++++++ .../src/test/resources/authentication.json | 41 +++++++++ 6 files changed, 254 insertions(+) create mode 100644 web-support-lib/src/main/java/cn/axzo/foundation/web/support/context/AxContext.java create mode 100644 web-support-lib/src/main/java/cn/axzo/foundation/web/support/context/AxContextInterceptor.java create mode 100644 web-support-lib/src/main/java/cn/axzo/foundation/web/support/context/AxUser.java create mode 100644 web-support-lib/src/test/resources/authentication.json diff --git a/web-support-lib/pom.xml b/web-support-lib/pom.xml index e6fd7b3..c996065 100644 --- a/web-support-lib/pom.xml +++ b/web-support-lib/pom.xml @@ -36,6 +36,11 @@ com.squareup.okhttp3 okhttp + + + com.alibaba + transmittable-thread-local + \ No newline at end of file diff --git a/web-support-lib/src/main/java/cn/axzo/foundation/web/support/config/DefaultWebMvcConfig.java b/web-support-lib/src/main/java/cn/axzo/foundation/web/support/config/DefaultWebMvcConfig.java index db43543..ae56966 100644 --- a/web-support-lib/src/main/java/cn/axzo/foundation/web/support/config/DefaultWebMvcConfig.java +++ b/web-support-lib/src/main/java/cn/axzo/foundation/web/support/config/DefaultWebMvcConfig.java @@ -2,6 +2,7 @@ package cn.axzo.foundation.web.support.config; import cn.axzo.foundation.util.FastjsonUtils; import cn.axzo.foundation.web.support.AppRuntime; +import cn.axzo.foundation.web.support.context.AxContextInterceptor; import cn.axzo.foundation.web.support.exception.AbstractExceptionHandler; import cn.axzo.foundation.web.support.interceptors.PrettyPrintInterceptor; import cn.axzo.foundation.web.support.interceptors.PrintVerboseInterceptor; @@ -187,6 +188,7 @@ public class DefaultWebMvcConfig extends DelegatingWebMvcConfiguration implement registry.addInterceptor(e); }); } + registry.addInterceptor(new AxContextInterceptor(appRuntime)); } /** diff --git a/web-support-lib/src/main/java/cn/axzo/foundation/web/support/context/AxContext.java b/web-support-lib/src/main/java/cn/axzo/foundation/web/support/context/AxContext.java new file mode 100644 index 0000000..21648d3 --- /dev/null +++ b/web-support-lib/src/main/java/cn/axzo/foundation/web/support/context/AxContext.java @@ -0,0 +1,89 @@ +package cn.axzo.foundation.web.support.context; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.ttl.TransmittableThreadLocal; +import com.google.common.base.Charsets; +import com.google.common.base.Strings; +import com.google.common.io.BaseEncoding; +import lombok.*; +import org.apache.commons.lang3.BooleanUtils; + +import javax.servlet.http.HttpServletRequest; +import java.util.Arrays; +import java.util.Optional; +import java.util.function.BiFunction; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AxContext { + private static final TransmittableThreadLocal CONTEXT = new TransmittableThreadLocal(); + private static final TransmittableThreadLocal REQUEST = new TransmittableThreadLocal(); + + private AxUser axUser; + private Long ouId; + + public static void set(AxContext contextInfo) { + CONTEXT.set(contextInfo); + } + + public static Optional get() { + return Optional.ofNullable(CONTEXT.get()); + } + + public static void clear() { + CONTEXT.remove(); + } + + public static void setRequest(HttpServletRequest request) { + REQUEST.set(request); + } + + public static Optional getRequest() { + return Optional.ofNullable(REQUEST.get()); + } + + + public static boolean init() { + if (!getRequest().isPresent()) { + return false; + } + HttpServletRequest request = getRequest().get(); + AxContext context = AxContext.builder().build(); + Boolean[] initialled = Arrays.stream(HeaderEnum.values()) + .map(e -> e.getFiller().apply(request, context)) + .toArray(Boolean[]::new); + //所有的header设置成功才算成功 + if (BooleanUtils.and(initialled)) { + set(context); + return true; + } + return false; + } + + @AllArgsConstructor + @Getter + public enum HeaderEnum { + USER("用户信息", (request, context) -> { + String userHeader = request.getHeader("userinfo"); + if (Strings.isNullOrEmpty(userHeader)) { + return false; + } + String userStr = new String(BaseEncoding.base64().decode(userHeader), Charsets.UTF_8); + context.setAxUser(JSONObject.parseObject(userStr, AxUser.class)); + return true; + }), + OU_ID("部门ID", (request, context) -> { + String ouIdHeader = request.getHeader("ouId"); + if (Strings.isNullOrEmpty(ouIdHeader)) { + return false; + } + context.setOuId(Long.parseLong(ouIdHeader)); + return true; + }); + + private final String desc; + private final BiFunction filler; + } +} diff --git a/web-support-lib/src/main/java/cn/axzo/foundation/web/support/context/AxContextInterceptor.java b/web-support-lib/src/main/java/cn/axzo/foundation/web/support/context/AxContextInterceptor.java new file mode 100644 index 0000000..c55dcd0 --- /dev/null +++ b/web-support-lib/src/main/java/cn/axzo/foundation/web/support/context/AxContextInterceptor.java @@ -0,0 +1,86 @@ +package cn.axzo.foundation.web.support.context; + +import cn.axzo.foundation.enums.AppEnvEnum; +import cn.axzo.foundation.web.support.AppRuntime; +import cn.axzo.foundation.web.support.rpc.HttpClient; +import cn.axzo.foundation.web.support.rpc.OkHttpClientImpl; +import cn.axzo.foundation.web.support.rpc.RequestParams; +import com.alibaba.fastjson.JSONObject; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.servlet.HandlerInterceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Map; + +/** + * 1. 从容器内调用, 端->apisix->this server可以从header中直接获取 + * 2. 非prd环境支持Authorization: Raw {personId:xxx} + * 3. 非prd环境支持通过token到puge换 + */ +@Slf4j +@RequiredArgsConstructor +public class AxContextInterceptor implements HandlerInterceptor { + private final AppRuntime appRuntime; + + private final static HttpClient HTTP_CLIENT = OkHttpClientImpl.builder().build(); + private final static Map ENV_HOSTS = ImmutableMap.of( + AppEnvEnum.local, "http://test-api.axzo.cn/pudge/webApi/oauth/apisix/authentication", + AppEnvEnum.dev, "http://dev-app.axzo.cn/pudge/webApi/oauth/apisix/authentication", + AppEnvEnum.test, "http://test-api.axzo.cn/pudge/webApi/oauth/apisix/authentication", + AppEnvEnum.pre, "http://pre-api.axzo.cn/pudge/webApi/oauth/apisix/authentication" + ); + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + AxContext.clear(); + AxContext.setRequest(request); + boolean initialled = AxContext.init(); + if (initialled) { + return true; + } + String authorization = request.getHeader("Authorization"); + if (!Strings.isNullOrEmpty(authorization) && appRuntime.getEnv() != AppEnvEnum.prd) { + return initByAuthorization(authorization); + } + + return true; + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, + Exception ex) { + AxContext.clear(); + } + + /** + * 支持传入明文的authentication 或者通过puge接口支持 + * 明文格式: authentication: Raw {ouid:xxx, axUser:{personId:xxx}} + * puge接口返回结果参考 test.resources.authentication.json + */ + private boolean initByAuthorization(String authorization) { + AxContext context = null; + if (authorization.startsWith("Raw")) { + context = JSONObject.parseObject(StringUtils.removeStart(authorization, "Raw "), AxContext.class); + } + if (authorization.startsWith("Bearer")) { + String result = HTTP_CLIENT.get(ENV_HOSTS.get(appRuntime.getEnv()), RequestParams.FormParams.builder() + .headers(ImmutableMap.of("Authorization", authorization)) + .build()); + JSONObject userinfo = JSONObject.parseObject(result).getJSONObject("userinfo"); + context = AxContext.builder() + .ouId(userinfo.getLong("ouId")) + .axUser(userinfo.toJavaObject(AxUser.class)) + .build(); + } + if (context != null) { + AxContext.set(context); + return true; + } + return false; + } +} diff --git a/web-support-lib/src/main/java/cn/axzo/foundation/web/support/context/AxUser.java b/web-support-lib/src/main/java/cn/axzo/foundation/web/support/context/AxUser.java new file mode 100644 index 0000000..655925e --- /dev/null +++ b/web-support-lib/src/main/java/cn/axzo/foundation/web/support/context/AxUser.java @@ -0,0 +1,31 @@ +package cn.axzo.foundation.web.support.context; + +import lombok.*; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AxUser { + + private Long personId; + + private VerifyStatusEnum verifyStatus; + + private String realName; + + private transient String phoneNumber; + + @Getter + @RequiredArgsConstructor + public enum VerifyStatusEnum { + //状态 0 - 未激活, 1 - 已激活,2 - 禁用,3- 认证失败 + NOT_VERIFIED(0, "未认证"), + VERIFYING(1, "认证中"), + VERIFY_SUCCESS(2, "认证成功"), + VERIFY_FAILED(3, "认证失败"), + ; + private final Integer code; + private final String message; + } +} diff --git a/web-support-lib/src/test/resources/authentication.json b/web-support-lib/src/test/resources/authentication.json new file mode 100644 index 0000000..49b8963 --- /dev/null +++ b/web-support-lib/src/test/resources/authentication.json @@ -0,0 +1,41 @@ + +{ + "appVersion": "0", + "ouType": 6, + "enabled": true, + "acntId": 9000456, + "idFaceUrl": "https://axzo-test.oss-cn-chengdu.aliyuncs.com/idcard/idfaceurl/21664884838922395543357.jpg", + "verifyStatus": "VERIFY_SUCCESS", + "identityType": 5, + "ouId": 5144, + "id": 9000456, + "workspaceId": 335, + "avatarUrl": "https://industrial-public.oss-cn-shanghai.aliyuncs.com/avator/default_avator.png", + "sex": "2", + "ipAddress": "10.0.3.225", + "acctId": 9000456, + "loginDevice": "NT_OMS_WEB", + "terminal": "NT_OMS_WEB", + "userId": 2003154, + "realName": "金海洋", + "faceUrl": "https://axzo-test.oss-cn-chengdu.aliyuncs.com/idcard/idfaceurl/21664884838922395543357.jpg", + "workspaceType": 6, + "phoneNumber": "18008065266", + "identityId": 2003154, + "verifiedStatus": 2, + "personId": 9000456, + "legacyGuess": { + "ouType": 6, + "workspaceType": 6, + "guessTerminal": "__GUESS_OK__", + "guessOU": "__GUESS_OK__", + "ouId": 5144, + "guessWorkspace": "__GUESS_OK__", + "guessSaasTenant": "__NO_NEED__", + "workspaceJoinType": 0, + "saasTenantId": 0, + "newTerminal": "NT_OMS_WEB", + "workspaceId": 335 + }, + "axzoId": 9000456 +} \ No newline at end of file