context info

This commit is contained in:
Gao Wei 2022-06-09 23:03:51 +08:00
parent 47f1bf593b
commit e7226c1b28
15 changed files with 392 additions and 343 deletions

View File

@ -0,0 +1,14 @@
package cn.axzo.framework.auth.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PreBuildContext {
String value() default "";
}

View File

@ -1,17 +0,0 @@
package cn.axzo.framework.auth.annotation;
import cn.axzo.framework.auth.domain.RequestInfo;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PreBuildUser {
String value() default "";
/**
* RequestInfo子类Class对象
*/
Class<? extends RequestInfo> requestInfo() default RequestInfo.class;
}

View File

@ -0,0 +1,14 @@
package cn.axzo.framework.auth.config;
import cn.axzo.framework.auth.service.ContextInfoBuilderAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ContextInfoConfiguration {
@Bean
public ContextInfoBuilderAspect ContextInfoBuilderAspect() {
return new ContextInfoBuilderAspect();
}
}

View File

@ -1,13 +0,0 @@
package cn.axzo.framework.auth.config;
import cn.axzo.framework.auth.service.BuilderUserInfoAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class UserInfoConfiguration {
@Bean
public BuilderUserInfoAspect builderUserInfoAspect(){
return new BuilderUserInfoAspect();
}
}

View File

@ -3,11 +3,14 @@ package cn.axzo.framework.auth.constants;
public class AopConstants {
private AopConstants(){}
public static final String HEADER_TENANT = "tenantId";
public static final String HEADER_SYSTEM_TYPE = "1";
public static final String HEADER_AUTH = "authorization";
public static final String HEADER_SYSTEM_TYPE = "systemType";
public static final String HEADER_SYSTEM_VERSION = "systemVersion";
public static final String HEADER_DEVICE_KIND = "deviceKind";
public static final String HEADER_DEVICE_NO = "deviceNo";
public static final String HEADER_APP_VERSION = "appVersion";
public static final String HEADER_SAAS_TENANT_ID = "SAAS_TENANT_ID";
public static final String HEADER_WORKSPACE_ID = "workspaceId";
public static final String VISIT_TO = "visitTo";
@ -15,6 +18,7 @@ public class AopConstants {
public static final String ENV_DEV="dev";
public static final String HEADER_AUTH = "authorization";

View File

@ -0,0 +1,57 @@
package cn.axzo.framework.auth.domain;
import javax.servlet.http.HttpServletRequest;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Data
@Slf4j
@AllArgsConstructor
@NoArgsConstructor
public class ContextInfo {
private String token;
private UserInfo userInfo;
private SystemAndDeviceInfo systemAndDeviceInfo;
private TerminalInfo terminalInfo;
/**
* 项目空间id
*/
private Long workspaceId;
/**
* 单位id
*/
private String ouId;
/**
* 租户id
*/
private String tenantId;
/**
* saas租户id
*/
private Long saasTenantId;
private String visitTo;
public void buildCustomUserInfo(UserInfoMap userInfoMap){
/**
* {@link cn.axzo.apollo.server.common.config.auth.UserContextFilter.PARAMETER_PROJECTID}
*/
}
public void buildCustomInfo(HttpServletRequest request) {
}
}

View File

@ -0,0 +1,27 @@
package cn.axzo.framework.auth.domain;
import com.alibaba.ttl.TransmittableThreadLocal;
/**
* 用户信息上下文 holder
* @author zhangtianyu
* @date 2022/5/11 11:21 AM
**/
public class ContextInfoHolder {
private static final TransmittableThreadLocal<ContextInfo> CONTEXT = new TransmittableThreadLocal<>();
private ContextInfoHolder(){}
public static void set(ContextInfo contextInfo) {
CONTEXT.set(contextInfo);
}
public static ContextInfo get() {
return CONTEXT.get();
}
public static void clear() {
CONTEXT.remove();
}
}

View File

@ -1,37 +0,0 @@
package cn.axzo.framework.auth.domain;
import lombok.Data;
import javax.servlet.http.HttpServletRequest;
/**
* 请求数据
* @author zhangtianyu
* @date 2022/4/25 10:10 AM
**/
@Data
public class RequestInfo {
/**
* 租户id
*/
private String tenantId;
/**
* saas租户id
*/
private Long saasTenantId;
private String systemType;
private String deviceKind;
private String deviceNo;
private String appVersion;
private String token;
private String visitTo;
/**
* 项目空间id
*/
private Long workspaceId;
/**
* 使用http request 设置自定义字段
* @param request http请求
*/
public void buildCustomRequestInfo(HttpServletRequest request) {}
}

View File

@ -0,0 +1,16 @@
package cn.axzo.framework.auth.domain;
import lombok.Data;
import javax.servlet.http.HttpServletRequest;
/**
**/
@Data
public class SystemAndDeviceInfo {
private String systemType;
private String deviceKind;
private String deviceNo;
private String appVersion;
}

View File

@ -0,0 +1,16 @@
package cn.axzo.framework.auth.domain;
public class TerminalInfo {
private String terminal;
CMS = NT_CMS, (1, cms)
boolean isCMS() {
if("1".equals(terminal)) {
}
}
}

View File

@ -7,12 +7,12 @@ import lombok.extern.slf4j.Slf4j;
import java.util.List;
@Data
@Slf4j
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {
/**
* 客户端Id
*/
@ -69,21 +69,9 @@ public class UserInfo {
* id
*/
private Long id;
private boolean accountNonLocked;
private String username;
/**
* 请求信息
*/
private RequestInfo requestInfo;
/**
* 使用用户信息map 设置自定义字段
* @param userInfoMap 用户信息
*/
public void buildCustomUserInfo(UserInfoMap userInfoMap){}
public <T extends RequestInfo> T getRequestInfo() {
return (T) requestInfo;
}
}

View File

@ -1,27 +0,0 @@
package cn.axzo.framework.auth.domain;
import com.alibaba.ttl.TransmittableThreadLocal;
/**
* 用户信息上下文 holder
* @author zhangtianyu
* @date 2022/5/11 11:21 AM
**/
public class UserInfoHolder {
private static final TransmittableThreadLocal<UserInfo> CONTEXT = new TransmittableThreadLocal<>();
private UserInfoHolder(){}
public static void set(UserInfo userInfo) {
CONTEXT.set(userInfo);
}
public static UserInfo get() {
return CONTEXT.get();
}
public static void clear() {
CONTEXT.remove();
}
}

View File

@ -1,173 +0,0 @@
package cn.axzo.framework.auth.service;
import cn.axzo.framework.auth.AuthException;
import cn.axzo.framework.auth.annotation.PreBuildUser;
import cn.axzo.framework.auth.constants.AopConstants;
import cn.axzo.framework.auth.domain.RequestInfo;
import cn.axzo.framework.auth.domain.UserInfo;
import cn.axzo.framework.auth.domain.UserInfoHolder;
import cn.axzo.framework.auth.enums.EnvEnum;
import cn.azxo.framework.common.logger.logback.PodNamespacePropertyDefiner;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.http.HttpException;
import cn.hutool.http.HttpRequest;
import cn.axzo.framework.auth.domain.UserInfoMap;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.json.JSONUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;
@Slf4j
@Aspect
@Component
@Order(1)
public class BuilderUserInfoAspect {
@Around(value = "@within(preBuildUser)")
public Object classHandler(ProceedingJoinPoint pjp, PreBuildUser preBuildUser) {
return handle(pjp, preBuildUser);
}
@Around(value = "@annotation(preBuildUser)")
public Object methodHandler(ProceedingJoinPoint pjp, PreBuildUser preBuildUser) {
return handle(pjp, preBuildUser);
}
@SneakyThrows
public Object handle(ProceedingJoinPoint pjp, PreBuildUser preBuildUser) {
HttpServletRequest httpRequest = null;
try {
//获取request
httpRequest = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
} catch (Exception exception) {
log.error("can not get request,there is a error occurrence ==>" + exception.getCause().getMessage());
}
AuthException.error(Objects.nonNull(httpRequest), "httpRequest cant be null, this is error");
fillInUserInfoDetail(httpRequest, pjp, preBuildUser.requestInfo());
try {
return pjp.proceed();
} finally {
UserInfoHolder.clear();
}
}
public void fillInUserInfoDetail(HttpServletRequest request, ProceedingJoinPoint pjp, Class<? extends RequestInfo> requestInfoClass) {
String userinfoJson;
// 本地运行 分两种情况 postMan/junit
// 判断本地环境
if (getSystemProperties() == null) {
// 如果本地环境 有两种如果是postman类型调用手动调用pudge
String token = request.getHeader("Authorization");
if (CharSequenceUtil.isEmpty(token)) {
// 硬逻辑 如果不携带token 可理解为junit测试 这里不覆盖 Userinfo的信息
return;
}
userinfoJson = getUserInfoFromToken(token);
} else {
// apisix 获取的请求
String userJsonInfo = request.getHeader(AopConstants.USER_INFO);
AuthException.error(CharSequenceUtil.isNotEmpty(userJsonInfo), "do not have userinfo error , please check apisix config is correct");
// 转编码
userinfoJson = Base64.decodeStr(userJsonInfo);
}
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
Method method = methodSignature.getMethod();
Class<?>[] parameterTypes = method.getParameterTypes();
Object[] pjpArgs = pjp.getArgs();
UserInfo userInfo = new UserInfo();
for (int i = 0; i < parameterTypes.length; i++) {
if (parameterTypes[i].isAssignableFrom(UserInfo.class)) {
userInfo = (UserInfo) pjpArgs[i];
break;
}
}
buildRequestInfo(userInfo, request, requestInfoClass);
buildUserInfo(userInfo, userinfoJson);
UserInfoHolder.set(userInfo);
}
public String getUserInfoFromToken(String token) {
// postman调用
String customerEnv = getCustomerEnv();
customerEnv = CharSequenceUtil.isEmpty(customerEnv) ? AopConstants.ENV_DEV : customerEnv;
String url = EnvEnum.getValueByKey(customerEnv);
AuthException.error(CharSequenceUtil.isNotEmpty(url), "There is no correct path to request");
try {
String response = HttpRequest.get(url).header("Authorization", token).execute().body();
// 使用token获取用户信息
AuthException.error(CharSequenceUtil.isNotEmpty(response), "not find user by token from pudge");
return response;
} catch (HttpException e) {
throw new AuthException("call pudge get/user error.");
}
}
/**
* 从env/properties 获取my_pod_namespace 的value
*/
public String getSystemProperties() {
PodNamespacePropertyDefiner podNamespacePropertyDefiner = new PodNamespacePropertyDefiner();
return podNamespacePropertyDefiner.getPropertyValue();
}
/**
* 从本地拿 CUSTOM_ENV的值
*/
public String getCustomerEnv() {
PodNamespacePropertyDefiner podNamespacePropertyDefiner = new PodNamespacePropertyDefiner();
return (String) podNamespacePropertyDefiner.getSystemProperties().get("CUSTOM_ENV");
}
@SneakyThrows
private void buildRequestInfo(UserInfo userInfo, HttpServletRequest request, Class<? extends RequestInfo> requestInfoClass) {
RequestInfo requestInfo = userInfo.getRequestInfo();
if (Objects.isNull(requestInfo)) {
requestInfo = requestInfoClass.newInstance();
userInfo.setRequestInfo(requestInfo);
}
// 请求头获取数据
requestInfo.setAppVersion(request.getHeader(AopConstants.HEADER_APP_VERSION));
requestInfo.setDeviceKind(request.getHeader(AopConstants.HEADER_DEVICE_KIND));
requestInfo.setDeviceNo(request.getHeader(AopConstants.HEADER_DEVICE_NO));
requestInfo.setSystemType(request.getHeader(AopConstants.HEADER_SYSTEM_TYPE));
requestInfo.setTenantId(request.getHeader(AopConstants.HEADER_TENANT));
requestInfo.setVisitTo(request.getHeader(AopConstants.VISIT_TO));
requestInfo.setToken(request.getHeader(AopConstants.HEADER_AUTH));
String saasTenantId = request.getHeader(AopConstants.HEADER_SAAS_TENANT_ID);
requestInfo.setSaasTenantId(CharSequenceUtil.isEmpty(saasTenantId) ? null : Long.valueOf(saasTenantId));
String workspaceId = request.getHeader(AopConstants.HEADER_WORKSPACE_ID);
requestInfo.setWorkspaceId(CharSequenceUtil.isEmpty(workspaceId) ? null : Long.valueOf(workspaceId));
requestInfo.buildCustomRequestInfo(request);
}
public void buildUserInfo(UserInfo userInfo, String userJsonInfo) {
TypeReference<Map<String, Object>> mapTypeReference = new TypeReference<Map<String, Object>>(){};
Map<String, Object> map = JSONUtil.toBean(userJsonInfo, mapTypeReference, false);
// 对bean进行封装属性
BeanUtil.fillBeanWithMap(map, userInfo, false);
userInfo.buildCustomUserInfo(new UserInfoMap(map));
}
}

View File

@ -0,0 +1,180 @@
package cn.axzo.framework.auth.service;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import cn.axzo.framework.auth.AuthException;
import cn.axzo.framework.auth.annotation.PreBuildContext;
import cn.axzo.framework.auth.constants.AopConstants;
import cn.axzo.framework.auth.domain.ContextInfo;
import cn.axzo.framework.auth.domain.ContextInfoHolder;
import cn.axzo.framework.auth.domain.SystemAndDeviceInfo;
import cn.axzo.framework.auth.domain.UserInfo;
import cn.axzo.framework.auth.domain.UserInfoMap;
import cn.axzo.framework.auth.enums.EnvEnum;
import cn.azxo.framework.common.logger.logback.PodNamespacePropertyDefiner;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.http.HttpException;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Aspect
@Component
@Order(1)
public class ContextInfoBuilderAspect {
@Around(value = "@within(preBuildContext)")
public Object classHandler(ProceedingJoinPoint pjp, PreBuildContext preBuildContext) {
return handle(pjp, preBuildContext);
}
@Around(value = "@annotation(preBuildContext)")
public Object methodHandler(ProceedingJoinPoint pjp, PreBuildContext preBuildContext) {
return handle(pjp, preBuildContext);
}
@SneakyThrows
public Object handle(ProceedingJoinPoint pjp, PreBuildContext preBuildContext) {
HttpServletRequest httpRequest = null;
try {
// 获取request
httpRequest = ((ServletRequestAttributes) Objects
.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
} catch (Exception exception) {
log.error("can not get request,there is a error occurrence ==>" + exception.getCause().getMessage());
}
AuthException.error(Objects.nonNull(httpRequest), "httpRequest cant be null, this is error");
fillInContextInfoDetail(httpRequest, pjp);
try {
return pjp.proceed();
} finally {
ContextInfoHolder.clear();
}
}
public void fillInContextInfoDetail(HttpServletRequest request, ProceedingJoinPoint pjp) {
String userinfoJson;
// 本地运行 分两种情况 postMan/junit
// 判断本地环境
if (getSystemProperties() == null) {
// 如果本地环境 有两种如果是postman类型调用手动调用pudge
String token = request.getHeader("Authorization");
if (CharSequenceUtil.isEmpty(token)) {
// 硬逻辑 如果不携带token 可理解为junit测试 这里不覆盖 Userinfo的信息
return;
}
userinfoJson = getUserInfoFromToken(token);
} else {
// apisix 获取的请求
String userJsonInfo = request.getHeader(AopConstants.USER_INFO);
AuthException.error(CharSequenceUtil.isNotEmpty(userJsonInfo),
"do not have userinfo error , please check apisix config is correct");
// 转编码
userinfoJson = Base64.decodeStr(userJsonInfo);
}
ContextInfo contextInfo = new ContextInfo();
buildUserInfo(contextInfo, userinfoJson);
fillContextInfoWithRequest(contextInfo, request);
contextInfo.buildCustomInfo(request);
ContextInfoHolder.set(contextInfo);
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
Method method = methodSignature.getMethod();
Class<?>[] parameterTypes = method.getParameterTypes();
Object[] pjpArgs = pjp.getArgs();
for (int i = 0; i < parameterTypes.length; i++) {
if (parameterTypes[i].isAssignableFrom(ContextInfo.class)) {
contextInfo = (ContextInfo) pjpArgs[i];
break;
}
}
}
public String getUserInfoFromToken(String token) {
// postman调用
String customerEnv = getCustomerEnv();
customerEnv = CharSequenceUtil.isEmpty(customerEnv) ? AopConstants.ENV_DEV : customerEnv;
String url = EnvEnum.getValueByKey(customerEnv);
AuthException.error(CharSequenceUtil.isNotEmpty(url), "There is no correct path to request");
try {
String response = HttpRequest.get(url).header("Authorization", token).execute().body();
// 使用token获取用户信息
AuthException.error(CharSequenceUtil.isNotEmpty(response), "not find user by token from pudge");
return response;
} catch (HttpException e) {
throw new AuthException("call pudge get/user error.");
}
}
/**
* 从env/properties 获取my_pod_namespace 的value
*/
public String getSystemProperties() {
PodNamespacePropertyDefiner podNamespacePropertyDefiner = new PodNamespacePropertyDefiner();
return podNamespacePropertyDefiner.getPropertyValue();
}
/**
* 从本地拿 CUSTOM_ENV的值
*/
public String getCustomerEnv() {
PodNamespacePropertyDefiner podNamespacePropertyDefiner = new PodNamespacePropertyDefiner();
return (String) podNamespacePropertyDefiner.getSystemProperties().get("CUSTOM_ENV");
}
@SneakyThrows
private void fillContextInfoWithRequest(ContextInfo contextInfo, HttpServletRequest request) {
SystemAndDeviceInfo sdInfo = new SystemAndDeviceInfo();
// 请求头获取数据
sdInfo.setAppVersion(request.getHeader(AopConstants.HEADER_APP_VERSION));
sdInfo.setDeviceKind(request.getHeader(AopConstants.HEADER_DEVICE_KIND));
sdInfo.setDeviceNo(request.getHeader(AopConstants.HEADER_DEVICE_NO));
sdInfo.setSystemType(request.getHeader(AopConstants.HEADER_SYSTEM_TYPE));
contextInfo.setSystemAndDeviceInfo(sdInfo);
contextInfo.setTenantId(request.getHeader(AopConstants.HEADER_TENANT));
contextInfo.setVisitTo(request.getHeader(AopConstants.VISIT_TO));
contextInfo.setToken(request.getHeader(AopConstants.HEADER_AUTH));
String saasTenantId = request.getHeader(AopConstants.HEADER_SAAS_TENANT_ID);
contextInfo.setSaasTenantId(CharSequenceUtil.isEmpty(saasTenantId) ? null : Long.valueOf(saasTenantId));
String workspaceId = request.getHeader(AopConstants.HEADER_WORKSPACE_ID);
contextInfo.setWorkspaceId(CharSequenceUtil.isEmpty(workspaceId) ? null : Long.valueOf(workspaceId));
}
public void buildUserInfo(ContextInfo contextInfo, String userJsonInfo) {
UserInfo userInfo = new UserInfo();
TypeReference<Map<String, Object>> mapTypeReference = new TypeReference<Map<String, Object>>() {
};
Map<String, Object> map = JSONUtil.toBean(userJsonInfo, mapTypeReference, false);
// 对bean进行封装属性
BeanUtil.fillBeanWithMap(map, userInfo, false);
contextInfo.setUserInfo(userInfo);
contextInfo.buildCustomUserInfo(new UserInfoMap(map));
}
}

View File

@ -1 +1 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.axzo.framework.auth.config.UserInfoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.axzo.framework.auth.config.ContextInfoConfiguration