SCIM 2.0 是一种服务机制,允许用户从第三方 SCIM 客户端推送或配置到 SCIM 服务提供商(即您的网站)中。在大多数情况下,该第三方 SCIM 客户端充当用户管理的权威机构,而网站本身通常禁用用户注册功能。此外,它还设计用于与单点登录(Single Sign-On)结合使用,通过 OpenID Connect 实现身份验证,此时身份提供方同时提供 SCIM 和 OpenID Connect 服务。请注意,SCIM 支持将用户从身份提供方推送到您的网站,但不支持作为 SCIM 客户端,将用户回传至身份提供方。
此功能与支持SCIM 协议的平台或软件兼容,目前经过测试且确认支持的平台或软件:
平台:
软件:
对于大多数系统,您必须在客户端应用程序中配置 SCIM。请参考客户端关于如何配置的文档。
SCIM插件位于"系统管理" -> "扩展" -> SCIM 2.0 集成。或可以在系统管理搜索“SCIM 2.0 集成”。
| 选项 | 必填 | 默认 | 描述 |
| 邮箱地址即用户名 | 否 | X | 如果SCIM客户端将用户名值设定为电子邮件地址,则应进行检查。为防止用户不愿公开的电子邮件地址信息泄露,网站中用户名不能使用电子邮件地址。电子邮件地址将通过从邮箱地址中移除“@domain.com”部分,并确保剩余部分在网站内唯一,必要时添加递增编号来生成。然而,当进行检查时,用户对应的用户名值将被返回给SCIM客户端,此时用户名设置为该电子邮件地址。 |
| 用户删除操作 | 是 | 禁用用户 | 如果SCIM客户端向站点发出 DELETE 请求,此值将决定站点的响应方式。您可以选择不批准用户、永久删除用户,或重新分配内容后删除。若选择重新分配,则会额外提供一个选项,用于指定新的用户,该用户默认为“Former Member”。 |
您可以使用映射选项,在用户配置时将 SCIM 用户属性信息映射到自定义配置字段。该映射通过特定的 JSON 格式进行定义。需要注意的是,启用此功能时,SCIM 客户端仅会收到公共属性和系统字段信息。自定义配置数据会保留在站点中,但不会传递给客户端。
配置对象包含以下两个属性。
{
"attributeMappings": [],
"onCreateOnly": false
}
| 选项 | 描述 |
| attributeMappings | 用于定义用户属性如何映射到自定义配置字段或系统字段的一组属性映射。 |
映射属性有两种方式:系统字段或自定义配置文件字段。系统字段是站点中的常量字段,而配置文件字段则可根据您的站点进行特定配置。您可以映射任何简单值的字段,例如字符串,或包含简单值的数组,例如字符串列表。
字段映射包含以下字段:
{
"mapsTo": { },
"attributes": [],
"format": "",
"onCreateOnly": false
}
| 选项 | 描述 |
| mapsTo | 一个映射对象,用于标识您是映射到系统字段还是配置文件字段。 |
| attributes | 用于定义 mapTo 字段的属性名称或路径的一组字符串,以映射其值。当未指定格式且指定了多个属性时,这些属性将按定义的顺序用空格连接在一起。 |
| format | 定义多个属性的连接方式,或向现有属性添加额外数据。它是一个字符串,其中 {attributeId} 表示要使用的声明。 |
当前支持的系统字段如下:
在定义系统字段时,会在 mapsTo 字段中添加以下 JSON 格式:
"mapsTo": { "systemField": "displayName" }
“systemField” 属性是唯一所需的内容,且必须符合上述定义的预设系统字段ID。
当不使用系统字段时,您可以指定自定义配置文件字段。如果该配置文件字段已存在,则其值由 OpenID Connect 提供程序管理;如果不存在,则在首次尝试写入值时自动创建。
"mapsTo": {"profileField":{"key": "profilrField_key", "label": "FieldLabel","fieldType": "Plain Text","searchable": true } }
您可以为“mapsTo”字段指定一个或多个属性。如果未指定格式,且指定了多个属性时,其值将按在“mapsTo”字段中定义的顺序用空格连接。属性可以是简单类型、复杂类型,或简单值和复杂类型的数组。根据属性类型的不同,“attribute”属性的值也会有所变化。有关可用字段的更多信息,请参阅用户模式,以了解与您的SCIM客户端配置发送内容之间的差异。
简单类型就是字符串或整数值,只需使用属性名即可。
复杂类型是一种以 JSON 对象形式表示的单个值。它通过主属性名,后跟一个点,再接子属性名(如main.subAttribute)来识别。
简单数组类型是一种包含简单值的数组。必须通过查询指定单个值。
"attributes": [ {"attribute": "first_name" }, { "attribute": "emails, "type":"array","delimiter":"," }]
简单的单值声明。
{ "attribute": "first_name" }
值为字符串数组的声明。
{ "attribute": "groups","type":"array", "delimiter":","}
您可以使用格式属性来选择声明值的显示方式。该属性是一个静态字符串,其中包含以 {claimName} 格式化的占位符,而 claimName 是在“声明”区域中定义的名称。当指定多个声明时,此方法非常有用。系统会将声明值替换占位符的显示内容,但数组的情况除外,其显示方式将根据声明定义中的分隔符规则决定。
单值格式
"format":"My name is {first_name}"
多值格式
"format":"{last_name}, {first_name}"
import base64
import datetime
from httpx import Client
from httpx import Timeout
from scim2_client.engines.httpx import SyncSCIMClient
from scim2_models import SearchRequest
from scim2_models import PatchOp, PatchOperation
from scim2_client import SCIMResponseErrorObject
# API Token, format as {apiKey}:{username}
api_token = "s8bxe182l57t7ouugx21p:admin"
token_bytes = api_token.encode('utf-8')
encoded_bytes = base64.b64encode(token_bytes)
base64_token = encoded_bytes.decode('utf-8')
client = Client(
base_url="https://your_site/scim/v2",
headers={
"Rest-Authorization-Method": "API",
"Rest-User-Token": "{token}".format(token=base64_token)
},
timeout=Timeout(timeout=60.0)
)
scim = SyncSCIMClient(client)
scim.discover()
User = scim.get_resource_model("User")
new_username = "kitty"
new_user_password = "123456"
new_user_email = "samlj@example.com"
update_username = "mimi"
# Create resources
user = User(user_name=new_username, password=new_user_password, emails=[{"value":new_user_email,"primary":"true"}])
try:
scim.create(user)
except SCIMResponseErrorObject as exc:
error = exc.to_error()
print(error.detail)
# Query resources, only support eq(equal) as operator, and userName as field in filter
response = scim.query(User, query_parameters=SearchRequest(filter='userName eq "{username}"'.format(username=new_username)))
for user in response.resources:
print(user.user_name)
# Query resources
user = scim.query(User, user.id)
assert user.user_name == new_username
# assert user.meta.last_modified == datetime.datetime(
# 2026, 6, 22, 1, 8, 17, 610000, tzinfo=datetime.timezone.utc
# )
# Update resources
user.display_name = update_username
user = scim.replace(user)
assert user.display_name == update_username
# assert user.meta.last_modified == datetime.datetime(
# 2026, 6, 22, 1, 8, 17, 610000, tzinfo=datetime.timezone.utc
# )
# Modify resources, the value must defined as an object (array).
patch = PatchOp[User](operations=[
PatchOperation(op=PatchOperation.Op.replace_, value={"active": False})
])
response = scim.modify(User, user.id, patch)
# Delete resources
scim.delete(User, user.id)