diff --git a/blade-auth/src/main/java/org/springblade/auth/dto/IamSsoProfileResponse.java b/blade-auth/src/main/java/org/springblade/auth/dto/IamSsoProfileResponse.java index d51f7f8..c2ff4f6 100644 --- a/blade-auth/src/main/java/org/springblade/auth/dto/IamSsoProfileResponse.java +++ b/blade-auth/src/main/java/org/springblade/auth/dto/IamSsoProfileResponse.java @@ -26,6 +26,7 @@ package org.springblade.auth.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonAlias; import lombok.Data; import java.util.Map; @@ -43,6 +44,13 @@ public class IamSsoProfileResponse { */ private String id; + /** + * IAM返回的本系统账号,部分部署直接返回在顶层。 + */ + @JsonProperty("account_no") + @JsonAlias({"accountNo", "account", "username", "user_name"}) + private String accountNo; + /** * IAM用户扩展属性 */ @@ -54,12 +62,20 @@ public class IamSsoProfileResponse { * @return 本系统账号 */ @JsonProperty(access = JsonProperty.Access.READ_ONLY) - public String getAccountNo() { - if (attributes == null) { - return null; + public String resolveAccountNo() { + if (accountNo != null && !accountNo.isBlank()) { + return accountNo; } - Object accountNo = attributes.get("account_no"); - return accountNo == null ? null : String.valueOf(accountNo); + if (attributes == null) { + return id; + } + for (String key : new String[]{"account_no", "accountNo", "account", "username", "user_name"}) { + Object value = attributes.get(key); + if (value != null && !String.valueOf(value).isBlank()) { + return String.valueOf(value); + } + } + return id; } } diff --git a/blade-auth/src/main/java/org/springblade/auth/granter/IamSsoTokenGranter.java b/blade-auth/src/main/java/org/springblade/auth/granter/IamSsoTokenGranter.java index 6c6d279..c1dfecf 100644 --- a/blade-auth/src/main/java/org/springblade/auth/granter/IamSsoTokenGranter.java +++ b/blade-auth/src/main/java/org/springblade/auth/granter/IamSsoTokenGranter.java @@ -143,7 +143,7 @@ public class IamSsoTokenGranter extends AuthorizationCodeGranter { } IamSsoProfileResponse profileResponse = requestIamProfile(accessToken); - String accountNo = profileResponse.getAccountNo(); + String accountNo = profileResponse.resolveAccountNo(); if (StringUtil.isBlank(accountNo)) { log.warn("IAM统一身份认证用户信息缺少account_no,iamId={}", profileResponse.getId()); throw new UserInvalidException(OAuth2TokenConstant.USER_NOT_FOUND); @@ -165,23 +165,32 @@ public class IamSsoTokenGranter extends AuthorizationCodeGranter { private UserInfo loadOrCreateIamUser(OAuth2Request request, IamSsoProfileResponse profileResponse, String tenantId, String accountNo) { R result = userClient.userInfo(tenantId, accountNo); - if (result.isSuccess() && result.getData() != null) { + if (result.isSuccess() && hasUser(result.getData())) { return result.getData(); } - log.info("IAM统一身份认证未匹配到本系统账号,开始自动创建用户,tenantId={}, accountNo={}", tenantId, accountNo); + log.info("IAM统一身份认证未匹配到本系统账号,开始自动创建用户,tenantId={}, accountNo={}, querySuccess={}", + tenantId, accountNo, result.isSuccess()); R saveResult = userClient.saveIamUser(buildIamUser(profileResponse, tenantId, accountNo)); if (!saveResult.isSuccess() || !Boolean.TRUE.equals(saveResult.getData())) { log.warn("IAM统一身份认证自动创建用户失败,tenantId={}, accountNo={}, msg={}", tenantId, accountNo, saveResult.getMsg()); throw new UserInvalidException(OAuth2TokenConstant.USER_NOT_FOUND); } R createdResult = userClient.userInfo(tenantId, accountNo); - if (!createdResult.isSuccess() || createdResult.getData() == null) { + if (!createdResult.isSuccess() || !hasUser(createdResult.getData())) { log.warn("IAM统一身份认证自动创建用户后未查询到用户,tenantId={}, accountNo={}", tenantId, accountNo); throw new UserInvalidException(OAuth2TokenConstant.USER_NOT_FOUND); } return createdResult.getData(); } + /** + * Feign 返回成功时,data 仍可能是一个 user 为空的 UserInfo 包装对象。 + * IAM 登录必须确保本地用户实体存在,避免被误报为密码校验失败。 + */ + private boolean hasUser(UserInfo userInfo) { + return userInfo != null && userInfo.getUser() != null; + } + private User buildIamUser(IamSsoProfileResponse profileResponse, String tenantId, String accountNo) { User user = new User(); user.setTenantId(tenantId); diff --git a/blade-auth/src/main/java/org/springblade/auth/handler/BladeAuthorizationHandler.java b/blade-auth/src/main/java/org/springblade/auth/handler/BladeAuthorizationHandler.java index 1526d27..e6e793e 100644 --- a/blade-auth/src/main/java/org/springblade/auth/handler/BladeAuthorizationHandler.java +++ b/blade-auth/src/main/java/org/springblade/auth/handler/BladeAuthorizationHandler.java @@ -40,6 +40,7 @@ import org.springblade.core.tool.jackson.JsonUtil; import org.springblade.core.tool.utils.DateUtil; import org.springblade.core.tool.utils.DesUtil; import org.springblade.core.tool.utils.SM2Util; +import org.springblade.core.tool.utils.StringUtil; import org.springblade.system.cache.SysCache; import org.springblade.system.pojo.entity.Tenant; @@ -74,6 +75,10 @@ public class BladeAuthorizationHandler extends AbstractAuthorizationHandler { */ @Override public OAuth2Validation preValidation(OAuth2Request request) { + // IAM授权码已经由外部身份系统完成认证,不读取或校验本地密码。 + if (StringUtil.equals("iam_sso", request.getGrantType())) { + return new OAuth2Validation(); + } if (request.isPassword() || request.isCaptchaCode()) { // 生产环境弱密码校验 if (bladeProperties.isProd() && isWeakPassword(request.getPassword())) {