From e5feaea601a919f257c939f2dc4769be01651969 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9D=8E=E7=99=BD?= <15656564262@163.com>
Date: Sat, 21 Sep 2024 14:46:04 +0800
Subject: [PATCH] init
---
.gitignore | 30 +++++
LICENSE | 21 ++++
Makefile | 6 +
README.md | 51 +++++++++
.../1.0.0/qaq-common-1.0.0-sources.jar.md5 | 1 +
.../1.0.0/qaq-common-1.0.0-sources.jar.sha1 | 1 +
.../qaq-common/1.0.0/qaq-common-1.0.0.jar.md5 | 1 +
.../1.0.0/qaq-common-1.0.0.jar.sha1 | 1 +
.../qaq-common/1.0.0/qaq-common-1.0.0.pom | 101 +++++++++++++++++
.../qaq-common/1.0.0/qaq-common-1.0.0.pom.md5 | 1 +
.../1.0.0/qaq-common-1.0.0.pom.sha1 | 1 +
.../qaq-public/qaq-common/maven-metadata.xml | 12 ++
.../qaq-common/maven-metadata.xml.md5 | 1 +
.../qaq-common/maven-metadata.xml.sha1 | 1 +
pom.xml | 101 +++++++++++++++++
.../qaq/base/annotation/CheckPermission.java | 12 ++
.../java/com/qaq/base/aspect/LogAspect.java | 57 ++++++++++
.../base/aspect/PermissionCheckAspect.java | 46 ++++++++
.../java/com/qaq/base/component/HttpUtil.java | 82 ++++++++++++++
.../base/component/JWTGeneratorComponent.java | 105 ++++++++++++++++++
.../base/component/JWTVerifierComponent.java | 49 ++++++++
.../com/qaq/base/component/NotifyUtil.java | 100 +++++++++++++++++
.../qaq/base/config/JWTGeneratorConfig.java | 61 ++++++++++
.../qaq/base/config/JWTVerifierConfig.java | 29 +++++
.../java/com/qaq/base/config/LarkConfig.java | 38 +++++++
.../com/qaq/base/config/LogginConfig.java | 15 +++
.../com/qaq/base/config/NotifyConfig.java | 42 +++++++
.../qaq/base/config/RestTemplateConfig.java | 48 ++++++++
.../java/com/qaq/base/config/WebConfig.java | 16 +++
.../com/qaq/base/enums/GatewayHeaderEnum.java | 21 ++++
.../base/exception/UnAuthorizedException.java | 7 ++
.../com/qaq/base/filter/LoggingFilter.java | 43 +++++++
.../qaq/base/intercepter/AuthInterceptor.java | 67 +++++++++++
src/main/java/com/qaq/base/model/Auth.java | 22 ++++
src/main/java/com/qaq/base/model/Message.java | 20 ++++
.../java/com/qaq/base/model/login/Token.java | 23 ++++
.../qaq/base/model/uniauth/AuthResult.java | 21 ++++
.../base/model/uniauth/AuthResultResp.java | 6 +
.../com/qaq/base/response/ApiResponse.java | 29 +++++
.../java/com/qaq/base/response/PageData.java | 23 ++++
.../java/com/qaq/base/utils/DateUtils.java | 66 +++++++++++
.../com/qaq/base/utils/HttpContextUtils.java | 55 +++++++++
.../base/utils/MutableHttpServletRequest.java | 51 +++++++++
.../java/com/qaq/base/utils/ObjectUtils.java | 56 ++++++++++
src/main/java/com/qaq/base/utils/Util.java | 5 +
45 files changed, 1545 insertions(+)
create mode 100644 .gitignore
create mode 100644 LICENSE
create mode 100644 Makefile
create mode 100644 README.md
create mode 100644 maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0-sources.jar.md5
create mode 100644 maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0-sources.jar.sha1
create mode 100644 maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0.jar.md5
create mode 100644 maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0.jar.sha1
create mode 100644 maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0.pom
create mode 100644 maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0.pom.md5
create mode 100644 maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0.pom.sha1
create mode 100644 maven-repo/io/github/qaq-public/qaq-common/maven-metadata.xml
create mode 100644 maven-repo/io/github/qaq-public/qaq-common/maven-metadata.xml.md5
create mode 100644 maven-repo/io/github/qaq-public/qaq-common/maven-metadata.xml.sha1
create mode 100644 pom.xml
create mode 100644 src/main/java/com/qaq/base/annotation/CheckPermission.java
create mode 100644 src/main/java/com/qaq/base/aspect/LogAspect.java
create mode 100644 src/main/java/com/qaq/base/aspect/PermissionCheckAspect.java
create mode 100644 src/main/java/com/qaq/base/component/HttpUtil.java
create mode 100644 src/main/java/com/qaq/base/component/JWTGeneratorComponent.java
create mode 100644 src/main/java/com/qaq/base/component/JWTVerifierComponent.java
create mode 100644 src/main/java/com/qaq/base/component/NotifyUtil.java
create mode 100644 src/main/java/com/qaq/base/config/JWTGeneratorConfig.java
create mode 100644 src/main/java/com/qaq/base/config/JWTVerifierConfig.java
create mode 100644 src/main/java/com/qaq/base/config/LarkConfig.java
create mode 100644 src/main/java/com/qaq/base/config/LogginConfig.java
create mode 100644 src/main/java/com/qaq/base/config/NotifyConfig.java
create mode 100644 src/main/java/com/qaq/base/config/RestTemplateConfig.java
create mode 100644 src/main/java/com/qaq/base/config/WebConfig.java
create mode 100644 src/main/java/com/qaq/base/enums/GatewayHeaderEnum.java
create mode 100644 src/main/java/com/qaq/base/exception/UnAuthorizedException.java
create mode 100644 src/main/java/com/qaq/base/filter/LoggingFilter.java
create mode 100644 src/main/java/com/qaq/base/intercepter/AuthInterceptor.java
create mode 100644 src/main/java/com/qaq/base/model/Auth.java
create mode 100644 src/main/java/com/qaq/base/model/Message.java
create mode 100644 src/main/java/com/qaq/base/model/login/Token.java
create mode 100644 src/main/java/com/qaq/base/model/uniauth/AuthResult.java
create mode 100644 src/main/java/com/qaq/base/model/uniauth/AuthResultResp.java
create mode 100644 src/main/java/com/qaq/base/response/ApiResponse.java
create mode 100644 src/main/java/com/qaq/base/response/PageData.java
create mode 100644 src/main/java/com/qaq/base/utils/DateUtils.java
create mode 100644 src/main/java/com/qaq/base/utils/HttpContextUtils.java
create mode 100644 src/main/java/com/qaq/base/utils/MutableHttpServletRequest.java
create mode 100644 src/main/java/com/qaq/base/utils/ObjectUtils.java
create mode 100644 src/main/java/com/qaq/base/utils/Util.java
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5493eb8
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,30 @@
+### Java template
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+*.iml
+.idea
+target/
+.vscode/
+.DS_Store
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..9cffc96
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 qaq-public
+
+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.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..df9e322
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,6 @@
+TAG?=1.0.0
+NAME:=qaq-common
+
+deploy:
+ mvn clean package deploy
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e3731da
--- /dev/null
+++ b/README.md
@@ -0,0 +1,51 @@
+# qaq-base-java
+
+qaq 的 java 公用代码,旨在让 QAQ 开发者便捷的调用 QAQ 开放 API。
+[发布 Maven 包的正确姿势](https://zhuanlan.zhihu.com/p/141676033)
+
+## 目录
+
+
+- [安装](#安装)
+- [加入答疑群](#加入答疑群)
+- [License](#License)
+
+
+
+## 安装
+
+- 运行环境:JDK 21 及以上
+
+- 最新版本 maven 坐标
+
+```shell
+
+ io.github.qaq-public
+ qaq-common
+ 1.0.0
+
+```
+
+- 如无法获取 qaq 依赖,请在 pom.xml 的 里增加
+
+```shell
+
+
+
+ gitpub-qaq-repo
+ The QAQ Repository on Github
+ https://qaq-public.github.io/qaq-common/maven-repo/
+
+
+
+ ...
+
+
+```
+
+## 加入答疑群
+
+[单击加入答疑群](https://applink.feishu.cn/client/chat/chatter/add_by_link?link_token=828s731f-83f2-400a-b093-04667ca93d4c)
+
+## License
+使用 MIT
diff --git a/maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0-sources.jar.md5 b/maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0-sources.jar.md5
new file mode 100644
index 0000000..4a45220
--- /dev/null
+++ b/maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0-sources.jar.md5
@@ -0,0 +1 @@
+6515e3e8fa38c41cd7601b4fd6867055
\ No newline at end of file
diff --git a/maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0-sources.jar.sha1 b/maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0-sources.jar.sha1
new file mode 100644
index 0000000..c607e68
--- /dev/null
+++ b/maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0-sources.jar.sha1
@@ -0,0 +1 @@
+7937230f0c22b50bd2de503cba3e7de9f4c385b0
\ No newline at end of file
diff --git a/maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0.jar.md5 b/maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0.jar.md5
new file mode 100644
index 0000000..0dfe550
--- /dev/null
+++ b/maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0.jar.md5
@@ -0,0 +1 @@
+89c7cd66d2eb2e44401dd5be876fbe15
\ No newline at end of file
diff --git a/maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0.jar.sha1 b/maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0.jar.sha1
new file mode 100644
index 0000000..36efb8f
--- /dev/null
+++ b/maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0.jar.sha1
@@ -0,0 +1 @@
+738b34b056bb4e007d155122910d60d537c587ac
\ No newline at end of file
diff --git a/maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0.pom b/maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0.pom
new file mode 100644
index 0000000..e77a893
--- /dev/null
+++ b/maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0.pom
@@ -0,0 +1,101 @@
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.3.4
+
+
+ io.github.qaq-public
+ qaq-common
+ 1.0.0
+ jar
+ qaq-common
+
+
+ blacklee123
+ 15656564262@163.com
+ qaq-public
+ https://qaq-public.github.io
+
+
+
+
+
+ UTF-8
+ 21
+ 21
+ false
+ 21
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ com.auth0
+ java-jwt
+ 4.4.0
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+ 1.70
+
+
+ com.larksuite.oapi
+ oapi-sdk
+ 2.3.4
+
+
+ org.aspectj
+ aspectjweaver
+ 1.9.22.1
+
+
+ org.projectlombok
+ lombok
+ 1.18.34
+ provided
+ true
+
+
+ org.projectlombok
+ lombok
+ 1.18.30
+ provided
+
+
+
+
+
+ local-repo-release
+ GitHub Release
+ file://${project.basedir}/maven-repo
+
+
+
+
+
+
+ maven-source-plugin
+
+
+ attach-sources
+ package
+
+ jar-no-fork
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0.pom.md5 b/maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0.pom.md5
new file mode 100644
index 0000000..8aa9906
--- /dev/null
+++ b/maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0.pom.md5
@@ -0,0 +1 @@
+b4341fd663b1af41e057a47e901ff13c
\ No newline at end of file
diff --git a/maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0.pom.sha1 b/maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0.pom.sha1
new file mode 100644
index 0000000..735569f
--- /dev/null
+++ b/maven-repo/io/github/qaq-public/qaq-common/1.0.0/qaq-common-1.0.0.pom.sha1
@@ -0,0 +1 @@
+c7843350bb2c02180ba8e133873f60264933719b
\ No newline at end of file
diff --git a/maven-repo/io/github/qaq-public/qaq-common/maven-metadata.xml b/maven-repo/io/github/qaq-public/qaq-common/maven-metadata.xml
new file mode 100644
index 0000000..b3c06d3
--- /dev/null
+++ b/maven-repo/io/github/qaq-public/qaq-common/maven-metadata.xml
@@ -0,0 +1,12 @@
+
+
+ io.github.qaq-public
+ qaq-common
+
+ 1.0.0
+
+ 1.0.0
+
+ 20240921064523
+
+
diff --git a/maven-repo/io/github/qaq-public/qaq-common/maven-metadata.xml.md5 b/maven-repo/io/github/qaq-public/qaq-common/maven-metadata.xml.md5
new file mode 100644
index 0000000..7b1f7f4
--- /dev/null
+++ b/maven-repo/io/github/qaq-public/qaq-common/maven-metadata.xml.md5
@@ -0,0 +1 @@
+a58a8999e938cc4054cfbdbce752906b
\ No newline at end of file
diff --git a/maven-repo/io/github/qaq-public/qaq-common/maven-metadata.xml.sha1 b/maven-repo/io/github/qaq-public/qaq-common/maven-metadata.xml.sha1
new file mode 100644
index 0000000..98ed42f
--- /dev/null
+++ b/maven-repo/io/github/qaq-public/qaq-common/maven-metadata.xml.sha1
@@ -0,0 +1 @@
+7f8ac3db05b4b357367308831d6c4a75dc06df01
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..e77a893
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,101 @@
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.3.4
+
+
+ io.github.qaq-public
+ qaq-common
+ 1.0.0
+ jar
+ qaq-common
+
+
+ blacklee123
+ 15656564262@163.com
+ qaq-public
+ https://qaq-public.github.io
+
+
+
+
+
+ UTF-8
+ 21
+ 21
+ false
+ 21
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ com.auth0
+ java-jwt
+ 4.4.0
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+ 1.70
+
+
+ com.larksuite.oapi
+ oapi-sdk
+ 2.3.4
+
+
+ org.aspectj
+ aspectjweaver
+ 1.9.22.1
+
+
+ org.projectlombok
+ lombok
+ 1.18.34
+ provided
+ true
+
+
+ org.projectlombok
+ lombok
+ 1.18.30
+ provided
+
+
+
+
+
+ local-repo-release
+ GitHub Release
+ file://${project.basedir}/maven-repo
+
+
+
+
+
+
+ maven-source-plugin
+
+
+ attach-sources
+ package
+
+ jar-no-fork
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/qaq/base/annotation/CheckPermission.java b/src/main/java/com/qaq/base/annotation/CheckPermission.java
new file mode 100644
index 0000000..f1838e7
--- /dev/null
+++ b/src/main/java/com/qaq/base/annotation/CheckPermission.java
@@ -0,0 +1,12 @@
+package com.qaq.base.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface CheckPermission {
+ String value();
+}
diff --git a/src/main/java/com/qaq/base/aspect/LogAspect.java b/src/main/java/com/qaq/base/aspect/LogAspect.java
new file mode 100644
index 0000000..1a7c16a
--- /dev/null
+++ b/src/main/java/com/qaq/base/aspect/LogAspect.java
@@ -0,0 +1,57 @@
+package com.qaq.base.aspect;
+
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.springframework.core.annotation.Order;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Aspect
+@Order(1)
+public class LogAspect {
+
+ @Around("within(@org.springframework.web.bind.annotation.RestController *) || within(@org.springframework.stereotype.Controller *)")
+ public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
+ long startTime = System.currentTimeMillis();
+ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+
+ String queryString = null;
+ String requestMethod = null;
+ String requestUri = null;
+
+ if (attributes != null && attributes.getRequest() != null) {
+ var request = attributes.getRequest();
+ queryString = request.getQueryString();
+ requestMethod = request.getMethod();
+ requestUri = request.getRequestURI();
+ }
+
+ try {
+ Object result = joinPoint.proceed();
+ var responseStatus = attributes != null && attributes.getResponse() != null ? attributes.getResponse().getStatus() : 0;
+ if (queryString != null) {
+ log.info("{} {} {}?{} {}", responseStatus, requestMethod, requestUri, queryString,
+ System.currentTimeMillis() - startTime);
+ } else {
+ log.info("{} {} {} {}", responseStatus, requestMethod, requestUri,
+ System.currentTimeMillis() - startTime);
+ }
+ return result;
+ } catch (Throwable ex) {
+ int responseStatus = attributes != null && attributes.getResponse() != null ? attributes.getResponse().getStatus() : 500;
+ if (queryString != null) {
+ log.error("{} {} {}?{} {} Exception: {}", responseStatus, requestMethod, requestUri, queryString,
+ System.currentTimeMillis() - startTime, ex.getMessage(), ex);
+ } else if (attributes != null) {
+ log.error("{} {} {} {} Exception: {}", responseStatus, requestMethod, requestUri,
+ System.currentTimeMillis() - startTime, ex.getMessage(), ex);
+ } else {
+ log.error("Exception at {} with message: {}", joinPoint, ex.getMessage(), ex);
+ }
+ throw ex;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/qaq/base/aspect/PermissionCheckAspect.java b/src/main/java/com/qaq/base/aspect/PermissionCheckAspect.java
new file mode 100644
index 0000000..ef844dc
--- /dev/null
+++ b/src/main/java/com/qaq/base/aspect/PermissionCheckAspect.java
@@ -0,0 +1,46 @@
+package com.qaq.base.aspect;
+
+import com.qaq.base.annotation.CheckPermission;
+import com.qaq.base.exception.UnAuthorizedException;
+import com.qaq.base.model.Auth;
+import jakarta.servlet.http.HttpServletRequest;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import java.lang.reflect.Method;
+
+@Aspect
+public class PermissionCheckAspect {
+
+ @Pointcut("@annotation(com.qaq.base.annotation.CheckPermission)")
+ public void readCheckerPoint() {
+ }
+
+ @Before("readCheckerPoint()")
+ public void advice(JoinPoint joinPoint) {
+ HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
+ .getRequest();
+ Auth auth = (Auth) request.getAttribute("auth");
+ MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
+ Method method = methodSignature.getMethod();
+ var checkPermission = method.getAnnotation(CheckPermission.class);
+
+ String permissionNeed = checkPermission.value();
+ check(auth, permissionNeed);
+ }
+
+ private void check(Auth auth, String permissionNeed) throws UnAuthorizedException {
+ if (!auth.havePermission(permissionNeed)) {
+ String msg = String.format("[%s][%s] no permission to operate, need permission %s",
+ auth.getToken().getEmail(), auth.getToken().getName(),
+ permissionNeed);
+ throw new UnAuthorizedException(msg);
+ }
+ }
+
+}
diff --git a/src/main/java/com/qaq/base/component/HttpUtil.java b/src/main/java/com/qaq/base/component/HttpUtil.java
new file mode 100644
index 0000000..67aa3cc
--- /dev/null
+++ b/src/main/java/com/qaq/base/component/HttpUtil.java
@@ -0,0 +1,82 @@
+package com.qaq.base.component;
+
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+@Component
+public class HttpUtil {
+
+ final private RestTemplate restTemplate = new RestTemplate();
+
+ public T exchange(String url, HttpMethod method, B body, Class responseType) {
+
+ var headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+
+ HttpEntity requestEntity = new HttpEntity<>(body, headers);
+
+ ResponseEntity resultEntity = restTemplate.exchange(url, method, requestEntity, responseType);
+
+ return resultEntity.getBody();
+ }
+
+
+ public T exchange(String url, HttpMethod method, B body, Class responseType, String authorization) {
+
+ var headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.setBearerAuth(authorization);
+
+ HttpEntity requestEntity = new HttpEntity<>(body, headers);
+
+ ResponseEntity resultEntity = restTemplate.exchange(url, method, requestEntity, responseType);
+
+ return resultEntity.getBody();
+ }
+
+ public T exchange(String url, HttpMethod method, B body, ParameterizedTypeReference responseType) {
+ var headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+
+ HttpEntity requestEntity = new HttpEntity<>(body, headers);
+
+ ResponseEntity resultEntity = restTemplate.exchange(url, method, requestEntity, responseType);
+
+ return resultEntity.getBody();
+ }
+
+ public T exchange(String url, HttpMethod method, B body, HttpHeaders headers, ParameterizedTypeReference responseType) {
+ // 请求体
+ if (headers == null) {
+ headers = new HttpHeaders();
+ }
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ // 发送请求
+ HttpEntity entity = new HttpEntity<>(body, headers);
+ ResponseEntity resultEntity = restTemplate.exchange(url, method, entity, responseType);
+ return resultEntity.getBody();
+ }
+
+ public T exchange(String url, HttpMethod method, B body, ParameterizedTypeReference responseType, String authorization) {
+
+ var headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.setBearerAuth(authorization);
+
+ HttpEntity requestEntity = new HttpEntity<>(body, headers);
+
+ ResponseEntity resultEntity = restTemplate.exchange(url, method, requestEntity, responseType);
+
+ return resultEntity.getBody();
+ }
+
+}
diff --git a/src/main/java/com/qaq/base/component/JWTGeneratorComponent.java b/src/main/java/com/qaq/base/component/JWTGeneratorComponent.java
new file mode 100644
index 0000000..93ba48b
--- /dev/null
+++ b/src/main/java/com/qaq/base/component/JWTGeneratorComponent.java
@@ -0,0 +1,105 @@
+package com.qaq.base.component;
+
+import java.security.interfaces.RSAPrivateKey;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import org.springframework.stereotype.Component;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.exceptions.JWTCreationException;
+import com.lark.oapi.service.authen.v1.model.GetUserInfoRespBody;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 生成jwt token, 包含name、avatar以及email字段, exp字段由tokenExpTimeInMinute控制
+ */
+@Slf4j
+@Component
+public class JWTGeneratorComponent {
+
+ private Integer tokenExpTimeInDay;
+ private RSAPrivateKey privateKey;
+
+ public JWTGeneratorComponent(RSAPrivateKey privateKey, Integer tokenExpTimeInDay) {
+ this.privateKey = privateKey;
+ this.tokenExpTimeInDay = tokenExpTimeInDay;
+ }
+
+ public String generate(GetUserInfoRespBody user) {
+ String salt = UUID.randomUUID().toString().trim().replaceAll("-", "");
+ String token = "";
+ Map header = new HashMap<>();
+ header.put("alg", "RS256");
+ header.put("typ", "JWT");
+ try {
+ // 只用签名,提供privateKey即可
+ Algorithm algorithm = Algorithm.RSA256(null, this.privateKey);
+ Date exp = getExpDate();
+ user.getUserId();
+ token = JWT.create()
+ .withExpiresAt(exp)
+ .withClaim("name", user.getName())
+ .withClaim("avatar", user.getAvatarUrl().trim())
+ .withClaim("email", user.getEmail() == null ? "" : user.getEmail())
+ .withClaim("userid", user.getUserId() == null ? "" : user.getUserId())
+ .withClaim("openid", user.getOpenId().trim())
+ .withClaim("salt", salt)
+ .withHeader(header)
+ .sign(algorithm);
+ } catch (JWTCreationException e) {
+ e.printStackTrace();
+ }
+ if (token.isBlank()) {
+ log.error("the jwt token is null, user: {} ", user);
+ throw new RuntimeException("JWT Token错误");
+ }
+ return token;
+ }
+
+ public String generate(com.lark.oapi.service.contact.v3.model.User user) {
+ String salt = UUID.randomUUID().toString().trim().replaceAll("-", "");
+ String token = "";
+ Map header = new HashMap<>();
+ header.put("alg", "RS256");
+ header.put("typ", "JWT");
+ try {
+ // 只用签名,提供privateKey即可
+ Algorithm algorithm = Algorithm.RSA256(null, this.privateKey);
+ Date exp = getExpDate();
+ token = JWT.create()
+ .withExpiresAt(exp)
+ .withClaim("name", user.getName())
+ .withClaim("avatar", user.getAvatar().getAvatar72().trim())
+ .withClaim("email", user.getEmail())
+ .withClaim("userid", user.getUserId().trim())
+ .withClaim("openid", user.getOpenId().trim())
+ .withClaim("salt", salt)
+ .withHeader(header)
+ .sign(algorithm);
+ } catch (JWTCreationException e) {
+ e.printStackTrace();
+ }
+ if (token.isBlank()) {
+ log.error("the jwt token is null, user: {} ", user);
+ throw new RuntimeException("JWT Token错误");
+ }
+ return token;
+ }
+
+ private Date getExpDate() {
+ Date exp = new Date();
+ Calendar calendar = new GregorianCalendar();
+ calendar.setTime(exp);
+ calendar.add(Calendar.DATE, tokenExpTimeInDay);
+ exp = calendar.getTime();
+ return exp;
+ }
+
+}
diff --git a/src/main/java/com/qaq/base/component/JWTVerifierComponent.java b/src/main/java/com/qaq/base/component/JWTVerifierComponent.java
new file mode 100644
index 0000000..2843f25
--- /dev/null
+++ b/src/main/java/com/qaq/base/component/JWTVerifierComponent.java
@@ -0,0 +1,49 @@
+package com.qaq.base.component;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.interfaces.Claim;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import org.springframework.stereotype.Component;
+
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
+import java.util.Map;
+
+@Component
+public class JWTVerifierComponent {
+
+ private final JWTVerifier jwtVerifier;
+
+ public JWTVerifierComponent(@NotBlank @NotNull String publicKeyStr) throws NoSuchAlgorithmException, InvalidKeySpecException {
+
+ PublicKey publicKey = getPublicKeyFromString(publicKeyStr);
+ Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) publicKey, null);
+
+ this.jwtVerifier = JWT.require(algorithm).withClaimPresence("email").build();
+ }
+
+ private PublicKey getPublicKeyFromString(String publicKeyStr) throws NoSuchAlgorithmException, InvalidKeySpecException {
+ // Remove the header and footer and newlines
+ String publicKeyPEM = publicKeyStr
+ .replace("-----BEGIN PUBLIC KEY-----", "")
+ .replace("-----END PUBLIC KEY-----", "")
+ .replaceAll("\\s", "");
+
+ byte[] decodedKey = Base64.getDecoder().decode(publicKeyPEM);
+ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
+ return KeyFactory.getInstance("RSA").generatePublic(keySpec);
+ }
+
+ public Map validToken(String jwtToken) {
+ var jwt = jwtVerifier.verify(jwtToken);
+ return jwt.getClaims();
+ }
+}
diff --git a/src/main/java/com/qaq/base/component/NotifyUtil.java b/src/main/java/com/qaq/base/component/NotifyUtil.java
new file mode 100644
index 0000000..4cc042a
--- /dev/null
+++ b/src/main/java/com/qaq/base/component/NotifyUtil.java
@@ -0,0 +1,100 @@
+package com.qaq.base.component;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.HttpMethod;
+
+import com.lark.oapi.service.contact.v3.model.User;
+import com.qaq.base.model.Message;
+import com.qaq.base.response.ApiResponse;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class NotifyUtil {
+
+ private com.qaq.base.component.HttpUtil httpUtil;
+ private String messageUrl;
+ private String notifyPackageUrl;
+ private String userMapUrl;
+ private String userUrl;
+
+ public NotifyUtil(com.qaq.base.component.HttpUtil httpUtil, String messageUrl, String notifyPackageUrl, String userMapUrl,
+ String userUrl) {
+ this.httpUtil = httpUtil;
+ this.messageUrl = messageUrl;
+ this.notifyPackageUrl = notifyPackageUrl;
+ this.userMapUrl = userMapUrl;
+ this.userUrl = userUrl;
+ }
+
+ public ApiResponse> send(Message message) {
+ var responseType = new ParameterizedTypeReference>>() {
+ };
+ var res = httpUtil.exchange(messageUrl, HttpMethod.POST, message, responseType);
+ return res;
+ }
+
+ public ApiResponse> notifyLibai(String content) {
+
+ return this.send(Message.builder()
+ .content(content)
+ .msg_type("post")
+ .touser(List.of("lifajin@pandadagames.com"))
+ .build());
+ }
+
+ public ApiResponse