通过创建认证提供商,可以使用基于OAuth2.0协议的身份验证提供商的用户身份登录纷享销客,从而实现单点登录 (SSO)。
什么叫OAuth2.0协议呢,差不多的意思就是,客户端要去资源拥有者去获取权限码,权限码获得之后客户端带着这个东西去认证服务尚去请求认证,然后获取授权的token,最后根据授权的token去请求资源。
而认证提供商通常指的是提供身份认证的服务或者系统。
通过配置认证提供商之后,就可以使用配置方的用户名和密码来登录纷享销客了,简称纷享免登,地址如下
我们可以通过配置的方式去配置标准化的认证提供商,也可以通过apl函数做自定义的认证提供商。
操作步骤:
一. 首先在企业设置---> 域名管理配置域名,等待审核通过,用来作为 返回登录的单点登录地址的域名。

2. 点击认证提供商,选择新建按钮

3. 提供商类型选择Open ID connect的话,可以按照标准的方式进行配置

具体字段的含义如下
- 提供商类型:OpenId Connet
- 名称:认证提供商名称(最长50个字符)
- URL后缀:用于生成Oauth2 SSO登入、登出和回调地址(最长50个字符,仅可包含下划线和字母数字字符。必须以字母开头,不包括空格,不得以下划线结尾,且不得包含两个连续下划线)
- Consumer Key:身份验证提供方发行的Consumer Key(Client ID)
- Consumer Secret:身份验证提供方发行的Consumer Secret(Client Secret)
- Token接口地址:获取身份验证提供方Token的API地址
- 用户信息接口地址:获取身份验证提供方用户信息的API地址
- 授权范围:身份验证提供方提供,允许访问用户信息的权限描述(不填写时默认传openid)
- 外部用户身份认证字段:从身份验证提供方的用户信息中,选择一个字段作为用户身份验证字段,需要确保此字段值的唯一性
- 纷享用户身份认证字段:从纷享的人员对象中,选择一个字段的API Name作为用户身份验证字段,需要确保此字段值的唯一性单点登录身份验证时,会使用身份验证提供方返回的用户信息的“外部用户身份认证字段”字段值,去查询纷享人员的“纷享用户身份认证字段”字段值,如果有返回结果则身份验证成功。
- 自定义登出地址:需要单点登出时访问的身份验证提供方的登出地址
点击保存之后,就会返回三个地址,其中第一个地址就是单点登录的地址,第二个是身份验证提供方的回调地址,第三个是提供方的单点登出地址。

四. 如果是自定义的认证提供商,就通过函数来进行书写了。

五. 书写函数
比如我尝试用Auth应用来进行配置

class TestOauth implements AuthProviderPlugin {
/**
* 登录时打开 {租户域名}.my.fxiaoke.com 跳转的第三方url
* redirect_uri为认证提供商生成的回调地址
* client_id身份认证提供商发行的client id
* 其他参数根据身份认证提供商的要求依次在该方法的参数中设置即可
*/
@Override
String authorizeUrl(FunctionContext context) {
log.info(context)
return "https://www.otherdomain.com/oauth2/auth?" + "response_type=code&" + "response_mode=query&" + "scope=openid&" + "redirect_uri=https://gbq666.nmy.fxiaoke.com.my.fxiaoke.com/oauth2/sp/callback/xxx" + //第三方登录后的回调地址 "client_id=634e552d1150df923ec8380e";
}
/**
* 第三方登录完成后的回调函数,一般用于获取用户信息
* @param params 第三方回调时,URL的参数
* @return 返回纷享系统的员工ID
* 该方法通常需要实现的逻辑:
* 1.从入参中获取code,用code去身份认证提供商获取token
* 2.获取token后,使用token去获取身份认证提供商的用户信息(外部用户信息)
* 3.用外部用户信息去查询对应的纷享用户信息
* 4.返回查询到的有效的纷享人员id,则代表认证成功。返回无效的纷享人员id,则代表认证失败
*/
@Override
Integer callback(FunctionContext context, Map<String, List<String>> params) {
//租户临时授权模式,如果有其他模式,请阅读第三方文档
String token = getAccessToken(params);
log.info(token);
//从人员对象,获取纷享用户userId
Integer userId = getEmployeeId(token);
log.info(userId);
return userId;
}
/**
* 在纷享系统登出时,自动跳转到第三方系统登出地址
* @return 第三方登录地址,如无需登出第三方,请返回null
*/
@Override
String logoffUrl(FunctionContext context) {
log.info("logoff")
log.info(context)
return "https://login.partner.microsoftonline.cn/9c6b0d0c-e641-4271-b1b9-5e7c552fb812/oauth2/v2.0/logout"
}
private static String appendUrl(String url, Map<String, String> data) {
String paramStr=''
data.each { String key, value -> paramStr += key + "="+ value + "&" }
paramStr = paramStr.substring(0, paramStr.length() - 1)
return url.contains("?") ? (url + "&" + paramStr) : (url + "?" + paramStr);
}
private String getAccessToken(Map<String, List<String>> parameterMap) {
return getAccessTokenByCode(parameterMap["code"].get(0))
}
private String getAccessTokenByCode(String code) {
FormBody body = FormBody.builder()
.field("client_id", "ce0b6a12-46d0-458d-bdf2-877e4e9eae3c")
.field("scope", "openid profile")
.field("grant_type","authorization_code")
.field("redirect_uri", "https://testol.my.fxiaoke.com/oauth/sp/callback/s11")
.field("client_secret", "vm0_7FXL9r~c6.I2.dx_0o883X553veXDl")
.field("code", code)
.build()
Request request = Request.builder()
.method("POST")
.url("https://login.partner.microsoftonline.cn/9c6b0d0c-e641-4271-b1b9-5e7c552fb812/oauth2/v2.0/token")
.timeout(7000)
.retryCount(0)
.body(body)
.build()
HttpResult o = (HttpResult) http.execute(request).getData()
log.info(o)
return o.content["access_token"].toString()
}
Integer getEmployeeId(String accessToken) {
log.info("token======"+accessToken)
HttpResult o = (HttpResult) http.get("https://microsoftgraph.chinacloudapi.cn/oidc/userinfo", ["Authorization": accessToken]).getData();
String user = o.content["email"].toString();
log.info(user)
QueryResult data = (QueryResult) object.find("PersonnelObj",
[["email": user]],
2,
0
).getData()
List<Map> dataList = data.dataList
if (dataList.size() <= 0) {
message.throwErrorMessage("找不到对应的用户, user:" + user)
}
if (dataList.size() > 1) {
message.throwErrorMessage("找到多个对应的用户, user:" + user)
}
return dataList[0]["user_id"].toString() as Integer
}
//debug 时候的入口方法
void debug(FunctionContext context, Map arg) {
}
}
成功返回三个地址之后,就可以使用登录的地址在浏览器地址栏输入,就可以跳转到相应的三方登录页面,输入三方的用户名和密码,就可以登录纷享成功了。
总结:主要就是一个基于aoauth2协议的一个三方登录,应用场景是用户可能希望只希望记住自己系统的密码,或者说嫌麻烦自己只想登录一个系统就可以全部登录。至于那些地址的配置,则需要跟对方服务来进行提供配置即可。