Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 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
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.

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