Skip to content

Commit 35b7eed

Browse files
committed
feat: add login API
1 parent 283a711 commit 35b7eed

File tree

25 files changed

+793
-119
lines changed

25 files changed

+793
-119
lines changed

app/src/main/java/com/tinyengine/it/config/context/DefaultLoginUserContext.java

Lines changed: 0 additions & 51 deletions
This file was deleted.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
drop table if exists `t_permission_role`;
2+
3+
create table `t_permission_role`
4+
(
5+
`id` int not null auto_increment comment '主键id',
6+
`name` varchar(255) not null comment '名称',
7+
`description` varchar(2000) comment '描述',
8+
`created_by` varchar(60) not null comment '创建人',
9+
`created_time` timestamp not null default current_timestamp comment '创建时间',
10+
`last_updated_by` varchar(60) not null comment '最后修改人',
11+
`last_updated_time` timestamp not null default current_timestamp comment '更新时间',
12+
primary key (`id`) using btree,
13+
unique index `u_idx_permission_role` (`name_cn`) using btree
14+
) engine = innodb comment = '';
15+
16+
drop table if exists `t_auth_usesr_units_roles`;
17+
18+
create table `t_auth_users_units_roles`
19+
(
20+
`id` int not null auto_increment comment '主键id',
21+
`user_id` int not null comment '用户',
22+
`unit_id` int not null comment '业务单元',
23+
`unit_type` int not null comment '业务单元类型',
24+
`tenant_id` int not null comment '组织id',
25+
`role_id` int not null comment '角色id',
26+
`expired_time` timestamp comment '过期时间',
27+
`created_by` varchar(60) not null comment '创建人',
28+
`created_time` timestamp not null default current_timestamp comment '创建时间',
29+
`last_updated_by` varchar(60) not null comment '最后修改人',
30+
`last_updated_time` timestamp not null default current_timestamp comment '更新时间',
31+
primary key (`id`) using btree,
32+
unique index `u_idx_auth_users_units_roles` (`user_id, unit_id, unit_type`) using btree
33+
) engine = innodb comment = '';

app/src/main/resources/sql/mysql/init_data_2025_1023.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,10 @@ INSERT INTO `t_business_category` (`id`, `code`, `name`, `business_group`, `tena
1111
INSERT INTO `t_business_category` (`id`, `code`, `name`, `business_group`, `tenant_id`, `renter_id`, `site_id`, `created_by`, `created_time`, `last_updated_by`, `last_updated_time`) VALUES (11, 'governmentAgency', '政府机构', '行业', '1', '1', '1', '1', '2025-10-14 00:26:27', '1', '2025-10-14 00:26:27');
1212
INSERT INTO `t_business_category` (`id`, `code`, `name`, `business_group`, `tenant_id`, `renter_id`, `site_id`, `created_by`, `created_time`, `last_updated_by`, `last_updated_time`) VALUES (12, 'Internet', '互联网', '行业', '1', '1', '1', '1', '2025-10-14 00:26:27', '1', '2025-10-14 00:26:27');
1313
INSERT INTO `t_business_category` (`id`, `code`, `name`, `business_group`, `tenant_id`, `renter_id`, `site_id`, `created_by`, `created_time`, `last_updated_by`, `last_updated_time`) VALUES (13, 'serviceTraining', '服务培训', '行业', '1', '1', '1', '1', '2025-10-14 00:26:27', '1', '2025-10-14 00:26:27');
14+
15+
INSERT INTO `t_permission_role` (`id`, `name`, `description`, `created_by`, `created_time`, `last_updated_by`, `last_updated_time`) VALUES (1, 'Tinybuilder_Admin', '超级管理员', '1', '2025-10-29 01:37:10', '1', '2025-10-29 01:37:10');
16+
INSERT INTO `t_permission_role` (`id`, `name`, `description`, `created_by`, `created_time`, `last_updated_by`, `last_updated_time`) VALUES (2, 'Tinybuilder_Tenant_Admin', '组织管理员', '1', '2025-10-29 01:37:36', '1', '2025-10-29 01:37:36');
17+
INSERT INTO `t_permission_role` (`id`, `name`, `description`, `created_by`, `created_time`, `last_updated_by`, `last_updated_time`) VALUES (3, 'Tinybuilder_Platform_Admin', '设计器管理员', '1', '2025-10-29 01:38:00', '1', '2025-10-29 01:38:00');
18+
INSERT INTO `t_permission_role` (`id`, `name`, `description`, `created_by`, `created_time`, `last_updated_by`, `last_updated_time`) VALUES (4, 'Tinybuilder_App_Admin', '应用管理员', '1', '2025-10-29 01:39:06', '1', '2025-10-29 01:39:06');
19+
INSERT INTO `t_permission_role` (`id`, `name`, `description`, `created_by`, `created_time`, `last_updated_by`, `last_updated_time`) VALUES (5, 'Tinybuilder_App_Developer', '应用开发者', '1', '2025-10-29 01:39:26', '1', '2025-10-29 01:39:26');
20+
INSERT INTO `t_permission_role` (`id`, `name`, `description`, `created_by`, `created_time`, `last_updated_by`, `last_updated_time`) VALUES (6, 'Guest', '游客', '1', '2025-10-29 01:39:38', '1', '2025-10-29 01:39:38');

app/src/main/resources/sql/mysql/update_table_ddl_2025_1014.sql

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,7 @@ ALTER TABLE t_app_extension ADD INDEX u_idx_app_extension (`tenant_id`, `platfor
1717
ALTER TABLE t_model ADD COLUMN app_id INT AFTER id;
1818
ALTER TABLE t_model ADD COLUMN platform_id INT AFTER app_id;
1919
ALTER TABLE t_model DROP INDEX u_idx_model;
20-
ALTER TABLE t_model ADD INDEX u_idx_model (`tenant_id`, `platform_id`, `app_id`, `name_cn`,`version`);
20+
ALTER TABLE t_model ADD INDEX u_idx_model (`tenant_id`, `platform_id`, `app_id`, `name_cn`,`version`);
21+
22+
ALTER TABLE t_user ADD COLUMN password VARCHAR(200) AFTER username;
23+
ALTER TABLE t_user ADD COLUMN salt VARCHAR(200) AFTER password;

base/src/main/java/com/tinyengine/it/common/context/LoginUserContext.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,6 @@ public interface LoginUserContext {
3434
*/
3535
String getRenterId();
3636

37-
/**
38-
* 返回当前应用信息
39-
* @return 应用ID
40-
*/
41-
int getAppId();
42-
4337
/**
4438
* 返回当前设计器信息
4539
* @return 设计器ID

base/src/main/java/com/tinyengine/it/controller/ResourceController.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ public Result<Resource> createResource(@Valid @RequestBody Resource resource) th
187187
})
188188
@SystemControllerLog(description = "上传图片")
189189
@PostMapping("/resource/upload")
190-
public Result<Resource> resourceUpload(@RequestParam MultipartFile file) throws Exception {
190+
public Result<Resource> resourceUpload(@RequestParam MultipartFile file, Integer appId) throws Exception {
191191
// 获取文件的原始名称
192192
String fileName = StringUtils.cleanPath(java.util.Optional.ofNullable(file.getOriginalFilename()).orElse("image"));
193193

@@ -202,7 +202,7 @@ public Result<Resource> resourceUpload(@RequestParam MultipartFile file) throws
202202
Resource resource = new Resource();
203203
resource.setName(fileName);
204204
resource.setResourceData(base64);
205-
resource.setAppId(loginUserContext.getAppId());
205+
resource.setAppId(appId);
206206
Resource result = resourceService.resourceUpload(resource);
207207
return Result.success(result);
208208
}
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
package com.tinyengine.it.login.Utils;
2+
3+
import io.jsonwebtoken.Claims;
4+
import io.jsonwebtoken.Jwts;
5+
import io.jsonwebtoken.SignatureAlgorithm;
6+
import io.jsonwebtoken.security.Keys;
7+
import org.springframework.stereotype.Component;
8+
9+
import javax.crypto.SecretKey;
10+
import java.util.Date;
11+
import java.util.HashMap;
12+
import java.util.Map;
13+
14+
@Component
15+
public class JwtUtil {
16+
17+
private static final long EXPIRATION_TIME = 3600000; // 1小时
18+
private static final String SECRET_STRING = "your-secret-key-at-least-32-chars-long-here";
19+
private static final SecretKey SECRET_KEY = Keys.hmacShaKeyFor(SECRET_STRING.getBytes());
20+
21+
/**
22+
* 生成包含完整用户信息的 JWT Token
23+
*/
24+
public String generateToken(String username, String roles, String userId,
25+
String tenantId, String renterId, Integer platformId, String siteId) {
26+
Map<String, Object> claims = new HashMap<>();
27+
claims.put("username", username);
28+
claims.put("roles", roles);
29+
claims.put("userId", userId);
30+
claims.put("tenantId", tenantId);
31+
claims.put("renterId", renterId);
32+
claims.put("platformId", platformId);
33+
claims.put("siteId", siteId);
34+
35+
return Jwts.builder()
36+
.claims(claims)
37+
.subject(username)
38+
.issuedAt(new Date())
39+
.expiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
40+
.signWith(SECRET_KEY)
41+
.compact();
42+
}
43+
44+
/**
45+
* 从 Token 中获取用户名
46+
*/
47+
public String getUsernameFromToken(String token) {
48+
try {
49+
Claims claims = Jwts.parser()
50+
.verifyWith(SECRET_KEY)
51+
.build()
52+
.parseSignedClaims(token)
53+
.getPayload();
54+
55+
return claims.getSubject();
56+
} catch (Exception e) {
57+
System.err.println("Failed to get username from token: " + e.getMessage());
58+
return null;
59+
}
60+
}
61+
62+
/**
63+
* 从 Token 中获取角色信息
64+
*/
65+
public String getRolesFromToken(String token) {
66+
return getClaimFromToken(token, "roles", String.class);
67+
}
68+
69+
/**
70+
* 从 Token 中获取用户ID
71+
*/
72+
public String getUserIdFromToken(String token) {
73+
return getClaimFromToken(token, "userId", String.class);
74+
}
75+
76+
/**
77+
* 从 Token 中获取租户ID
78+
*/
79+
public String getTenantIdFromToken(String token) {
80+
return getClaimFromToken(token, "tenantId", String.class);
81+
}
82+
83+
/**
84+
* 从 Token 中获取业务租户ID
85+
*/
86+
public String getRenterIdFromToken(String token) {
87+
return getClaimFromToken(token, "renterId", String.class);
88+
}
89+
90+
/**
91+
* 从 Token 中获取平台ID
92+
*/
93+
public Integer getPlatformIdFromToken(String token) {
94+
return getClaimFromToken(token, "platformId", Integer.class);
95+
}
96+
97+
/**
98+
* 从 Token 中获取站点ID
99+
*/
100+
public String getSiteIdFromToken(String token) {
101+
return getClaimFromToken(token, "siteId", String.class);
102+
}
103+
104+
/**
105+
* 通用的claim获取方法
106+
*/
107+
private <T> T getClaimFromToken(String token, String claimName, Class<T> clazz) {
108+
try {
109+
Claims claims = Jwts.parser()
110+
.verifyWith(SECRET_KEY)
111+
.build()
112+
.parseSignedClaims(token)
113+
.getPayload();
114+
115+
return claims.get(claimName, clazz);
116+
} catch (Exception e) {
117+
System.err.println("Failed to get claim '" + claimName + "' from token: " + e.getMessage());
118+
return null;
119+
}
120+
}
121+
122+
/**
123+
* 验证 Token 是否有效
124+
*/
125+
public boolean validateToken(String token) {
126+
try {
127+
Jwts.parser()
128+
.verifyWith(SECRET_KEY)
129+
.build()
130+
.parseSignedClaims(token);
131+
return true;
132+
} catch (Exception e) {
133+
System.err.println("Token validation failed: " + e.getMessage());
134+
return false;
135+
}
136+
}
137+
138+
/**
139+
* 检查 Token 是否过期
140+
*/
141+
public boolean isTokenExpired(String token) {
142+
try {
143+
Claims claims = Jwts.parser()
144+
.verifyWith(SECRET_KEY)
145+
.build()
146+
.parseSignedClaims(token)
147+
.getPayload();
148+
149+
return claims.getExpiration().before(new Date());
150+
} catch (Exception e) {
151+
return true;
152+
}
153+
}
154+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.tinyengine.it.login.Utils;
2+
3+
import com.tinyengine.it.login.model.PasswordResult;
4+
import org.bouncycastle.jce.provider.BouncyCastleProvider;
5+
import java.security.MessageDigest;
6+
import java.security.Security;
7+
import java.util.UUID;
8+
9+
/**
10+
* SM3 密码哈希工具类
11+
*/
12+
public class SM3PasswordUtil {
13+
14+
static {
15+
Security.addProvider(new BouncyCastleProvider());
16+
}
17+
18+
private static final String SM3_ALGORITHM = "SM3";
19+
private static final String PASSWORD_SALT_PREFIX = "SM3_";
20+
21+
/**
22+
* SM3 哈希计算
23+
*/
24+
public static String sm3Hash(String data, String salt) throws Exception {
25+
MessageDigest md = MessageDigest.getInstance(SM3_ALGORITHM, "BC");
26+
String dataWithSalt = data + salt;
27+
byte[] hash = md.digest(dataWithSalt.getBytes("UTF-8"));
28+
return bytesToHex(hash);
29+
}
30+
31+
/**
32+
* 创建用户密码
33+
*/
34+
public static PasswordResult createPassword(String plainPassword) throws Exception {
35+
String salt = generateSalt();
36+
String passwordHash = sm3Hash(plainPassword, salt);
37+
return new PasswordResult(passwordHash, salt);
38+
}
39+
40+
/**
41+
* 验证用户密码
42+
*/
43+
public static boolean verifyPassword(String inputPassword, String storedHash, String salt) throws Exception {
44+
String computedHash = sm3Hash(inputPassword, salt);
45+
return computedHash.equals(storedHash);
46+
}
47+
48+
/**
49+
* 生成随机盐值
50+
*/
51+
private static String generateSalt() {
52+
return PASSWORD_SALT_PREFIX + UUID.randomUUID().toString().replace("-", "").substring(0, 16);
53+
}
54+
55+
private static String bytesToHex(byte[] bytes) {
56+
StringBuilder hexString = new StringBuilder();
57+
for (byte b : bytes) {
58+
String hex = Integer.toHexString(0xff & b);
59+
if (hex.length() == 1) {
60+
hexString.append('0');
61+
}
62+
hexString.append(hex);
63+
}
64+
return hexString.toString();
65+
}
66+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.tinyengine.it.login.config;
2+
3+
import org.springframework.context.annotation.Configuration;
4+
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
5+
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
6+
7+
@Configuration
8+
public class LoginConfig implements WebMvcConfigurer {
9+
10+
private final SSOInterceptor ssoInterceptor;
11+
12+
public LoginConfig(SSOInterceptor ssoInterceptor) {
13+
this.ssoInterceptor = ssoInterceptor;
14+
}
15+
16+
@Override
17+
public void addInterceptors(InterceptorRegistry registry) {
18+
registry.addInterceptor(ssoInterceptor)
19+
.addPathPatterns("/**")
20+
.excludePathPatterns(
21+
// 注册相关
22+
"/register",
23+
"/platform-center/api/user/register",
24+
"/**/register",
25+
26+
// 登录相关
27+
"/login",
28+
"/**/login"
29+
);
30+
}
31+
}
32+
33+

0 commit comments

Comments
 (0)