一、功能背景
在实际项目中,SuperMap iServer 通常需与已有的统一用户认证平台进行集成,以实现 SSO 单点登录功能。对于常见的标准 SSO 单点登录系统,iServer 提供了 CAS、Keycloak、第三方(如 QQ 登录或微博登录)等登录模式的配置入口。然而,对于非标准的用户自定义登录系统,只能通过扩展开发来实现,其实现通常较为复杂。为支持快速、高效地与项目中已有的用户自定义登录系统进行对接,iServer 提供了一种基于配置 groovy 代码的方式支持非标准 SSO 登录认证功能。
二、配置步骤
集成过程的核心是通过调用用户自定义登录系统的用户信息 API 以获取用户身份信息,并基于该身份进行 SSO 单点登录。由于不同自定义登录系统中用户信息接口存在较大差异,iServer 提供了基于 groovy 代码的自定义配置机制,支持适配各类用户信息接口。为支持非标准 SSO 登录认证功能,您需要编写 groovy、properties 等自定义配置文件,并在 iserver-system.xml 中开启该功能。
2.1 自定义配置概述
您需要在目录【iServer 产品安装包】/webapps/iserver/WEB-INF/config/ 下创建 security-integration 文件夹,并创建如下文件:
| 文件全称 | 描述 |
| security-integration-sso.properties | 配置文件。用于开启非标准 SSO 登录认证功能,以及配置用户自定义登录系统中用户信息接口的访问 URL、调用参数、返回内容等关键信息 |
| sso-integration.groovy | SSO 单点登录需要的 Groovy 脚本。使用函数定义 SSO 单点登录全流程的全部相关接口,包括请求方法、请求参数、请求头、响应结构等信息 |
| httpclientbuilder.groovy | 配置 httpclient 的 Groovy 脚本。支持通过函数定义代理拦截请求、忽略 https 证书验证等特殊配置。一般情况下不需要创建该文件。 |
2.1.1 security-integration-sso.properties 配置
properties 文件用于开启非标准 SSO 登录认证功能,并配置用户自定义登录系统中用户信息接口的访问 URL、调用参数、返回内容等关键信息,使服务器能够据此进行请求。需要设置的开关及接口信息如下,请按需在 properties 文件中填写相关代码:
- security-integration.sso.enable=true(或false)。当设置为 true 时表示开启非标准 SSO 登录认证功能,设置为 false 时关闭
- security-integration.sso.defaultRedirectUrl=/admin-ui/home。重定向到 iServer 服务管理界面。该配置项直接填写即可,无需修改
- security-integration.sso.login.url-pattern=跳转到的第三方 SSO 登录的 URL 模板。如:http://ip:port/login/oauth/authorize?client_id=38a8c4c4722c2fbae9d5&response_type=code&scope=openid%20profile%20email&redirect_uri={callback}&state={state}
- security-integration.sso.userinfoapi.url=用户信息接口的请求 URL。如http://ip:port/scp-account/rest/v1/users/current-user
- security-integration.sso.userinfoapi.required=Groovy 脚本中 createUserInfoRequest 函数所需的请求参数。若存在多个时,由“,”分隔。
- security-integration.sso.userinfoapi.response-contenttype=json。用户信息接口的响应格式
此外,该文件还需对 Groovy 脚本将调用的不同变量,即 security-integration.sso.login.url-pattern 中定义登录跳转模板中的 URL 请求参数及 security-integration.sso.userinfoapi.required 中的参数,预定义其值的获取方式。支持的获取方式包括登录跳转 URL 请求参数、HTTP 请求、配置项三类,不同类型获取方式可定义的相关属性如下(其中 X 为自定义变量的名称,可按实际情况设置):
- 当变量值的获取方式为登录跳转 URL 请求参数时,支持定义其请求参数名,可在 properties 文件中填写如下两行代码:
- security-integration.sso.define.X.from=request
- security-integration.sso.define.X.request.querykey=指定请求参数名,如:code。
- 当内置函数 createUserInfoRequest、parseUserInfo 不足以支撑完整的第三方 SSO 登录认证功能时,可自定义请求接口。当变量值的获取方式为新增的 HTTP 请求时,支持定义接口的 URL、必选请求参数、Groovy 脚本中接口对应的函数名等相关信息,可在 properties 文件中按需填写如下代码:
- security-integration.sso.define.X.from=http
- security-integration.sso.define.X.http.url=接口的请求 URL。如:http://172.16.120.205:8000/api/login/oauth/access_token
- (可选)security-integration.sso.define.X.http.function=Groovy 脚本中自定义函数的函数名。默认名称为 create{X}Request
- (可选)security-integration.sso.define.X.http.required=对该接口请求时所需的必选参数,如:code。若存在多个时,由“,”分隔
- (可选)security-integration.sso.define.X.http.responsejsonpath=对该接口请求时能够获取变量值的 json 字段,如:access_token
- (可选)security-integration.sso.define.X.http.responsefunction=对该接口请求时能够获取变量值的 Groovy 脚本函数名
- 当变量值的获取方式为配置项时,支持定义其具体的参数值,可在 properties 文件中填写如下两行代码:
- security-integration.sso.define.X.from=config
- security-integration.sso.define.X.config.value=具体参数值。
提示:本文件中代码填写顺序不影响实际执行顺序。
2.1.2 sso-integration.groovy 配置
Groovy 脚本用于定义支撑完整的第三方 SSO 登录认证功能所需的函数,这些函数描述了所有相关接口的请求和响应方法,包括必选函数 createUserInfoRequest、parseUserInfo,以及其他需要的函数(由 security-integration-sso.properties 配置文件中 security-integration.sso.define.X.http.function 等属性定义)。示例如下:
def createUserInfoRequest(required) {
return [method:"POST", query:["access-token":required.token, code:required.code], headers:[Accept:"application/json"]]
}
def parseUserInfo(Response) {
def result = [sub:Response.id]
return result;
}
其中,
- createUserInfoRequest 用于构建向用户信息接口发送的请求,需要定义 HTTP 请求方法、请求参数、请求头等信息:
- 函数的变量名:当代码中需要引用变量时,可写为 {函数变量名}.{变量}
- method 字段:描述向该接口请求时使用的 HTTP 方法
- query 字段:描述向该接口请求时使用的请求参数,通常可引用 properties 文件中定义的变量
- headers 字段:描述向该接口请求时使用的请求头
- parseUserInfo 用于处理用户信息接口返回的 json 响应格式结果
- 函数的变量名:当代码中需要引用变量时,可写为 {函数变量名}.{变量}
- result:描述向该接口请求时返回的响应结果中需要保留的信息字段,形式为多对映射,每对由“,”分隔。如:[openId:jsonResponse.id,nickname:jsonResponse.nickname]。其他支持的内置字段如下:
- name:用户名称,string 类型,默认为 anonym
- password:用户密码,string 类型
- passwordLastModified:用户最后修改日期,Date 类型
- description:用户描述,string 类型
- isLocked:该用户是否被锁定,boolean 类型,默认为 false
- roles:用户关联的角色,string[] 类型
- userGroups:用户关联的用户组,string[] 类型
- ownRoles: 用户自已的角色,不包括所属用户组关联的角色,string[] 类型
- expirationTime:过期时间,Long 类型
2.1.3 httpclientbuilder.groovy 配置
该文件用于定义 configure 函数,对 HttpClientBuilder 进行自定义配置,如设置代理、超时时间、添加证书等。
- HttpClientBuilder 的默认内容如下:
def configure(builder) {
return builder;
}
- 若在 HttpClientBuilder 中设置代理,示例如下
import org.apache.http.HttpHost
def configure(builder) {
return builder.setProxy(new HttpHost("127.0.0.1", 8888));
}
2.2 iserver-system.xml 配置
为使 iServer 登录界面能够应用 SSO 账号登录模式,还需在系统配置中开启该功能。修改【iServer产品安装包】/webapps/iserver/WEB-INF/iserver-system.xml中 <SSOConfig> 节点下的配置即可:
- <enable>:用于设置非标准 SSO 登录认证功能是否启用。默认为 false,设置为 true 时为开启。
三、配置举例
以下以某用户信息 API 为例,说明 SSO 集成配置过程。
用户信息 API 接口说明:
#接口说明
本接口用于获取当前操作人的用户信息,需要使用 token 访问本接口。具体如下:
- 通过 URL 传递 access_token,如:http://127.0.0.1/test?access_token=xxxxxxxx
- token 的获取方式请参考 http://zhi.gtmap.cn/share/9f317f41-a190-4e0e-b513-9d3f58294a69
#请求说明
方式: GET
地址: http://ip:port/scp-account/rest/v1/users/current-user
Feign: UserManagerClient#getCurrentUser
版本:1.0.5-SNAPSHOT
#响应结果
{
"id":"b1e5ecd0c74b4f97ba0f62efc9f22a8b",
"username":"wqy",
"alias":"wqy",
"admin":1,
"enabled":1,
"locked":0,
"onLeave":0,
"registerStatus":1,
"signId":"ff808081840e717e01849dff44580042",
"createAt":null,
"updateAt":null,
"expired":"2048-08-31 00:00:00",
"password":null,
"mobile":"15261807626",
"email":null,
"tel":null,
"birthday":null,
"gender":"男",
"title":null,
"resume":null,
"address":null,
"sequenceNumber":null,
"idCard":null,
"idCardType":"identity",
"idCardFrontalId":null,
"idCardReverseId":null,
"userType":0,
"jobNumber":null,
"partyMember":"N",
"pictureId":null,
"caNumber":null,
"modifyPwd":1,
"graduateSchool":null,
"shortNumber":null,
"fax":null,
"workingYears":null,
"entryDate":null,
"enterpriseDTO":null,
"roleRecordList":[
{
"id":"4028882a644acbd401644acc7cfa0004",
"name":"admin",
"alias":"管理员",
"level":1,
"enabled":1,
"createAt":null,
"updateAt":null,
"parentRecords":[
{
"id":"8aaa077165aea8ed0165aeccfc7d0002",
"name":"ptyg",
"alias":"员工",
"level":0,
"enabled":1,
"createAt":"2018-09-06 20:13:14",
"updateAt":"2022-07-21 13:53:30",
"parentRecords":null,
"excludeRecords":null,
"gradingRoleIds":null,
"gradingAuthoritys":null,
"orgRecords":null
}
],
"excludeRecords":null,
"gradingRoleIds":null,
"gradingAuthoritys":null,
"orgRecords":null
}
],
"orgRecordList":[
]
}
步骤一、编写 security-integration-sso.properties
1、配置用户自定义登录系统中用户信息接口的访问 URL、调用参数、返回内容等关键信息
#启用非标准 SSO 登录认证功能
security-integration.sso.enable=true
#重定向到 iServer 服务管理界面
security-integration.sso.defaultRedirectUrl=/admin-ui/home
#根据接口说明设置跳转到的第三方 SSO 登录的 URL 模板
security-integration.sso.login.url-pattern=http://192.168.41.71/scp-account/oauth/authorize?client_id=iserver&redirect_uri={callback}&response_type=code&state={state}
#根据接口说明配置用户信息接口的请求 URL
security-integration.sso.userinfoapi.url=http://192.168.41.71/scp-account/rest/v1/users/current-user
#根据接口说明定义访问接口时需要的请求参数,即 accesstoken
security-integration.sso.userinfoapi.required=accesstoken
#设置用户信息接口的响应格式,即 json
security-integration.sso.userinfoapi.response-contenttype=json
2、根据接口说明补充其他必要变量
1)对上一步骤中所需的 accesstoken 参数的获取方式进行定义,其由 HTTP 请求获取
#定义 accesstoken 参数来源,即 HTTP 请求
security-integration.sso.define.accesstoken.from=http
#获取 accesstoken 值的接口对应的 HTTP 请求地址
security-integration.sso.define.accesstoken.http.url=http://192.168.41.71/scp-account/oauth/token
#对该接口请求时所需的必选参数
security-integration.sso.define.accesstoken.http.required=code,clientid,clientsecret
#对该接口请求时能够获取 accesstoken 参数值的响应字段
security-integration.sso.define.accesstoken.http.responsejsonpath=.access_token
2)对上一步骤中需要使用的必选请求参数(code、clientid、clientsecret)进行定义
#定义配置参数 clientid
security-integration.sso.define.clientid.from=config
security-integration.sso.define.clientid.config.value=iserver
#定义配置参数 clientsecret
security-integration.sso.define.clientsecret.from=config
security-integration.sso.define.clientsecret.value=lFEtXGu77f89p7xmdKtj
#定义请求参数 code,来源为请求参数
security-integration.sso.define.code.from=request
#进行请求时所需的请求参数名
security-integration.sso.define.code.request.querykey=code
步骤二、编写 Groovy 处理脚本
定义支撑完整的第三方 SSO 登录认证功能所需的函数,包括必选函数 createUserInfoRequest、parseUserInfo,以及用于获取 accesstoken 参数值的函数 createaccesstokenRequest
def createaccesstokenRequest(required) {
return [method:"POST", body:[code:required.code,"grant_type":"authorization_code","redirect_uri":"http://172.16.15.206:8090/iserver/sso-callback","client_id":required.clientid,"client_secret":required.clientsecret],headers:["Content-Type":"application/x-www-form-urlencoded"]]
}
def createUserInfoRequest(required) {
return [method:"GET", query:[access_token:required.accesstoken]]
}
def parseUserInfo(jsonResponse) {
def result = [openId:jsonResponse.id, name:jsonResponse.username,nickName:jsonResponse.alias,roles:['ADMIN']]
return result;
}
步骤三、在 iserver-system.xml 中开启非标准 SSO 登录认证
修改【iServer产品安装包】/webapps/iserver/WEB-INF/iserver-system.xml 中 <SSOConfig> 节点下的配置参数 <enable> 为 true。
到此,所有配置完成。重启 iServer,点击登录进入登录界面,点击底部 SSO 账号登录即可。