Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE t_user
ADD COLUMN use_tenant_id INT AFTER private_key ;
2 changes: 0 additions & 2 deletions app/src/main/resources/sql/h2/init_data_for_test_v1.0.0.sql

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE t_user
ADD COLUMN use_tenant_id INT AFTER private_key ;

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,22 @@ public enum ExceptionEnum implements IBaseError {
/**
* Cm 339 exception enum.
*/
CM339("CM339", "token检验失败,请重新登录");
CM339("CM339", "token检验失败,请重新登录"),

/**
* Cm 340 exception enum.
*/
CM340("CM340", "请求资源不存在"),

/**
* Cm 341 exception enum.
*/
CM341("CM341", "组织在当前用户组织列表中匹配不到"),

/**
* Cm 342 exception enum.
*/
CM342("CM342", "数字格式异常"),;
/**
* 错误码
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public class AiChatController {
@SystemControllerLog(description = "AI chat")
@PostMapping("/ai/chat")
public ResponseEntity<?> aiChat(@RequestBody ChatRequest request,
@RequestHeader(value = "Authorization", required = false) String authorization) throws Exception {
@RequestHeader(value = "Authorization", required = true) String authorization) throws Exception {

if (authorization != null && authorization.startsWith("Bearer ")) {
String token = authorization.replace("Bearer ", "");
Expand Down Expand Up @@ -117,7 +117,7 @@ public ResponseEntity<?> aiChat(@RequestBody ChatRequest request,
@SystemControllerLog(description = "AI completions")
@PostMapping("/chat/completions")
public ResponseEntity<?> completions(@RequestBody ChatRequest request,
@RequestHeader(value = "Authorization", required = false) String authorization) throws Exception {
@RequestHeader(value = "Authorization", required = true) String authorization) throws Exception {
if (authorization != null && authorization.startsWith("Bearer ")) {
String token = authorization.replace("Bearer ", "");
request.setApiKey(token);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ public Result<User> me() {
user.setUsername(loginUserContext.getLoginUserId());
}
user.setTenant(tenants);

user.setPassword(null);
user.setPrivateKey(null);
user.setPublicKey(null);
user.setSalt(null);
return Result.success(user);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ public void addInterceptors(InterceptorRegistry registry) {
// 登录相关
"/platform-center/api/user/login",
// 忘记密码
"/platform-center/api/user/forgot-password"
"/platform-center/api/user/forgot-password",
// AI
"/app-center/api/ai/chat",
"/app-center/api/chat/completions"
);
}
}
Expand Down
164 changes: 96 additions & 68 deletions base/src/main/java/com/tinyengine/it/login/config/SSOInterceptor.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
/**
* Copyright (c) 2023 - present TinyEngine Authors.
* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
*
* <p>
* Use of this source code is governed by an MIT-style license.
*
* <p>
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/

package com.tinyengine.it.login.config;
Expand All @@ -17,6 +16,7 @@
import com.tinyengine.it.login.utils.JwtUtil;
import com.tinyengine.it.login.config.context.DefaultLoginUserContext;
import com.tinyengine.it.login.model.UserInfo;
import com.tinyengine.it.mapper.AuthUsersUnitsRolesMapper;
import com.tinyengine.it.model.entity.Tenant;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
Expand All @@ -34,69 +34,97 @@
@Component
public class SSOInterceptor implements HandlerInterceptor {

@Autowired
private JwtUtil jwtUtil;

@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {

String authorization = request.getHeader("Authorization");
// 如果没有token,重定向到登录页
if (authorization == null || authorization.isEmpty()) {
log.info("No token");
throw new ServiceException(ExceptionEnum.CM336.getResultCode(), ExceptionEnum.CM336.getResultMsg());
}
String token = jwtUtil.getTokenFromRequest(authorization);
String requestURI = request.getRequestURI();

log.info("Intercepting: {}, Token: {}", requestURI, token != null ? "present" : "null");

try {
// 验证token
if (!jwtUtil.validateToken(token)) {
log.warn("Token validation failed");
throw new ServiceException(ExceptionEnum.CM339.getResultCode(), ExceptionEnum.CM339.getResultMsg());
}

// 从token中获取用户信息
String username = jwtUtil.getUsernameFromToken(token);
String userId = jwtUtil.getUserIdFromToken(token);
List<Tenant> tenants = jwtUtil.getTenantIdFromToken(token);
String roles = jwtUtil.getRolesFromToken(token);
Integer platformId = jwtUtil.getPlatformIdFromToken(token);


// 检查必需的用户信息
if (username == null || username.isEmpty() || userId == null) {
log.warn("User information is incomplete - username: {}, userId: {}", username, userId);
throw new ServiceException(ExceptionEnum.CM339.getResultCode(), ExceptionEnum.CM339.getResultMsg());
}

// 存储用户信息到LoginUserContext
UserInfo userInfo = new UserInfo(userId, username, tenants);

userInfo.setPlatformId(platformId != null ? platformId : 0);
userInfo.setRoles(roles != null ? roles : "USER");
userInfo.setToken(token);

DefaultLoginUserContext.setCurrentUser(userInfo);

log.info("Token validated and user context set for user: {}", username);
return true;

} catch (Exception e) {
log.error("Token validation exception: {}", e.getMessage(), e);
DefaultLoginUserContext.clear();
throw new ServiceException(ExceptionEnum.CM339.getResultCode(), ExceptionEnum.CM339.getResultMsg());
}
}

@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
// 请求完成后清理用户上下文
DefaultLoginUserContext.clear();
log.debug("Cleared user context for request completion");
}
@Autowired
private JwtUtil jwtUtil;
@Autowired
AuthUsersUnitsRolesMapper authUsersUnitsRolesMapper;

@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {

String authorization = request.getHeader("Authorization");
String org = request.getHeader("X-Lowcode-Org");
// 如果没有token,重定向到登录页
if (authorization == null || authorization.isEmpty()) {
log.info("No token");
throw new ServiceException(ExceptionEnum.CM336.getResultCode(), ExceptionEnum.CM336.getResultMsg());
}
String token = jwtUtil.getTokenFromRequest(authorization);
String requestURI = request.getRequestURI();

log.info("Intercepting: {}, Token: {}", requestURI, token != null ? "present" : "null");

try {
// 验证token
if (!jwtUtil.validateToken(token)) {
log.warn("Token validation failed");
throw new ServiceException(ExceptionEnum.CM339.getResultCode(), ExceptionEnum.CM339.getResultMsg());
}

// 从token中获取用户信息
String username = jwtUtil.getUsernameFromToken(token);
String userId = jwtUtil.getUserIdFromToken(token);
String roles = jwtUtil.getRolesFromToken(token);
Integer platformId = jwtUtil.getPlatformIdFromToken(token);


// 检查必需的用户信息
if (username == null || username.isEmpty() || userId == null) {
log.warn("User information is incomplete - username: {}, userId: {}", username, userId);
throw new ServiceException(ExceptionEnum.CM339.getResultCode(), ExceptionEnum.CM339.getResultMsg());
}
int userIdInt;
try {
userIdInt = Integer.parseInt(userId);
} catch (NumberFormatException e) {
log.error("Invalid userId format: {}", userId);
throw new ServiceException(ExceptionEnum.CM342.getResultCode(), ExceptionEnum.CM342.getResultMsg());
}
List<Tenant> tenants = authUsersUnitsRolesMapper.queryAllTenantByUserId(userIdInt);
if (tenants == null) {
log.warn("No tenants found for userId: {}", userId);
throw new ServiceException(ExceptionEnum.CM340.getResultCode(), ExceptionEnum.CM340.getResultMsg());
}

if (!"null".equals(org) && org != null) {
boolean findOrg = false;
for (Tenant tenant : tenants) {
tenant.setIsInUse(tenant.getId().equals(org));
if (tenant.getIsInUse()) {
findOrg = true;
}
}
if (!findOrg) {
log.warn("X-Lowcode-Org not found in user's tenants - X-Lowcode-Org: {}", org);
throw new ServiceException(ExceptionEnum.CM341.getResultCode(), ExceptionEnum.CM341.getResultMsg());
}
}
// 存储用户信息到LoginUserContext
UserInfo userInfo = new UserInfo(userId, username, tenants);

userInfo.setPlatformId(platformId != null ? platformId : 0);
userInfo.setRoles(roles != null ? roles : "USER");
userInfo.setToken(token);

DefaultLoginUserContext.setCurrentUser(userInfo);

log.info("Token validated and user context set for user: {}", username);
return true;

} catch (Exception e) {
log.error("Token validation exception: {}", e.getMessage(), e);
DefaultLoginUserContext.clear();
throw new ServiceException(ExceptionEnum.CM339.getResultCode(), ExceptionEnum.CM339.getResultMsg());
}
}

@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
// 请求完成后清理用户上下文
DefaultLoginUserContext.clear();

log.debug("Cleared user context for request completion");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public class DefaultLoginUserContext implements LoginUserContext {
private static final int DEFAULT_PLATFORM = 1;
private static final String DEFAULT_TENANT = "1";



/**
* 返回当前用户所在的业务租户id
*
Expand All @@ -31,11 +33,15 @@ public String getTenantId() {
return DEFAULT_TENANT;
}
for (Tenant tenant : tenantList) {
if (tenant.getIsInUse()) {
return tenant.getId();
if(tenant.getIsInUse()!=null){
if (tenant.getIsInUse()) {
return tenant.getId();
}
}else{
return tenantList.get(0).getId();
}
}

}
return DEFAULT_TENANT;
}

Expand Down Expand Up @@ -69,6 +75,7 @@ public void setTenants(List<Tenant> tenants) {
CURRENT_USER.set(userInfo);
}


/**
* 设置当前用户信息
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,12 @@
import com.tinyengine.it.common.base.Result;
import com.tinyengine.it.common.context.LoginUserContext;
import com.tinyengine.it.common.exception.ExceptionEnum;
import com.tinyengine.it.common.exception.ServiceException;
import com.tinyengine.it.common.log.SystemControllerLog;
import com.tinyengine.it.login.model.*;
import com.tinyengine.it.login.utils.JwtUtil;
import com.tinyengine.it.login.utils.SM3PasswordUtil;
import com.tinyengine.it.login.config.context.DefaultLoginUserContext;
import com.tinyengine.it.login.model.PasswordResult;
import com.tinyengine.it.login.model.PasswordValidationResult;
import com.tinyengine.it.login.model.SSOTicket;
import com.tinyengine.it.login.model.ValidationResult;
import com.tinyengine.it.login.service.ConfigurablePasswordValidator;
import com.tinyengine.it.login.service.LoginService;
import com.tinyengine.it.login.service.TokenBlacklistService;
Expand All @@ -31,15 +29,14 @@
import com.tinyengine.it.model.entity.Tenant;
import com.tinyengine.it.model.entity.User;
import com.tinyengine.it.service.app.UserService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.CrossOrigin;
Expand Down Expand Up @@ -235,13 +232,23 @@ public Result<ValidationResult> validateToken(@RequestParam String token) {
schema = @Schema(implementation = App.class))),
@ApiResponse(responseCode = "400", description = "请求失败")
})

@SystemControllerLog(description = "设置当前组织")
@GetMapping("/user/tenant")
public Result<SSOTicket> setTenant(@RequestParam Integer tenantId) {
List<Tenant> tenants = loginUserContext.getTenants();
int userIdInt;
String userId = loginUserContext.getLoginUserId();
try {
userIdInt = Integer.parseInt(userId);
} catch (NumberFormatException e) {
return Result.failed(ExceptionEnum.CM342);
}
List<Tenant> tenants = authUsersUnitsRolesMapper.queryAllTenantByUserId(userIdInt);

if (tenantId == null) {
return Result.failed(ExceptionEnum.CM320);
}

if (tenants == null || tenants.isEmpty()) {
return Result.failed(ExceptionEnum.CM337);
}
Expand All @@ -259,30 +266,25 @@ public Result<SSOTicket> setTenant(@RequestParam Integer tenantId) {
}

if (!found) {
return Result.failed(ExceptionEnum.CM337);
return Result.failed(ExceptionEnum.CM341);
}
//存储当前组织到LoginUserContext
UserInfo currentUser = DefaultLoginUserContext.getCurrentUser();
currentUser.setTenants(tenantList);
DefaultLoginUserContext.setCurrentUser(currentUser);

// 通过 RequestContextHolder 获取请求
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getRequest();
String authHeader = request.getHeader("Authorization");
String headerToken = jwtUtil.getTokenFromRequest(authHeader);
String headerToken = jwtUtil.getTokenFromRequest(authHeader);
if (headerToken == null || headerToken.isEmpty()) {
return Result.failed(ExceptionEnum.CM336);
}
String token = jwtUtil.generateTokenWithSelectedTenant(headerToken, tenantList);
// 将原 token 加入黑名单
Claims claims = Jwts.parser()
.verifyWith(JwtUtil.getSecretKey())
.build()
.parseSignedClaims(headerToken)
.getPayload();

long expiryTime = claims.getExpiration().getTime();
tokenBlacklistService.blacklistToken(headerToken, expiryTime);
// 创建SSO票据
SSOTicket ticket = new SSOTicket();
ticket.setToken(token);
ticket.setToken(headerToken);
ticket.setUsername(DefaultLoginUserContext.getCurrentUser().getUsername());
ticket.setExpireTime(System.currentTimeMillis() + 3600000);

Expand Down
Loading
Loading