diff --git a/code/fms/fms-api/config/dbconfigs/README.md b/code/fms/fms-api/config/dbconfigs/README.md
index 54940590..024b9474 100644
--- a/code/fms/fms-api/config/dbconfigs/README.md
+++ b/code/fms/fms-api/config/dbconfigs/README.md
@@ -14,3 +14,14 @@ driver=com.microsoft.sqlserver.jdbc.SQLServerDriver
```
The `*.properties` files in this directory are ignored by Git.
+
+## Development startup for legacy SQL Server
+
+Run the following command from the `fms-api` directory when connecting to a
+legacy SQL Server that only supports TLS 1.0:
+
+```batch
+set JAVA_HOME=D:\devtool\jdk\jdk-21
+set PATH=%JAVA_HOME%\bin;%PATH%
+mvn "-Dspring-boot.run.jvmArguments=-Djava.security.properties=./config/legacy-tls.security" spring-boot:run
+```
diff --git a/code/fms/fms-api/config/legacy-tls.security b/code/fms/fms-api/config/legacy-tls.security
new file mode 100644
index 00000000..882abf47
--- /dev/null
+++ b/code/fms/fms-api/config/legacy-tls.security
@@ -0,0 +1,3 @@
+# Development-only compatibility for SQL Server 2008 R2.
+# Keep this override scoped to the fms-api JVM.
+jdk.tls.disabledAlgorithms=SSLv3, TLSv1.1, DTLSv1.0, RC4, DES, MD5withRSA, DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, ECDH
diff --git a/code/fms/fms-api/pom.xml b/code/fms/fms-api/pom.xml
index 3659fe72..b55a8c78 100644
--- a/code/fms/fms-api/pom.xml
+++ b/code/fms/fms-api/pom.xml
@@ -43,6 +43,11 @@
druid
1.2.18
+
+ org.projectlombok
+ lombok
+ true
+
io.jsonwebtoken
jjwt-api
diff --git a/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/FmsApiApplication.java b/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/FmsApiApplication.java
index 29e2d6e1..f3cc8c9b 100644
--- a/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/FmsApiApplication.java
+++ b/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/FmsApiApplication.java
@@ -1,5 +1,7 @@
package cn.g3soft.fmsapi;
+import cn.g3soft.fmsapi.utils.snowflake.contract.IdGeneratorOptions;
+import cn.g3soft.fmsapi.utils.snowflake.idgen.IdGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
@@ -9,6 +11,10 @@ import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
public class FmsApiApplication {
public static void main(String[] args) {
+ IdGeneratorOptions options = new IdGeneratorOptions();
+ options.WorkerId = 1;
+ IdGenerator.setIdGenerator(options);
+
SpringApplication.run(FmsApiApplication.class, args);
}
diff --git a/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/config/JwtAuthFilter.java b/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/config/JwtAuthFilter.java
index e64c4f80..275afff1 100644
--- a/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/config/JwtAuthFilter.java
+++ b/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/config/JwtAuthFilter.java
@@ -1,6 +1,7 @@
package cn.g3soft.fmsapi.config;
import cn.g3soft.fmsapi.database.OrgContext;
+import cn.g3soft.fmsapi.service.ActiveSessionRegistry;
import cn.g3soft.fmsapi.utils.ApiResponse;
import cn.g3soft.fmsapi.utils.JwtUtils;
import io.jsonwebtoken.JwtException;
@@ -24,6 +25,9 @@ public class JwtAuthFilter extends OncePerRequestFilter {
@Resource
private JwtUtils jwtUtils;
+ @Resource
+ private ActiveSessionRegistry activeSessionRegistry;
+
@Resource
private ObjectMapper objectMapper;
@@ -39,16 +43,42 @@ public class JwtAuthFilter extends OncePerRequestFilter {
HttpServletResponse response,
FilterChain filterChain
) throws ServletException, IOException {
- String orgId;
+ JwtUtils.TokenClaims claims;
try {
String token = getToken(request);
- orgId = jwtUtils.getOrgId(token);
- OrgContext.setOrgId(orgId);
+ claims = jwtUtils.parseToken(token);
} catch (JwtException | IllegalArgumentException exception) {
- writeUnauthorized(response);
+ writeUnauthorized(
+ response,
+ ApiResponse.AUTH_ERROR_CODE,
+ "未登录或登录已失效"
+ );
return;
}
+ if (!activeSessionRegistry.hasActiveSession(claims.orgId(), claims.userId())) {
+ writeUnauthorized(
+ response,
+ ApiResponse.AUTH_ERROR_CODE,
+ "未登录或登录已失效"
+ );
+ return;
+ }
+
+ if (!activeSessionRegistry.isActive(
+ claims.orgId(),
+ claims.userId(),
+ claims.sessionId()
+ )) {
+ writeUnauthorized(
+ response,
+ ApiResponse.SESSION_REPLACED_CODE,
+ "账号已在其他设备登录,请重新登录"
+ );
+ return;
+ }
+
+ OrgContext.setOrgId(claims.orgId());
try {
filterChain.doFilter(request, response);
} finally {
@@ -64,11 +94,12 @@ public class JwtAuthFilter extends OncePerRequestFilter {
return authorization.substring(BEARER_PREFIX.length()).trim();
}
- private void writeUnauthorized(HttpServletResponse response) throws IOException {
- ApiResponse body = ApiResponse.fail(
- ApiResponse.AUTH_ERROR_CODE,
- "未登录或登录已失效"
- );
+ private void writeUnauthorized(
+ HttpServletResponse response,
+ int code,
+ String message
+ ) throws IOException {
+ ApiResponse body = ApiResponse.fail(code, message);
response.setStatus(200);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
diff --git a/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/controller/DataController.java b/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/controller/DataController.java
index 6b37d779..5d0ce85f 100644
--- a/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/controller/DataController.java
+++ b/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/controller/DataController.java
@@ -1,8 +1,11 @@
package cn.g3soft.fmsapi.controller;
+import cn.g3soft.fmsapi.service.DataSaveService;
import cn.g3soft.fmsapi.service.DataService;
import cn.g3soft.fmsapi.utils.ApiResponse;
+import cn.g3soft.fmsapi.utils.snowflake.idgen.IdGenerator;
import jakarta.annotation.Resource;
+import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -19,6 +22,9 @@ public class DataController {
@Resource
private DataService dataService;
+ @Resource
+ private DataSaveService dataSaveService;
+
@PostMapping("/loaddata")
public ApiResponse>> loadData(
@RequestBody(required = false) Map params
@@ -32,4 +38,17 @@ public class DataController {
) throws SQLException {
return ApiResponse.success(dataService.loadDataBySql(params));
}
+
+ @PostMapping("/saveobjt")
+ public ApiResponse saveObject(
+ @RequestBody(required = false) List