跳到主要内容

SSO

CETA上的应用除了支持用户名密码登录,还支持准的oauth2,cas,oidc协议的sso

1.文档

请先熟悉需要实现的SSO协议

  1. OAUTH2 : 英文文档,中文文档
  2. OIDC :英文文档,中文文档
  3. CAS :英文文档

2.原理

相信如果你已经通读了上述SSO协议,作为一个Client端,只需要关心怎么去调用认证服务(即redirect_uri),和认证完成的回调(即callback,通过callback确定是那个用户),刚好ceta提供了redirect_url和callback的扩展。

3.使用步骤

3.1 配置Login

配置登录方式,详情请看下图

  1. Unique Identifier : 唯一识别码
  2. Auth Type : 如果是用户名密码的用basic,sso的随意
  3. Login Property : 只有是basic的时候才需要配置,表示匹配用户表的那个字段,一般是email/nickname等
  4. Need Captcha : 是否需要验证码,也只有basic的时候才起作用
  5. Hide : 是否展示
  6. Default : 是否默认
  7. Order : 展示顺序 Login-config 配置后的展示效果 Login

3.2 配置 redirect_url 的flow

  1. Rest trigger 要求

    • 必须是一个 REST-Flow。
    • 使用 POST 请求。
    • 需要一个输入参数:
      • loginMethod:当前登录方式,对应3.1 配置项。
  2. 流程输出设置

    • 输出值的键为 redirectUrl,内容为第三方认证服务的地址。

Redirect Url

3.3 配置callback 的flow

  1. Rest trigger 要求

    • 必须是一个 REST-Flow。
    • 使用 POST 请求。
    • 需要两个输入参数:
      • loginMethod:当前登录方式,对应 3.1 配置项。
      • callback:认证服务返回的内容。
  2. 流程输出设置

    • 必须包含以下四个值:
      • ssoUserId:SSO 用户的唯一标识。
      • ssoType:选择登录方式的 3.1 配置项Auth Type
      • ssoEmail:用户邮箱。
      • ssoPhone:用户电话号码。 Callback

3.4 配置用户

User

  1. 登录类型: 支持那种sso,对应用户表的sso_type
  2. 统一登录用户名: SSO callback 的用户唯一标识,对应用户表的sso_username
  3. 允许本地登录: 是否允许用户名密码登录,对应用户表的sso_allow_builtin

3.5 用户匹配

  1. 用户匹配逻辑 getSsoMappedUser

    • 通过 ssoUserIdssoTypeprojectToken 查找用户。
    • 首先尝试匹配 sso_typesso_username
    • 如果找不到用户,检查 ssoUserId 是否包含 @
      • 如果是邮箱地址,则匹配 sso_typeemail
      • 否则,匹配 sso_typeusername
    • 过滤掉 sso_username 为空的用户。
    • 如果找到多个用户,抛出异常。
    • 如果没有找到用户,返回 null
    • 返回找到的用户(如果有)。
  2. 匹配步骤

    • 先用 3.3的ssoUserId 进行匹配。
    • 如果找不到用户且 3.3的ssoEmail 不为空,再用 3.3的ssoEmail 进行匹配。

代码中详细的描述了匹配规则


//用户匹配
private UserPo getSsoMappedUser(String ssoUserId, String ssoType, String projectToken) {
List<UserPo> users = userManagementRepository.findUserByProperties(
projectToken, Map.of(
"sso_type", ssoType,
"sso_username", ssoUserId
));
if (users.isEmpty()) {
if (ssoUserId.contains("@")) {
users = userManagementRepository.findUserByProperties(
projectToken, Map.of(
"sso_type", ssoType,
"email", ssoUserId
));
} else {
users = userManagementRepository.findUserByProperties(
projectToken, Map.of(
"sso_type", ssoType,
"username", ssoUserId
));
}
users = users.stream().filter(userPo -> StringUtils.isBlank(userPo.getSsoUsername())).collect(Collectors.toList());
}
if (users.size() > 1) {
throw new IllegalArgumentException("More than 1 user can login with ssoType: " + ssoType + " ssoId: " + ssoUserId);
}
if (users.isEmpty()) {
return null;
}
return users.get(0);
}

// loginMethodToken 是选择的认证类型
//先用**3.3**返回的ssoUserId匹配
UserPo user = getSsoMappedUser(ssoUserInfo.getSsoUserId(), loginMethodToken, projectToken);
if (null == user && ssoUserInfo.getSsoEmail() != null) {
//如果查找不到,再用**3.3**返回的ssoEmail匹配
user = getSsoMappedUser(ssoUserInfo.getSsoEmail(), loginMethodToken, projectToken);
}