Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yd-user
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
xingmin
yd-user
Commits
8ac650dd
Commit
8ac650dd
authored
Oct 22, 2026
by
jianan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
idP
parent
e488d737
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
220 additions
and
1 deletions
+220
-1
yd-user-api/src/main/java/com/yd/user/api/client/CasdoorClient.java
+96
-0
yd-user-api/src/main/java/com/yd/user/api/config/CasdoorProperties.java
+29
-0
yd-user-api/src/main/java/com/yd/user/api/controller/ApiClientUserController.java
+8
-0
yd-user-api/src/main/java/com/yd/user/api/service/ApiClientUserService.java
+3
-0
yd-user-api/src/main/java/com/yd/user/api/service/impl/ApiClientUserServiceImpl.java
+58
-0
yd-user-feign/src/main/java/com/yd/user/feign/client/clientuser/ApiClientUserFeignClient.java
+20
-1
yd-user-feign/src/main/java/com/yd/user/feign/fallback/clientuser/ApiClientUserFeignFallbackFactory.java
+6
-0
No files found.
yd-user-api/src/main/java/com/yd/user/api/client/CasdoorClient.java
0 → 100644
View file @
8ac650dd
package
com
.
yd
.
user
.
api
.
client
;
import
com.fasterxml.jackson.databind.JsonNode
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.yd.common.exception.BusinessException
;
import
com.yd.user.api.config.CasdoorProperties
;
import
lombok.extern.slf4j.Slf4j
;
import
org.apache.commons.lang3.StringUtils
;
import
org.springframework.http.HttpEntity
;
import
org.springframework.http.HttpHeaders
;
import
org.springframework.http.HttpMethod
;
import
org.springframework.http.MediaType
;
import
org.springframework.http.client.SimpleClientHttpRequestFactory
;
import
org.springframework.stereotype.Component
;
import
org.springframework.web.client.RestTemplate
;
import
java.nio.charset.StandardCharsets
;
import
java.util.LinkedHashMap
;
import
java.util.Map
;
/**
* Casdoor 管理 API 客户端:以应用 clientId/clientSecret 的 Basic Auth 调用,
* 身份为应用 owner。仅封装查询与创建两类原子操作。
*/
@Slf4j
@Component
public
class
CasdoorClient
{
private
final
CasdoorProperties
properties
;
private
final
RestTemplate
restTemplate
;
private
final
ObjectMapper
objectMapper
=
new
ObjectMapper
();
public
CasdoorClient
(
CasdoorProperties
properties
)
{
this
.
properties
=
properties
;
SimpleClientHttpRequestFactory
factory
=
new
SimpleClientHttpRequestFactory
();
factory
.
setConnectTimeout
(
5000
);
factory
.
setReadTimeout
(
10000
);
this
.
restTemplate
=
new
RestTemplate
(
factory
);
}
/**
* 按组织 + 手机号查询用户 subject。
*
* @return Casdoor 用户 id(sub);不存在返回 null
*/
public
String
findSubByPhone
(
String
phone
)
{
String
url
=
String
.
format
(
"%s/api/get-user?owner=%s&phone=%s"
,
properties
.
getEndpoint
(),
properties
.
getOrganization
(),
phone
);
JsonNode
root
=
exchange
(
url
,
HttpMethod
.
GET
,
null
);
JsonNode
data
=
root
.
path
(
"data"
);
if
(
data
.
isMissingNode
()
||
data
.
isNull
())
{
return
null
;
}
return
data
.
path
(
"id"
).
asText
(
null
);
}
/**
* 创建用户,创建后按手机号查回 subject。
*/
public
String
createUserAndGetSub
(
String
username
,
String
displayName
,
String
phone
,
String
password
)
{
Map
<
String
,
Object
>
body
=
new
LinkedHashMap
<>();
body
.
put
(
"owner"
,
properties
.
getOrganization
());
body
.
put
(
"name"
,
username
);
body
.
put
(
"displayName"
,
displayName
);
body
.
put
(
"phone"
,
phone
);
body
.
put
(
"countryCode"
,
"CN"
);
body
.
put
(
"password"
,
password
);
String
url
=
properties
.
getEndpoint
()
+
"/api/add-user"
;
JsonNode
root
=
exchange
(
url
,
HttpMethod
.
POST
,
body
);
if
(!
"ok"
.
equalsIgnoreCase
(
root
.
path
(
"status"
).
asText
()))
{
throw
new
BusinessException
(
"Casdoor 创建用户失败:"
+
root
.
path
(
"msg"
).
asText
());
}
String
sub
=
findSubByPhone
(
phone
);
if
(
StringUtils
.
isBlank
(
sub
))
{
throw
new
BusinessException
(
"Casdoor 创建用户成功但查回 subject 为空,phone="
+
phone
);
}
return
sub
;
}
private
JsonNode
exchange
(
String
url
,
HttpMethod
method
,
Object
body
)
{
HttpHeaders
headers
=
new
HttpHeaders
();
headers
.
setBasicAuth
(
properties
.
getClientId
(),
properties
.
getClientSecret
(),
StandardCharsets
.
UTF_8
);
if
(
body
!=
null
)
{
headers
.
setContentType
(
MediaType
.
APPLICATION_JSON
);
}
try
{
String
response
=
restTemplate
.
exchange
(
url
,
method
,
new
HttpEntity
<>(
body
,
headers
),
String
.
class
).
getBody
();
return
objectMapper
.
readTree
(
response
);
}
catch
(
Exception
e
)
{
log
.
error
(
"调用 Casdoor 管理 API 异常 url={} method={}"
,
url
,
method
,
e
);
throw
new
BusinessException
(
"调用 Casdoor 失败:"
+
e
.
getMessage
());
}
}
}
yd-user-api/src/main/java/com/yd/user/api/config/CasdoorProperties.java
0 → 100644
View file @
8ac650dd
package
com
.
yd
.
user
.
api
.
config
;
import
lombok.Getter
;
import
lombok.Setter
;
import
org.springframework.boot.context.properties.ConfigurationProperties
;
import
org.springframework.stereotype.Component
;
/**
* Casdoor 统一身份提供方配置。
* 凭据通过 Nacos(yd-user-api.yml)注入,不入库、不进 git。
*/
@Getter
@Setter
@Component
@ConfigurationProperties
(
prefix
=
"casdoor"
)
public
class
CasdoorProperties
{
/** Casdoor 站点地址,如 https://auth.anjibao.cn */
private
String
endpoint
;
/** 管理 API 鉴权用的应用 clientId */
private
String
clientId
;
/** 管理 API 鉴权用的应用 clientSecret */
private
String
clientSecret
;
/** 转介人所属 Casdoor 组织(owner) */
private
String
organization
;
}
yd-user-api/src/main/java/com/yd/user/api/controller/ApiClientUserController.java
View file @
8ac650dd
...
@@ -115,4 +115,12 @@ public class ApiClientUserController implements ApiClientUserFeignClient {
...
@@ -115,4 +115,12 @@ public class ApiClientUserController implements ApiClientUserFeignClient {
return
apiClientUserService
.
getByIdpSub
(
tenantBizId
,
idpSub
);
return
apiClientUserService
.
getByIdpSub
(
tenantBizId
,
idpSub
);
}
}
/**
* 为客户端用户开通统一身份
*/
@Override
public
Result
<
String
>
provisionIdp
(
String
clientUserBizId
)
{
return
apiClientUserService
.
provisionIdp
(
clientUserBizId
);
}
}
}
yd-user-api/src/main/java/com/yd/user/api/service/ApiClientUserService.java
View file @
8ac650dd
...
@@ -26,6 +26,9 @@ public interface ApiClientUserService {
...
@@ -26,6 +26,9 @@ public interface ApiClientUserService {
Result
<
ApiClientUserIdpLoginResponse
>
getByIdpSub
(
String
tenantBizId
,
String
idpSub
);
Result
<
ApiClientUserIdpLoginResponse
>
getByIdpSub
(
String
tenantBizId
,
String
idpSub
);
/** 为客户端用户开通统一身份(幂等),返回 Casdoor subject。 */
Result
<
String
>
provisionIdp
(
String
clientUserBizId
);
Result
saveClientUserList
(
List
<
String
>
userBizIdList
,
Result
saveClientUserList
(
List
<
String
>
userBizIdList
,
String
tenantBizId
,
String
tenantBizId
,
String
projectBizId
);
String
projectBizId
);
...
...
yd-user-api/src/main/java/com/yd/user/api/service/impl/ApiClientUserServiceImpl.java
View file @
8ac650dd
...
@@ -12,6 +12,7 @@ import com.yd.common.result.Result;
...
@@ -12,6 +12,7 @@ import com.yd.common.result.Result;
import
com.yd.common.utils.RandomStringGenerator
;
import
com.yd.common.utils.RandomStringGenerator
;
import
com.yd.csf.feign.client.agentreferralrelation.ApiAgentReferralRelationFeignClient
;
import
com.yd.csf.feign.client.agentreferralrelation.ApiAgentReferralRelationFeignClient
;
import
com.yd.csf.feign.request.agentreferralrelation.ApiAgentReferralRelationSaveRequest
;
import
com.yd.csf.feign.request.agentreferralrelation.ApiAgentReferralRelationSaveRequest
;
import
com.yd.user.api.client.CasdoorClient
;
import
com.yd.user.api.service.ApiClientUserService
;
import
com.yd.user.api.service.ApiClientUserService
;
import
com.yd.user.feign.request.clientuser.*
;
import
com.yd.user.feign.request.clientuser.*
;
import
com.yd.user.feign.response.clientuser.ApiClientUserDetailResponse
;
import
com.yd.user.feign.response.clientuser.ApiClientUserDetailResponse
;
...
@@ -46,6 +47,9 @@ public class ApiClientUserServiceImpl implements ApiClientUserService {
...
@@ -46,6 +47,9 @@ public class ApiClientUserServiceImpl implements ApiClientUserService {
@Autowired
@Autowired
private
ApiAgentReferralRelationFeignClient
apiAgentReferralRelationFeignClient
;
private
ApiAgentReferralRelationFeignClient
apiAgentReferralRelationFeignClient
;
@Autowired
private
CasdoorClient
casdoorClient
;
/**
/**
* 分页查询-客户端用户信息
* 分页查询-客户端用户信息
* @param request
* @param request
...
@@ -192,6 +196,60 @@ public class ApiClientUserServiceImpl implements ApiClientUserService {
...
@@ -192,6 +196,60 @@ public class ApiClientUserServiceImpl implements ApiClientUserService {
}
}
/**
/**
* 开通统一身份(幂等):
* 已有 idp_sub 直接返回;否则按手机号查 Casdoor,查无则创建,回写 idp_sub。
*/
@Override
public
Result
<
String
>
provisionIdp
(
String
clientUserBizId
)
{
ClientUser
clientUser
=
iClientUserService
.
queryOne
(
clientUserBizId
);
if
(
Objects
.
isNull
(
clientUser
))
{
throw
new
BusinessException
(
"客户端用户信息不存在"
);
}
if
(
StringUtils
.
isNotBlank
(
clientUser
.
getIdpSub
()))
{
return
Result
.
success
(
clientUser
.
getIdpSub
());
}
String
phone
=
normalizePhone
(
clientUser
.
getPhone
());
if
(
StringUtils
.
isBlank
(
phone
))
{
throw
new
BusinessException
(
"客户端用户手机号为空,无法开通统一身份"
);
}
String
sub
=
casdoorClient
.
findSubByPhone
(
phone
);
if
(
StringUtils
.
isBlank
(
sub
))
{
String
initialPassword
=
buildInitialPassword
(
clientUser
,
phone
);
// Casdoor 登录名用业务ID,保证唯一可溯源;用户实际以手机号登录
sub
=
casdoorClient
.
createUserAndGetSub
(
clientUser
.
getClientUserBizId
(),
clientUser
.
getName
(),
phone
,
initialPassword
);
}
clientUser
.
setIdpSub
(
sub
);
iClientUserService
.
saveOrUpdate
(
clientUser
);
return
Result
.
success
(
sub
);
}
/** 手机号规范化:去非数字、去国家码,得到 11 位本地号码。 */
private
String
normalizePhone
(
String
phone
)
{
if
(
StringUtils
.
isBlank
(
phone
))
{
return
null
;
}
String
digits
=
phone
.
replaceAll
(
"\\D"
,
""
);
if
(
digits
.
startsWith
(
"86"
)
&&
digits
.
length
()
==
13
)
{
digits
=
digits
.
substring
(
2
);
}
return
digits
;
}
/** 规则初始密码:证件号后六位;无证件或不足六位时用手机号后六位。 */
private
String
buildInitialPassword
(
ClientUser
clientUser
,
String
phone
)
{
String
idCard
=
clientUser
.
getIdCardNumber
();
if
(
StringUtils
.
isNotBlank
(
idCard
)
&&
idCard
.
length
()
>=
6
)
{
return
idCard
.
substring
(
idCard
.
length
()
-
6
);
}
if
(
phone
.
length
()
>=
6
)
{
return
phone
.
substring
(
phone
.
length
()
-
6
);
}
throw
new
BusinessException
(
"无法生成初始密码:证件号与手机号均不足六位"
);
}
/**
* 同步sys_user系统用户,批量更新客户端用户表数据
* 同步sys_user系统用户,批量更新客户端用户表数据
* @param userBizIdList
* @param userBizIdList
* @param tenantBizId
* @param tenantBizId
...
...
yd-user-feign/src/main/java/com/yd/user/feign/client/clientuser/ApiClientUserFeignClient.java
View file @
8ac650dd
...
@@ -17,11 +17,12 @@ import java.util.List;
...
@@ -17,11 +17,12 @@ import java.util.List;
/**
/**
* 客户端用户信息Feign客户端
* 客户端用户信息Feign客户端
*/
*/
@FeignClient
(
name
=
"yd-user-api"
,
path
=
"/user/api/clientUser"
,
fallbackFactory
=
ApiClientUserFeignFallbackFactory
.
class
)
@FeignClient
(
name
=
"yd-user-api"
,
path
=
"/user/api/clientUser"
,
fallbackFactory
=
ApiClientUserFeignFallbackFactory
.
class
)
public
interface
ApiClientUserFeignClient
{
public
interface
ApiClientUserFeignClient
{
/**
/**
* 分页查询-客户端用户信息
* 分页查询-客户端用户信息
*
* @param request
* @param request
* @return
* @return
*/
*/
...
@@ -31,6 +32,7 @@ public interface ApiClientUserFeignClient {
...
@@ -31,6 +32,7 @@ public interface ApiClientUserFeignClient {
/**
/**
* 详情-客户端用户信息
* 详情-客户端用户信息
*
* @param clientUserBizId
* @param clientUserBizId
* @return
* @return
*/
*/
...
@@ -41,6 +43,7 @@ public interface ApiClientUserFeignClient {
...
@@ -41,6 +43,7 @@ public interface ApiClientUserFeignClient {
/**
/**
* 根据条件查询客户端用户业务ID列表
* 根据条件查询客户端用户业务ID列表
*
* @param tenantBizId
* @param tenantBizId
* @param tenantBizId
* @param tenantBizId
* @return
* @return
...
@@ -51,6 +54,7 @@ public interface ApiClientUserFeignClient {
...
@@ -51,6 +54,7 @@ public interface ApiClientUserFeignClient {
/**
/**
* 查询-客户端用户详情 body入参
* 查询-客户端用户详情 body入参
*
* @return
* @return
*/
*/
@PostMapping
(
"/body/detail"
)
@PostMapping
(
"/body/detail"
)
...
@@ -58,6 +62,7 @@ public interface ApiClientUserFeignClient {
...
@@ -58,6 +62,7 @@ public interface ApiClientUserFeignClient {
/**
/**
* 保存-客户端用户信息(新增或者编辑)
* 保存-客户端用户信息(新增或者编辑)
*
* @return
* @return
*/
*/
@PostMapping
(
"/save"
)
@PostMapping
(
"/save"
)
...
@@ -65,6 +70,7 @@ public interface ApiClientUserFeignClient {
...
@@ -65,6 +70,7 @@ public interface ApiClientUserFeignClient {
/**
/**
* 编辑-客户端用户审批状态
* 编辑-客户端用户审批状态
*
* @return
* @return
*/
*/
@PutMapping
(
"/edit/approvalStatus"
)
@PutMapping
(
"/edit/approvalStatus"
)
...
@@ -72,6 +78,7 @@ public interface ApiClientUserFeignClient {
...
@@ -72,6 +78,7 @@ public interface ApiClientUserFeignClient {
/**
/**
* 编辑-客户端用户openId
* 编辑-客户端用户openId
*
* @return
* @return
*/
*/
@PutMapping
(
"/edit/openId"
)
@PutMapping
(
"/edit/openId"
)
...
@@ -80,6 +87,7 @@ public interface ApiClientUserFeignClient {
...
@@ -80,6 +87,7 @@ public interface ApiClientUserFeignClient {
/**
/**
* 多条件查询客户端用户列表信息
* 多条件查询客户端用户列表信息
*
* @return
* @return
*/
*/
@PostMapping
(
"/condition/list"
)
@PostMapping
(
"/condition/list"
)
...
@@ -97,4 +105,15 @@ public interface ApiClientUserFeignClient {
...
@@ -97,4 +105,15 @@ public interface ApiClientUserFeignClient {
Result
<
ApiClientUserIdpLoginResponse
>
getByIdpSub
(
Result
<
ApiClientUserIdpLoginResponse
>
getByIdpSub
(
@NotBlank
(
message
=
"租户业务ID不能为空"
)
@RequestParam
(
value
=
"tenantBizId"
)
String
tenantBizId
,
@NotBlank
(
message
=
"租户业务ID不能为空"
)
@RequestParam
(
value
=
"tenantBizId"
)
String
tenantBizId
,
@NotBlank
(
message
=
"IdP subject不能为空"
)
@RequestParam
(
value
=
"idpSub"
)
String
idpSub
);
@NotBlank
(
message
=
"IdP subject不能为空"
)
@RequestParam
(
value
=
"idpSub"
)
String
idpSub
);
/**
* 为已存在的客户端用户开通统一身份:按手机号查/建 Casdoor 用户,回写 idp_sub(幂等)。
*
* @param clientUserBizId 客户端用户业务ID
* @return Casdoor subject
*/
@PostMapping
(
"/provisionIdp"
)
@Operation
(
summary
=
"为客户端用户开通统一身份"
)
Result
<
String
>
provisionIdp
(
@NotBlank
(
message
=
"客户端用户表唯一业务ID不能为空"
)
@RequestParam
(
value
=
"clientUserBizId"
)
String
clientUserBizId
);
}
}
yd-user-feign/src/main/java/com/yd/user/feign/fallback/clientuser/ApiClientUserFeignFallbackFactory.java
View file @
8ac650dd
...
@@ -65,6 +65,12 @@ public class ApiClientUserFeignFallbackFactory implements FallbackFactory<ApiCli
...
@@ -65,6 +65,12 @@ public class ApiClientUserFeignFallbackFactory implements FallbackFactory<ApiCli
log
.
error
(
"按IdP subject解析登录身份失败 tenantBizId={} idpSub={}"
,
tenantBizId
,
idpSub
,
cause
);
log
.
error
(
"按IdP subject解析登录身份失败 tenantBizId={} idpSub={}"
,
tenantBizId
,
idpSub
,
cause
);
return
null
;
return
null
;
}
}
@Override
public
Result
<
String
>
provisionIdp
(
String
clientUserBizId
)
{
log
.
error
(
"为客户端用户开通统一身份失败 clientUserBizId={}"
,
clientUserBizId
,
cause
);
return
null
;
}
};
};
}
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment