后端基础框架
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
sys:
|
||||
# Your App secret key will be used for securely signing the session cookie
|
||||
# Make sure you are changing this key for your deployment with a strong key.
|
||||
# You can generate a strong key using `openssl rand -base64 42`.
|
||||
# Alternatively you can set it with `SECRET_KEY` environment variable.
|
||||
secret-key: WrLAa6u2LxhO+rQYQM7id01/FJPZ2GxH3pY/A2IFO6mOA0uaKzUIYWOC
|
||||
# Console API base URL
|
||||
console-api-url: http://localhost:8181
|
||||
console-web-url: http://localhost:5173
|
||||
# Model Configuration
|
||||
multimodal-send-image-format: base64
|
||||
# Files URL
|
||||
files-url: http://127.0.0.1:5123
|
||||
# Storage configuration
|
||||
# use for store upload files, private keys...
|
||||
# storage type: local, aliyun-oss, tencent-cos
|
||||
storage-type: local
|
||||
storage-local-path: D:/dev/ai-writing/storage/
|
||||
# Aliyun oss Storage configuration
|
||||
aliyun-oss-bucket-name: your-bucket-name
|
||||
aliyun-oss-access-key: your-access-key
|
||||
aliyun-oss-secret-key: your-secret-key
|
||||
aliyun-oss-endpoint: your-endpoint
|
||||
aliyun-oss-auth-version: v1
|
||||
aliyun-oss-region: your-region
|
||||
|
||||
# Invite Configuration
|
||||
invite-expiry-hours: 24
|
||||
|
||||
server:
|
||||
# 应用服务 WEB 访问端口
|
||||
port: 8282
|
||||
|
||||
spring:
|
||||
### 处理连接池冲突 #####
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
# 数据源
|
||||
datasource:
|
||||
# url: jdbc:kingbase8://172.26.138.234:54321/ai-writing?currentSchema=public
|
||||
# username: system
|
||||
# password: 123456
|
||||
# driver-class-name: com.kingbase8.Driver
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://223.83.129.121:33060/oai-writting?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
username: oai-write
|
||||
password: rpriBbbPRzBNFDYS
|
||||
# url: jdbc:mysql://127.0.0.1:3306/ai-writting?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
# username: root
|
||||
# password: zhangjie
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
# 数据库 连接池
|
||||
hikari:
|
||||
auto-commit: true
|
||||
connection-test-query: SELECT 1
|
||||
validation-timeout: 1000
|
||||
connection-timeout: 60000
|
||||
idle-timeout: 600000
|
||||
max-lifetime: 1200000
|
||||
maximum-pool-size: 15
|
||||
minimum-idle: 10
|
||||
pool-name: HikariCP
|
||||
data:
|
||||
redis:
|
||||
port: 6379
|
||||
host: localhost
|
||||
password:
|
||||
database: 0
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数(使用负值表示没有限制) 默认 8
|
||||
max-active: 8
|
||||
# 连接池中的最大空闲连接 默认 8
|
||||
max-idle: 8
|
||||
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
|
||||
max-wait: -1
|
||||
# 连接池中的最小空闲连接 默认 0
|
||||
min-idle: 0
|
||||
mail:
|
||||
# default(默认smtp邮箱服务) , aliyun-mail-push (阿里云邮件推送)
|
||||
type: aliyun-mail-push
|
||||
ak: LTAI5t9xS8GFTSxwWJmi8BMk
|
||||
sk: ntjZii2NFteyh4JshILyDf9xZwc79L
|
||||
host: smtpdm.aliyun.com
|
||||
port: 465
|
||||
username: robot@mail.oailab.com
|
||||
password: OaiMail13579
|
||||
protocol: smtp
|
||||
properties:
|
||||
mail:
|
||||
smtp:
|
||||
auth: true
|
||||
ssl:
|
||||
enable: true
|
||||
|
||||
# Login
|
||||
jwt:
|
||||
# Token 有效天数
|
||||
token-validity-days: 15
|
||||
|
||||
# Upload configuration
|
||||
upload:
|
||||
upload-file-size-limit: 15
|
||||
upload-file-batch-limit: 5
|
||||
upload-image-file-size-limit: 10
|
||||
@@ -0,0 +1,38 @@
|
||||
spring:
|
||||
application:
|
||||
name: ai-base
|
||||
profiles:
|
||||
active: local
|
||||
servlet:
|
||||
multipart:
|
||||
enabled: true
|
||||
max-file-size: 100MB
|
||||
max-request-size: 100MB
|
||||
|
||||
server:
|
||||
tomcat:
|
||||
connection-timeout: 300000
|
||||
servlet:
|
||||
session:
|
||||
timeout: 300000
|
||||
|
||||
# mybatis-plus 配置
|
||||
mybatis-plus:
|
||||
global-config:
|
||||
db-config:
|
||||
# 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
|
||||
logic-delete-field: deleted
|
||||
# 逻辑已删除值(默认为 1)
|
||||
logic-delete-value: 1
|
||||
# 逻辑未删除值(默认为 0)
|
||||
logic-not-delete-value: 0
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
auto-mapping-behavior: full
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
use-generated-keys: true
|
||||
mapper-locations: classpath*:mapper/**/*Mapper.xml
|
||||
type-aliases-package: com.jxyunge.oai.mybatis.entity
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!--日志配置-->
|
||||
<configuration>
|
||||
<property name="log.base" value="logs/"/>
|
||||
|
||||
<!-- 控制台输出 -->
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 按照每天生成错误日志文件 -->
|
||||
<appender name="ERRORFILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<Prudent>true</Prudent>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!--日志文件输出的文件名 -->
|
||||
<FileNamePattern>${log.base}LOG_ERROR-%d{yyyy-MM-dd}.log</FileNamePattern>
|
||||
<!--日志文件保留天数 -->
|
||||
<MaxHistory>5</MaxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印错误日志 -->
|
||||
<level>ERROR</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<!-- 按照每天生成警告日志文件 -->
|
||||
<appender name="WARNFILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<Prudent>true</Prudent>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!--日志文件输出的文件名 -->
|
||||
<FileNamePattern>${log.base}LOG_WARN-%d{yyyy-MM-dd}.log</FileNamePattern>
|
||||
<!--日志文件保留天数 -->
|
||||
<MaxHistory>5</MaxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印警告日志 -->
|
||||
<level>WARN</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<!-- 按照每天生成INFO日志文件 -->
|
||||
<appender name="INFOFILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<Prudent>true</Prudent>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!--日志文件输出的文件名 -->
|
||||
<FileNamePattern>${log.base}LOG_INFO-%d{yyyy-MM-dd}.log</FileNamePattern>
|
||||
<!--日志文件保留天数 -->
|
||||
<MaxHistory>5</MaxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印INFO日志 -->
|
||||
<level>INFO</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<!-- 按照每天生成DEBUG日志文件 -->
|
||||
<appender name="DEBUGFILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<Prudent>true</Prudent>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!--日志文件输出的文件名 -->
|
||||
<FileNamePattern>${log.base}LOG_DEBUG-%d{yyyy-MM-dd}.log</FileNamePattern>
|
||||
<!--日志文件保留天数 -->
|
||||
<MaxHistory>5</MaxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印DEBUG日志 -->
|
||||
<level>DEBUG</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<!-- 日志输出级别 -->
|
||||
<root level="info">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
<appender-ref ref="ERRORFILE"/>
|
||||
<appender-ref ref="WARNFILE"/>
|
||||
<appender-ref ref="INFOFILE"/>
|
||||
<appender-ref ref="DEBUGFILE"/>
|
||||
</root>
|
||||
</configuration>
|
||||
@@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>oailab.com</groupId>
|
||||
<artifactId>ai-base</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>api-start</artifactId>
|
||||
<version>${revision}</version>
|
||||
<name>api-start</name>
|
||||
<description>api-start</description>
|
||||
|
||||
<url/>
|
||||
<licenses>
|
||||
<license/>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer/>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection/>
|
||||
<developerConnection/>
|
||||
<tag/>
|
||||
<url/>
|
||||
</scm>
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>oailab.com</groupId>
|
||||
<artifactId>api-service</artifactId>
|
||||
<version>${revision}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>21</source>
|
||||
<target>21</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,26 @@
|
||||
package oailab.com.apistart;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
@SpringBootApplication(scanBasePackages = "oailab.com", exclude = {SecurityAutoConfiguration.class})
|
||||
@EnableScheduling
|
||||
@EnableCaching
|
||||
@EnableAsync
|
||||
@EnableTransactionManagement
|
||||
@Slf4j
|
||||
@PropertySource(value = {"file:${user.dir}/config/application.yml"})
|
||||
public class ApiStartApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ApiStartApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package oailab.com.apistart.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* Cross-origin configuration.
|
||||
*
|
||||
* @author Benny Deng
|
||||
* @see <a href="http://www.bianmaren.com">Benny Deng</a>
|
||||
* @since 6/17/24 PM11:19
|
||||
* @version 1.0
|
||||
**/
|
||||
@Configuration
|
||||
public class CorsConfig implements WebMvcConfigurer{
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowCredentials(true)
|
||||
.allowedOriginPatterns("*")
|
||||
.allowedHeaders("*")
|
||||
.allowedMethods("*");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package oailab.com.apistart.config;
|
||||
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import oailab.com.apistart.interceptor.SysTokenInterceptor;
|
||||
import oailab.com.apistart.interceptor.WebTokenInterceptor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* Mvc configuration.
|
||||
*
|
||||
* @author Benny Deng
|
||||
* @version 1.0
|
||||
* @see <a href="http://www.bianmaren.com">Benny Deng</a>
|
||||
* @since 6/17/24 PM11:19
|
||||
**/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class WebMvcConf implements WebMvcConfigurer {
|
||||
|
||||
@Resource
|
||||
private SysTokenInterceptor sysTokenInterceptor;
|
||||
@Resource
|
||||
private WebTokenInterceptor webTokenInterceptor;
|
||||
|
||||
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
// 将非 /api/** 的请求转发到 index.html
|
||||
registry.addViewController("/{path:[^\\.]*}")
|
||||
.setViewName("forward:/index.html");
|
||||
registry.addViewController("/{path1:[^\\.]*}/{path2:[^\\.]*}")
|
||||
.setViewName("forward:/index.html");
|
||||
registry.addViewController("/{path1:[^\\.]*}/{path2:[^\\.]*}/{path3:[^\\.]*}")
|
||||
.setViewName("forward:/index.html");
|
||||
registry.addViewController("/{path1:[^\\.]*}/{path2:[^\\.]*}/{path3:[^\\.]*}/{path4:[^\\.]*}")
|
||||
.setViewName("forward:/index.html");
|
||||
registry.addViewController("/{path1:[^\\.]*}/{path2:[^\\.]*}/{path3:[^\\.]*}/{path4:[^\\.]*}/{path5:[^\\.]*}")
|
||||
.setViewName("forward:/index.html");
|
||||
// 根据需要支持更深的路径
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
// 确保静态资源可以正常访问
|
||||
registry.addResourceHandler("/**")
|
||||
.addResourceLocations("classpath:/static/");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(sysTokenInterceptor).addPathPatterns("/api/sys/**");
|
||||
registry.addInterceptor(webTokenInterceptor).addPathPatterns("/api/web/**");
|
||||
registry.addInterceptor(sysTokenInterceptor).addPathPatterns("/api/payment/**");
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package oailab.com.apistart.controller;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import oailab.com.apiservice.constants.LoginConstants;
|
||||
import oailab.com.apiservice.enums.sys.ErrorCodeEnum;
|
||||
import oailab.com.apiservice.exception.BizException;
|
||||
import oailab.com.apiservice.mybatis.entity.sys.TenantAccount;
|
||||
import oailab.com.apiservice.mybatis.entity.sys.TenantWorkspace;
|
||||
import oailab.com.apiservice.mybatis.entity.workspace.EndUsers;
|
||||
import oailab.com.apiservice.mybatis.service.sys.TenantAccountService;
|
||||
import oailab.com.apiservice.mybatis.service.sys.TenantWorkspaceService;
|
||||
import oailab.com.apiservice.mybatis.service.workspace.EndUsersService;
|
||||
|
||||
/**
|
||||
* Base controller..
|
||||
*
|
||||
* @author Benny Deng
|
||||
* @version 1.0
|
||||
* @see <a href="http://www.bianmaren.com">Benny Deng</a>
|
||||
* @since 6/17/24 PM11:19
|
||||
**/
|
||||
public class BaseController {
|
||||
|
||||
@Resource
|
||||
private TenantAccountService tenantAccountService;
|
||||
@Resource
|
||||
private TenantWorkspaceService tenantWorkspaceService;
|
||||
@Resource
|
||||
private EndUsersService endUsersService;
|
||||
|
||||
/**
|
||||
* 获取当前登录的账号
|
||||
*
|
||||
* @param request 请求
|
||||
* @return 租户账号
|
||||
*/
|
||||
protected String getCurrentTenantAccountFromReq(HttpServletRequest request) {
|
||||
return getAttrFromReq(request, LoginConstants.SYS_LOGIN_USER_ACCOUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前工作间ID
|
||||
*
|
||||
* @param request 请求
|
||||
*/
|
||||
protected String getCurrentWorkspaceIdFromReq(HttpServletRequest request) {
|
||||
return getAttrFromReqHeaders(request, LoginConstants.HEADER_WORKSPACE_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前工作间
|
||||
*
|
||||
* @param request 请求
|
||||
*/
|
||||
protected TenantWorkspace getCurrentWorkspaceFromReq(HttpServletRequest request) {
|
||||
TenantWorkspace workspace = tenantWorkspaceService.getById(getCurrentWorkspaceIdFromReq(request));
|
||||
if (null == workspace) {
|
||||
throw new BizException(ErrorCodeEnum.WorkspaceNotFound);
|
||||
}
|
||||
return workspace;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录的租户
|
||||
*
|
||||
* @param request 请求
|
||||
* @return 租户
|
||||
*/
|
||||
protected TenantAccount getCurrentTenantFromReq(HttpServletRequest request) {
|
||||
return tenantAccountService.getByAccount(getCurrentTenantAccountFromReq(request));
|
||||
}
|
||||
|
||||
protected String getEndUserIdFromFromReq(HttpServletRequest request) {
|
||||
return getAttrFromReq(request, LoginConstants.WEB_LOGIN_USER_ACCOUNT);
|
||||
}
|
||||
|
||||
protected EndUsers getEndUserFromFromReq(HttpServletRequest request) {
|
||||
return endUsersService.getById(getEndUserIdFromFromReq(request));
|
||||
}
|
||||
|
||||
protected String getAppIdFromReq(HttpServletRequest request) {
|
||||
return getAttrFromReq(request, LoginConstants.API_APP_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求头中获取属性数据
|
||||
*
|
||||
* @param request 请求
|
||||
* @param key 属性key
|
||||
* @return 属性值
|
||||
*/
|
||||
protected String getAttrFromReq(HttpServletRequest request, String key) {
|
||||
Object userId = request.getAttribute(key);
|
||||
if (null != userId) {
|
||||
return userId.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求头中获取信息
|
||||
*
|
||||
* @param request 请求
|
||||
* @param key 属性key
|
||||
*/
|
||||
protected String getAttrFromReqHeaders(HttpServletRequest request, String key) {
|
||||
Object userId = request.getHeader(key);
|
||||
if (null != userId) {
|
||||
return userId.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
protected String getApiTokenFromReq(HttpServletRequest request) {
|
||||
// 通常从 header 中读取:Authorization: Bearer token
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
if (StrUtil.isNotBlank(authHeader) && authHeader.startsWith("Bearer ")) {
|
||||
return authHeader.substring(7);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package oailab.com.apistart.interceptor;
|
||||
|
||||
import cn.hutool.jwt.JWT;
|
||||
import cn.hutool.jwt.JWTUtil;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import oailab.com.apiservice.annotation.PassToken;
|
||||
import oailab.com.apiservice.constants.LoginConstants;
|
||||
import oailab.com.apiservice.enums.sys.ErrorCodeEnum;
|
||||
import oailab.com.apiservice.exception.BizException;
|
||||
import oailab.com.apiservice.mybatis.entity.sys.TenantAccount;
|
||||
import oailab.com.apiservice.mybatis.service.sys.TenantAccountService;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Token validation interceptor.
|
||||
*
|
||||
* @author Benny Deng
|
||||
* @version 1.0
|
||||
* @see <a href="http://www.bianmaren.com">Benny Deng</a>
|
||||
* @since 6/17/24 PM11:19
|
||||
**/
|
||||
@Slf4j
|
||||
@Component("sysTokenInterceptor")
|
||||
public class SysTokenInterceptor implements HandlerInterceptor {
|
||||
|
||||
@Resource
|
||||
private TenantAccountService tenantAccountService;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) {
|
||||
|
||||
// 从 http 请求头中取出 token
|
||||
String token = request.getHeader(LoginConstants.LOGIN_AUTHORIZATION);
|
||||
|
||||
// 如果不是映射到方法直接通过
|
||||
if (!(handler instanceof HandlerMethod handlerMethod)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Method method = handlerMethod.getMethod();
|
||||
|
||||
// 检查是否有 @PassToken 注释,有则跳过认证
|
||||
if (method.isAnnotationPresent(PassToken.class)) {
|
||||
PassToken passToken = method.getAnnotation(PassToken.class);
|
||||
if (passToken.required()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 无token
|
||||
if (token == null) {
|
||||
throw new BizException(ErrorCodeEnum.TokenIsNull);
|
||||
}
|
||||
|
||||
token = extractBearerToken(token);
|
||||
|
||||
// 获取 token 中的 user account
|
||||
String account;
|
||||
JWT jwt = JWTUtil.parseToken(token);
|
||||
account = jwt.getPayload("account").toString();
|
||||
|
||||
TenantAccount tenantAccount = tenantAccountService.getByAccount(account);
|
||||
if (null == tenantAccount) {
|
||||
throw new BizException(ErrorCodeEnum.AccountDoesNotExist);
|
||||
}
|
||||
|
||||
// 验证 token
|
||||
if (!JWTUtil.verify(token, tenantAccount.getPassword().getBytes())) {
|
||||
throw new BizException(ErrorCodeEnum.AuthTokenInvalid);
|
||||
}
|
||||
|
||||
// 将提取的account,放入请求
|
||||
request.setAttribute(LoginConstants.SYS_LOGIN_USER_ACCOUNT, account);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove "Bearer " prefix
|
||||
*
|
||||
* @param authorizationHeader 授权码
|
||||
*/
|
||||
private static String extractBearerToken(String authorizationHeader) {
|
||||
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
|
||||
// Remove "Bearer " prefix
|
||||
return authorizationHeader.substring(7);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, ModelAndView modelAndView) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, Exception ex) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package oailab.com.apistart.interceptor;
|
||||
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import oailab.com.apiservice.annotation.PassToken;
|
||||
import oailab.com.apiservice.constants.LoginConstants;
|
||||
import oailab.com.apiservice.enums.sys.ErrorCodeEnum;
|
||||
import oailab.com.apiservice.exception.BizException;
|
||||
import oailab.com.apiservice.mybatis.entity.workspace.EndUsers;
|
||||
import oailab.com.apiservice.mybatis.service.workspace.EndUsersService;
|
||||
import oailab.com.apiservice.service.PassportBusinessService;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Token validation interceptor.
|
||||
*
|
||||
* @author Benny Deng
|
||||
* @version 1.0
|
||||
* @see <a href="http://www.bianmaren.com">Benny Deng</a>
|
||||
* @since 6/17/24 PM11:19
|
||||
**/
|
||||
@Slf4j
|
||||
@Component("webTokenInterceptor")
|
||||
public class WebTokenInterceptor implements HandlerInterceptor {
|
||||
|
||||
@Resource
|
||||
private PassportBusinessService passportBusinessService;
|
||||
@Resource
|
||||
private EndUsersService endUsersService;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) {
|
||||
|
||||
// 从 http 请求头中取出 token
|
||||
String token = request.getHeader(LoginConstants.LOGIN_AUTHORIZATION);
|
||||
|
||||
// 如果不是映射到方法直接通过
|
||||
if (!(handler instanceof HandlerMethod handlerMethod)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Method method = handlerMethod.getMethod();
|
||||
|
||||
// 检查是否有 @PassToken 注释,有则跳过认证
|
||||
if (method.isAnnotationPresent(PassToken.class)) {
|
||||
PassToken passToken = method.getAnnotation(PassToken.class);
|
||||
if (passToken.required()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 无token
|
||||
if (token == null) {
|
||||
throw new BizException(ErrorCodeEnum.TokenIsNull);
|
||||
}
|
||||
|
||||
token = extractBearerToken(token);
|
||||
|
||||
Map<String, Object> payload = passportBusinessService.parse(token);
|
||||
|
||||
String endUserId = (String) payload.get("endUserId");
|
||||
|
||||
EndUsers endUser = endUsersService.getById(endUserId);
|
||||
if (null == endUser) {
|
||||
throw new BizException(ErrorCodeEnum.AccountDoesNotExist);
|
||||
}
|
||||
|
||||
// 验证 token
|
||||
if (!passportBusinessService.verify(token)) {
|
||||
throw new BizException(ErrorCodeEnum.AuthTokenInvalid);
|
||||
}
|
||||
|
||||
// 将提取的account,放入请求
|
||||
request.setAttribute(LoginConstants.WEB_LOGIN_USER_ACCOUNT, endUserId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove "Bearer " prefix
|
||||
*
|
||||
* @param authorizationHeader 授权码
|
||||
*/
|
||||
private static String extractBearerToken(String authorizationHeader) {
|
||||
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
|
||||
// Remove "Bearer " prefix
|
||||
return authorizationHeader.substring(7);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, ModelAndView modelAndView) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, Exception ex) {
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user