# sso - 单点登录扩展

单点登录扩展(Single Sign-On)帮助第三方开发者实现一个个性化的、可复用的、可配置化的单点登录扩展插件。安装单点登录扩展插件后,会在系统设置 > 单点登录中自动显示一个新的单点登录配置方案,用户选择启用后可显示扩展提供的配置参数,配置完成并保存后即可使用安装的单点登录扩展进行系统登录认证。

通过扩展插件可以实现多种常见的单点登录需求:

  1. 增加一个个性化的OAuth2单点登录机制,如支持企业微信
  2. 支持LDAP服务器、Windows域认证
  3. 支持某些特定HTTP头的身份认证
  4. 数字证书单点登录
  5. ……

# 扩展文件结构

/
├──package.json //定义扩展的配置信息
├──main.action.ts //扩展的主体代码,提供后端的脚本逻辑
├──main.action //main.action.ts编译后产生的文件
└──thumbnail.png //缩略图

# package.json

package.json中可以配置扩展提供出去的配置选项,这些选项会出现在系统设置 > 单点登录中。

"contributes":{
    "sso":{
        "settingsForm":{},
    }
}

# main.action.ts

单点登录主要是后端的逻辑,main.action.ts是扩展的后端脚本的默认入口,定义了单点登录扩展需要实现的一些函数逻辑。

/**
 * 通过脚本定义一个个性化的单点登录方案。
 * 
 * 1. 单点登录扩展需要安装到系统,并在【系统设置】-【单点登录】中启用才会真正的生效。
 * 2. 此脚本文件提供了一些约定的函数,扩展实现者按需实现这些函数即可实现自己想要的单点登录机制,并且可以被复用到不同的系统环境下。
 * 3. 可以实现类似OAuth2、CAS的单点登录流程、根据特定的http头进行自动登录、或基于LDAP进行身份验证的登录流程
 * 4. 可以使用`svr-api/sys`中的`getSysSetting`函数获取相关系统设置信息。
 */

/**
 * 当一个未登录的用户访问了需要进行登录才能访问的资源时调用。
 * 
 * 1. 此函数可以帮助实现根据特定的url参数、http头、甚至是IP地址特征等信息进行自动登录。
 * 2. 此函数不是必须实现的,`onNeedSignin`、`onSSOGetAuthRedirectURL`、`onPasswordSignin`通常应该至少实现一个。
 * 
 * @param request web请求对象
 * @param response web响应对象
 * @param ssoArgs 当前正在使用单点登录方案的配置参数信息
 * @returns
 * 	1. 明确返回一个`UserInfo`对象,那么系统将无需用户输入相关密码直接使用返回的用户信息登录并初始化权限信息。  
 * 	2. 返回`true`,表示此函数已经自己处理了response响应信息(比如`response.sendError(403)`,系统将不再继续处理此次Web请求。
 * 	3. 如果抛出异常,那么将提示异常信息给用户。  
 * 	4. 没有返回值,那么系统将继续走后续的登录流程机制,相当于未实现此函数。此时调用者也可以通过自己调用脚本api`loginWithoutPassword`实现登录。
 */
// function onNeedSignin(request: HttpServletRequest, response: HttpServletResponse, ssoArgs: SSOArgs): UserInfo | boolean | void {
// 	return null;
// }

/**
 * 当用户在系统默认的用户名密码登录框中输入用户密码信息进行登录时调用。
 *
 * 1. 可用于支持向第三方LDAP、Windows域、或数据库表进行用户身份信息判断。
 * 2. 账号为 admin 的用户登录时不受这个脚本函数的影响
 * 3. 此函数不是必须实现的,`onNeedSignin`、`onSSOGetAuthRedirectURL`、`onPasswordSignin`通常应该至少实现一个。
 * 
 * @param request
 * @param response 
 * @param userId 用户输入的用户ID或者手机号
 * @param password 明文密码
 * @param ssoArgs 当前正在使用单点登录方案的配置参数信息
 * @returns
 * 	1. 返回`UserInfo`对象,那么系统将直接使用返回的用户信息登录初始化权限信息。   
 * 	2. 如果抛出异常,那么将提示异常信息给用户。 
 * 	3. 如果没有返回值,那么将进行系统默认的登录验证。
 */
// function onPasswordSignin(request: HttpServletRequest, response: HttpServletResponse, userId: string, password: string, ssoArgs: SSOArgs): UserInfo | void {
// }

/**
 * 当一个用户登录成功后,执行一次此函数。
 * 
 * 1. 可以通过此钩子函数修改当前用户的登录信息(包括权限列表等)。
 * 2. 此函数不是必须实现的
 *
 * @param request
 * @param response
 * @param user 当前用户
 * @param ssoArgs 当前正在使用单点登录方案的配置参数信息
 * @throws 抛出异常,那么将导致用户登录失败,可用于进行额外的登录验证判断。
 */
// function onDidSignin(request: HttpServletRequest, response: HttpServletResponse, user: CurrentUser, ssoArgs: SSOArgs): void {
// }

/**
 * 判断访问系统所使用的app是不是支持单点登录方案进行授权登录
 *
 * 比如当前单点登录方案为支持微信授权的登录方案,系统可以根据当前判断函数来确定在微信浏览器中可以跳
 * 转微信登录用户授权,在钉钉浏览器中不能支持跳转获取,借助这个功能系统可以提供:
 *
 * 1. 在登录界面上展示使用`微信授权`登录
 * 2. 在默认登录方式为当前单点登录方案时,微信浏览器中访问到系统登录的页面就会尝试跳转获取微信用户授
 *    权
 *
 * 实现注意:
 *
 * 1. 如果不实现当前方法,默认返回`true`
 * 2. 可能存在使用的微信在访问到第三方系统,然后第三方系统使用iframe内嵌本系统的情况,请根据需求调整
 *    当前方法的实现
 *
 * @param userAgent 服务器收到请求的请求头 User-Agent
 * @param request web请求对象
 * @param ssoArgs 当前正在使用单点登录方案的配置参数信息
 * @returns true代表当前访问量浏览器支持
 */
// function onSSOCheckAppSupported(userAgent: string, request: HttpServletRequest, ssoArgs: SSOArgs): boolean {
// 	return true;
// }

/**
 * 用于在使用当前单点登录方案进行登录操作时,构建一个访问第三方认证授权服务系统获取授权用户信息的url
 *
 * 系统默认设置使用当前单点登录方案进行登录或者用户选择当前登录方案进行登录时,调用本方法获取重定向
 * 的url
 *
 * 对于类`OAuth2`协议、类`CAS`协议相关的单点登录方案,都有跳转到第三方认证授权服务中心页面获取授权信
 * 息的步骤
 *
 * 请根据第三方认证服务系统的要求按需构建这个url,如果在第三方系统获取到授权后,带上授权信息回跳到本
 * 系统,这个回跳地址可以使用当前函数中的参数`redirect_uri`,这个参数可以标识了使用当前单点登录方案
 * 获取的授权,可以帮助定位到当前单点登录方案解析url上授权信息为确切用户信息,参数`redirect_uri`的结
 * 构为
 * ```
 * {domainOrIp}[:port]/[{contenxtPath}]/api/auth/redirect?ssoid={当前单点登录方案对应在单点登录系统设置中id}[&redirect_url=${登录成功后跳转的系统页面地址}]
 * ```
 *
 * 返回值期望:
 *
 * 1. 返回`false`或者没有null,表示当前访问系统的app,无法使用当前定制的单点登录方案进行登录
 * 2. 返回`true`,表示当前单点登录方案支持这个app登录,且脚本函数以及处理页面重定向,系统在调用本方
 *    法后不需要在作重定向操作
 * 3. 返回一个URL地址,应该是一个可以用于重定向的完整的URL地址。请确保URL地址中的字符都有正确的编
 *    码,一些特殊字符(比如空格,[,]、中文等)可能引起服务器抛出不必要的错误,比如:
 *    1. QQ手机扫码登录斗
 *       鱼:<https://xui.ptlogin2.qq.com/cgi-bin/xlogin?appid=716027609&pt_3rd_aid=101047385&daid=383&pt_skey_valid=0&style=35&s_url=http%3A%2F%2Fconnect.qq.com&refer_cgi=authorize&which=&client_id=101047385&redirect_uri=https%3A%2F%2Fpassport.douyu.com%2Findex%2Funion%2Findex%2Fqq&state=6fc429913eed76f55dc59714fd6d5d46&scope=&response_type=code&approval_prompt=force&chInfo=ch_share__chsub_CopyLink>
 *    2. 迅雷PC页面请求QQ授
 *       权:<https://graph.qq.com/oauth2.0/show?which=Login&display=pc&client_id=101164677&redirect_uri=https%3A%2F%2Flogin-i-ssl.xunlei.com%2Fthirdlogin%3FOSVersion%3DMacIntel%26appName%3DWEB-vip.xunlei.com%26appid%3D101%26clientVersion%3DNONE%26deviceModel%3Dchrome%252F87.0.4280.88%26deviceName%3DPC-Chrome%26devicesign%3Dwdi10.8c2ba76bd4d351a44e0fb0275e1eaa89dac5aba824761b9e4e8dedf8fa27fe68%26format%3Dcookie%26netWorkType%3DNONE%26platformVersion%3D1%26protocolVersion%3D300%26providerName%3DNONE%26redirectUrl%3Dhttps%253A%252F%252Fvip.xunlei.com%252F%26sdkVersion%3Dv4.5.11%26thirdAppid%3D%26thirdType%3Dqq&response_type=code>
 *    3. 获取微信授权
 *       <https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxa79ae207bd77d088&redirect_uri=http://example.com/api/auth/{id}/{wechat}/redirect&response_type=code&scope=snsapi_userinfo&state=1223#wechat_redirect>
 *
 * @see https://developers.weixin.qq.com/doc/service/guide/h5/auth.html
 *
 * @param request web请求对象
 * @param response web响应对象
 * @param redirect_uri 实现者应该使用此参数构建获取授权后回跳到本系统的url,这样当收到授权验
 *        证信息信息回调请求或者根据code换取令牌的请求时系统才会自动定位到`onSSOCheckTicket`函数。
 * @param ssoArgs 当前正在使用单点登录方案的配置参数信息
 * @returns 详见返回值说明
 */
// function onSSOGetAuthRedirectURL(request: HttpServletRequest, response: HttpServletResponse, redirect_uri: string, ssoArgs: SSOArgs): boolean | string {
// 	return null;
// }

/**
 * 在获取到第三方认证授权服务获取到授权认证信息后,根据授权信息解析出对应的用户信息
 *
 * 1. OAuth2协议中第三方系统授权后会在回跳url中带上code参数,CAS协议会在回跳地址带上ticket参数,系统
 *    实现本方法是可以使用code参数(ticket参数)向第三方系统换取到用户信息
 * 2. 系统提供的微信小程序授权登录场景中,当用户授权小程序页面后,小程序的原生界面会通过ajax请求把授
 *    权信息(加密的)发送给系统进行解密获取到授权的用户信息
 *
 * 实现注意事项:
 *
 * 1. 当实现了`onSSOGetAuthRedirectURL`函数时,通常也需要实现此函数。
 * 2. 此函数只需要验证票据的合法性获得用户授权的用户信息,系统会自动根据系统设置中的设置决定是否根据
 *    返回的用户信息进行登录
 * 3. 如果验证失败,那么应该抛出异常。
 *
 * 返回值说明:
 *
 * 1. 如果返回的用户信息中有明确的用户目录信息(`userDirectory`,`sys`表示内部用户,`external`表示外
 *    部用户),那么系统将自动根据用户的目录去决定去内部用户库还是外部用户库去查找用户信息
 * 2. 如果在用户库中没有找到与返回用户信息匹配的用户,根据系统设置中配置单点登录的策略可能需要根据返
 *    回用户信息创建一个用户,如果用户表中有与返回用户信息同名的物理表字段,那么在新增用户时会将信息
 *    写入这些字段,用户表没有但是返回用户信息存在的字段,会被忽略掉,
 *
 * @param request web请求对象,可通过`request.getParameter()`获取url上的参数,通过
 *        `request.getRequestBody()`函数获取POST方式发送的参数,如果请求的`Contect-Type`是
 *        `application/json` 那么`request.getRequestBody()`返回json对象。
 * @param response web响应对象
 * @param ssoArgs 当前正在使用单点登录方案的配置参数信息
 * @returns 返回 验证票据所返回的用户信息
 * @throws 如果验证失败,那么应该抛出异常。
 */
// function onSSOCheckTicket(request: HttpServletRequest, response: HttpServletResponse, ssoArgs: SSOArgs): UserInfo {
// 	return null;
// }

/**
 * 在本系统注销登录后,通知第三方系统也注销登录
 *
 * 系统使用当前单点登录方案作为默认登录方式情况下,在本系统注销登录后,用户在第三方系统还是处于登录
 * 状态,此函数用于构建一个在第三方系统进行注销登录的url
 *
 * 如果在第三方系统注销登录后还要回跳到本系统,请使用参数`redirect_uri`,这个会帮助跳转到注销登录后
 * 需要跳转到的页面,这个参数的结构为:
 *
 * ```
 * {domainOrIp}[:port]/[{contenxtPath}]/api/auth/signoutRedirect?ssoid={当前单点登录方案对应在单点登录系统设置中id}[&redirect_url=${登录成功后跳转的系统页面地址}]
 * ```
 *
 * @param request web请求对象
 * @param response web响应对象
 * @param redirect_uri 为通知第三方注销登录后,期望回跳到本系统的url地址
 * @param ssoArgs 当前正在使用单点登录方案的配置参数信息
 * @returns 请确保返回的url中使用的字符都有正确的编码,一些特殊字符(比如空格,[,]等)可能引起服务器
 *        抛出不必要的错误
 */
// function onSSOGetLogoutRedirectURL(request: HttpServletRequest, response:HttpServletResponse, redirect_uri: string, ssoArgs: SSOArgs): string {
// 	return null;
// }

/**
 * 从第三方平台(如企业微信、钉钉)获取企业所有成员的基本信息。
 * 
 * 1. 使用场景:系统绑定企业平台账号后,通过比较系统用户库与企业平台账号中的所有成员信息,更新系统用户库
 * 2. 此函数不是必须实现的,如果实现了,表明当前单点登录方案支持批量获取用户信息
 *
 * @param ssoArgs 当前正在使用单点登录方案的配置参数信息
 * @return 一次性返回所有用户的信息
 * 	1. 返回的用户信息的属性如果在用户表中有同名字段(物理字段名同名,忽略大小写),那么在新增用户时将自动写入这些字段
 * 	2. 为了以后支持多个公众号,系统会默认先找`ssoid+下划线+属性名`的字段是否存在,如果存在,优先用这个。
 */
// function fetchUsers(ssoArgs: SSOArgs): Array<UserInfo> {
// 	return null;
// }

/**
 * 从第三方平台(如企业微信、钉钉)获取系统所绑定企业所有部门的基本信息。
 *
 * 1. 使用场景:系统绑定企业平台账号后,同步系统部门信息与第三方企业平台上的企业部门信息数据
 * 2. 此函数不是必须实现的,如果实现了,表明当前单点登录方案支持批量获取部门信息
 *
 * @param ssoArgs 当前正在使用单点登录方案的配置参数信息
 * @return 一次性返回所有部门的信息
 * 	1. 返回的部门信息的属性如果在部门表中有同名字段(物理字段名同名,忽略大小写),那么在新增部门时将自动写入这些字段
 * 	2. 为了以后支持多个公众号,系统会默认先找`ssoid+下划线+属性名`的字段是否存在,如果存在,优先用这个。
 */
// function fetchtDepartments(ssoArgs: SSOArgs): Array<DeptInfo> {
// 	return null;
// }

/**
 * 从第三方平台(如钉钉)获得所有的用户组信息。
 * 
 * 1. 使用场景:系统绑定企业平台账号后,同步系统用户组信息与第三方企业平台上的用户组数据
 * 2. 此函数不是必须实现的,如果实现了,表明当前单点登录方案支持批量获取用户组信息
 *
 * @param ssoArgs 当前正在使用单点登录方案的配置参数信息
 * @return 一次性返回所有用户组的信息
 * 	1. 返回的用户组信息的属性如果在用户组表中有同名字段(物理字段名同名,忽略大小写),那么在新增部门时将自动写入这些字段
 * 	2. 为了以后支持多个公众号,系统会默认先找`ssoid+下划线+属性名`的字段是否存在,如果存在,优先用这个。
 */
// function fetchtUserGroups(ssoArgs: SSOArgs): Array<UserGroupInfo> {
// 	return null;
// }

/**
 * 用于在配置界面上测试用户的配置是否正确。
 * 
 * @param conf 用户在配置界面上所做的配置(可能还未保存)
 * @returns 如果测试成功,明确返回true,测试失败抛出异常
 */
// function testConf(conf: JSONObject): boolean {
// 	return null;
// }
是否有帮助?
0条评论
评论