外部系统接入SSO-OAuth2(6.3.x 之后版本支持)

一、概述

统一身份认证系统提供了单点登录功能。本文档详述了统一身份认证系统单点登录接口,介绍了应用系统对接统一身份认证系统的过程和方法,用以帮助开发人员将应用系统接入统一身份认证系统。

本文档支持的协议:OAuth2

二、统一认证集成规范

2.1 SSO登录场景

序号 场景 预期结果 确认
1 登录跳转,在未登录的条件下,直接访问业务系统 跳转到单点登录界面
2 登录跳转,在已登录的条件下,直接访问业务系统 直接进入业务系统
3 正常登录,先登录认证,然后访问业务系统 直接进入业务系统
4 正常登出,在单点登录系统登出 访问业务系统要重新登录
5 正常登出,在业务系统登出 单点登录系统同步登出

2.2 认证流程

OAuth2 登录和认证流程
外部系统接入SSO-OAuth2(6.3.x 之后版本支持)

  1. 后端验证 token 后,创建本地会话(如 JSESSIONID 或自定义 Cookie);
  2. 后续应用请求靠本地会话维持登录状态,不再依赖原始 token;
  3. 本地会话有过期时间(如 30 分钟无操作过期);

三、认证服务接口

3.1 SSO服务端认证

浏览器登录

分类 说明
请求地址(开发环境) http://${host}:${port}/pamirs/sso/auth?client_id={client_id}&redirect_uri={redirect_uri}&state={state}
请求地址(测试环境) http://${host}:${port}/pamirs/sso/auth?client_id={client_id}&redirect_uri={redirect_uri}&state={state}
请求方式 GET
请求参数 redirect_uri:应用系统回调地址
client_id:SSO 服务端颁发的应用 ID
state:可选但推荐,用于防止 CSRF 的随机字符串(UUID 随机码)
请求示例 http://${host}:${port}/pamirs/sso/auth?client_id=client123456&redirect_rui=http://app.example.com&state=abc123
响应说明 1. 已登录:重定向到 redirect 地址并携带授权码 code,如 http://app.example.com&state=abc123&code=xxx
2. 未登录:重定向到服务器 SSO 登录地址
备注 SSO 登录成功后,会携带授权码并重定向到 redirect 地址

3.2 验证授权码

分类 说明
请求地址(开发环境) http://${host}:${port}/pamirs/sso/oauth2/authorize
请求地址(测试环境) http://${host}:${port}/pamirs/sso/oauth2/authorize
请求方式 POST
请求 Body {"grant_type":"authorization_code","client_id":"xxxx","client_secret":"xxxxx","code":"xxxx"}
请求示例 http://${host}:${port}/pamirs/sso/oauth2/authorize
响应说明 {"code":"0","msg":"成功","data":{"access_token":"xxxxx","expires_in":7200,"refresh_token":"xxxxxx","refresh_token_expiresIn":604800}}
备注

3.2 根据token获取用户信息

分类 说明
请求地址(开发环境) http://${host}:${port}/pamirs/sso/oauth2/getUserInfo
请求地址(测试环境) http://${host}:${port}/pamirs/sso/oauth2/getUserInfo
请求方式 POST
请求头 Authorization: Bearer xxxxxxxx
请求 Body {"client_id":"xxxx"}
请求示例 http://${host}:${port}/pamirs/sso/oauth2/getUserInfo
响应 {"code":"0","msg":"成功","data":{"name":"xxxxx","email":"xxxxx","login":"xxxxxx","id":"xxxxxxx"}}
备注 成功返回用户信息;失败返回错误码:1

根据实际情况其他协议的验证接口

3.3 单点登出接口

分类 说明
请求地址(开发环境) http://${host}:${port}/pamirs/sso/oauth2/logout
请求地址(测试环境) http://${host}:${port}/pamirs/sso/oauth2/logout
请求方式 POST
请求头 Authorization: Bearer xxxxxxxx
请求 Body {"client_id":"xxxx"}
请求示例 http://${host}:${port}/pamirs/sso/oauth2/logout
响应头 HTTP/1.1 200;Content-Type: application/html;charset=UTF-8
响应体 登出成功页面
备注 应用系统向 SSO 系统发起登出请求,SSO 收到后会通知所有其它系统登出该用户

四、服务注册

  1. 添加以下依赖
        <!-- sso相关 -->
        <dependency>
            <groupId>pro.shushi.pamirs.core</groupId>
            <artifactId>pamirs-sso-api</artifactId>
        </dependency>
        <dependency>
            <groupId>pro.shushi.pamirs.core</groupId>
            <artifactId>pamirs-sso-common</artifactId>
        </dependency>
        <dependency>
            <groupId>pro.shushi.pamirs.core</groupId>
            <artifactId>pamirs-sso-server</artifactId>
        </dependency>
  2. 配置文件
    pamirs
      sso:
        enabled: true
        server:
          loginUrl: http://127.0.0.1:8091/login
          authType: OAUTH2

五、应用注册

  1. 在服务端登记接入的应用,颁发应用的client_Id和client_secret; 注意回调地址,必须带上Schema(http(s))
    外部系统接入SSO-OAuth2(6.3.x 之后版本支持)
    外部系统接入SSO-OAuth2(6.3.x 之后版本支持)

  2. 并确保部署的应用机器能访问到SSO的服务端:

五、客户端接入

5.1 方式一:引入SSO客户端

业务系统在接入SSO统一认证系统时,工程中引入SSO客户端jar包,实现少量业务逻辑就可以完成接入,过程如下:

  1. 引入客户端依赖
    <dependency>
    <groupId>pro.shushi.pamirs.core</groupId>
    <artifactId>pamirs-sso-client-starter</artifactId>
    </dependency>
  2. 应用配置修改
    修改你SpringBoot应用的application.yaml,增加客户端的相关配置项

    pamirs:
      sso:
        #服务端地址
        server-url: http(s)://ssoIp:port/pamirs/sso/auth
        #客户端ID
        client-id: 1003
        #客户端密钥
        client-secret: 23bfde398056736c6ead917c3e6a2a5d
        #客户端排除拦截urls,默认为空,支持AntPathMatcher匹配
        exclude-urls:
          - /sample/demo/page
          - /sample/demo/i18n
        #客户端拦截urls,默认通配所有路径,支持AntPathMatcher匹配
        url-patterns:
          - /sample/*

说明:

  • server-url为SSO服务端认证地址;
  • client-id和client-secret是步骤一在服务端登记时生成的应用标识和密钥,用于客户端向服务端发起accessToken请求做校验;

5.2 方式二:不引入客户端包

使用场景:不使用SSO客户端包或者不是java语言,可以参照上面的认证接口进行。

六、外部应用接入到Oinone应用中心

1、确保在SSO应用注册时,正确填写了 回调地址;
2、在应用中心创建应用,参考下面的截图;
外部系统接入SSO-OAuth2(6.3.x 之后版本支持)
3、配置应用的首页
外部系统接入SSO-OAuth2(6.3.x 之后版本支持)
给应用配置首页:
1、选择“绑定URL”
2、URL配置为:http(s)://ssoIp:port/pamirs/sso/apply/transfer?client_id=xxxxxxxxxxxxxxxxxxxxxx
client_id 为外部应用在SSO服务端登记后生成的唯一应用标识
开发环境:http://${host}:${port}/pamirs/sso/apply/transfer?client_id=xxxxxxxxxxxxxxxxxxxxxx
测试环境:http://${host}:${port}/pamirs/sso/apply/transfer?client_id=xxxxxxxxxxxxxxxxxxxxxx
线上环境:http://${host}:${port}/pamirs/sso/apply/transfer?client_id=xxxxxxxxxxxxxxxxxxxxxx
3、跳转类型选择“开单新窗口”;
4、配置完成保存后,刷新页面。就可以从配置中心跳转到目标页面的回调地址了。

Oinone社区 作者:yexiu原创文章,如若转载,请注明出处:https://doc.oinone.top/other/25443.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

(0)
yexiu的头像yexiu数式员工
上一篇 2025年12月24日 pm5:39
下一篇 2026年2月10日 pm3:38

相关推荐

  • 如何通过业务数据拿到工作流用户任务待办

    在模型里面建一个非存储字段,用来传输工作流用户任务待办ID。。 界面设计器把这个字段拖到列表页里,并在跳转动作上配置上下文参数,把任务待办id传到表单页里。 重写教师模型的queryPage,通过业务数据id查询出每条业务数据的工作流用户任务待办id返回给前端。 @Function.Advanced(displayName = "查询教师列表", type = FunctionTypeEnum.QUERY, category = FunctionCategoryEnum.QUERY_PAGE, managed = true) @Function(openLevel = {FunctionOpenEnum.LOCAL, FunctionOpenEnum.REMOTE, FunctionOpenEnum.API}) public Pagination<Teacher> queryPage(Pagination<Teacher> page, IWrapper<Teacher> queryWrapper) { Pagination<Teacher> teacherPagination = new Teacher().queryPage(page, queryWrapper); List<Teacher> content = teacherPagination.getContent(); if (CollectionUtils.isEmpty(content)) { return teacherPagination; } List<Long> teacherIds = content.stream().map(Teacher::getId).collect(Collectors.toList()); List<WorkflowUserTask> workflowUserTasks = Models.data().queryListByWrapper(Pops.<WorkflowUserTask>lambdaQuery() .from(WorkflowUserTask.MODEL_MODEL) .in(WorkflowUserTask::getNodeDataBizId, teacherIds) .orderByDesc(WorkflowUserTask::getCreateDate) ); if (CollectionUtils.isEmpty(workflowUserTasks)) { return teacherPagination; } Map<Long/*业务id*/, WorkflowUserTask> userTaskMap = workflowUserTasks.stream().collect(Collectors.toMap( WorkflowUserTask::getNodeDataBizId, a -> a, (old, n) -> old) ); for (Teacher teacher : content) { if (userTaskMap.containsKey(teacher.getId())) { teacher.setWorkflowUserTaskId(userTaskMap.get(teacher.getId()).getId()); } } return teacherPagination; } 查看效果,任务待办id成功传到表单里面。

    2025年1月10日
    2.2K00
  • 打印支持非存储数据导出

    介绍 平台提供的默认打印功能没有支持非存储数据的导出。我们可以自定义打印导出功能,以满足业务中个性化的需求。 实现思路 重写打印任务模型,添加业务数据字段 自定义打印动作,前端将导出数据放到业务数据字段中 使用导出数据扩展点机制修改导出数据 代码示例 继承平台的打印任务模型,加上需要业务数据字段,这个字段用于传输需要打印的表单数据,但是需要自定义打印的表单往往不止一个,所以需要定义为通用的Object字段。 @Model.model(TransientPdfPrintTask.MODEL_MODEL) @Model(displayName = "传输模型Pdf打印任务") public class TransientPdfPrintTask extends PdfPrintTask { public static final String MODEL_MODEL="demo.TransientPdfPrintTask"; @Field(displayName = "业务数据") private Object businessData; } 自定义打印动作 @Model.model(TransientPdfPrintTask.MODEL_MODEL) @Component public class TransientPdfPrintTaskAction extends AbstractPdfPrintTaskAction<TransientPdfPrintTask> { @Resource private PdfFileService pdfFileService; @Action(displayName = "打印", contextType = ActionContextTypeEnum.CONTEXT_FREE, bindingType = {ViewTypeEnum.TABLE}) @Override public TransientPdfPrintTask createPrintTask(TransientPdfPrintTask data) { return super.createPrintTask(data); } @Override protected void doExport(TransientPdfPrintTask exportTask, PdfDefinitionContext context) { pdfFileService.doExportSync(exportTask, context); } @Function.Advanced(type = FunctionTypeEnum.QUERY) @Function(openLevel = FunctionOpenEnum.API) public TransientPdfPrintTask construct(TransientPdfPrintTask data) { String model = FetchUtil.fetchVariables(PdfConstants.MODEL); data.construct(); data.setModel(model); return data; } } 本篇文章只介绍同步打印,如果异步需要修改doExport方法。 编写导出的数据处理逻辑 @Ext(PdfPrintTask.class) public class PrintExportExt extends AbstractPdfExportFetchDataExtPointImpl implements PdfExportFetchDataExtPoint { // 这里使用扩展点表达式匹配需要打印的非存储模型,只有表达式为true才会走这段逻辑。 @Override @ExtPoint.Implement(expression = "context.model==\"" + ProductPricingClientTransient.MODEL_MODEL + "\"") public List<Object> fetchExportData(PdfPrintTask exportTask, PdfDefinitionContext context) { List<Object> result = new ArrayList<>(); List<Object> dataList = new ArrayList<>(); TransientPdfPrintTask transientPdfPrintTask = new TransientPdfPrintTask(); transientPdfPrintTask.set_d(exportTask.get_d()); dataList.add(transientPdfPrintTask.getBusinessData()); result.add(dataList); return result; } } 前端自定义打印按钮,将数据提交给业务数据字段,并使用同步导出打印。

    2025年10月13日
    70600
  • 如何设计公用跳转动作(类似导入导出动作)

    背景 设计一个公共动作,在界面设计器可以拖到页面里,点击之后跳转指定页面。就像导入导出一样。 实现思路 元数据计算时,初始化跳转动作,为本模块以及依赖于本模块的所有模块生成该跳转动作。实现所有模型都有该跳转动作的元数据。 代码示例: package pro.shushi.pamirs.top.core.init; import com.google.common.collect.Lists; import org.apache.commons.collections4.MapUtils; import org.springframework.stereotype.Component; import pro.shushi.pamirs.boot.base.enmu.ActionTargetEnum; import pro.shushi.pamirs.boot.base.enmu.ActionTypeEnum; import pro.shushi.pamirs.boot.base.model.ViewAction; import pro.shushi.pamirs.boot.common.api.command.AppLifecycleCommand; import pro.shushi.pamirs.boot.common.extend.MetaDataEditor; import pro.shushi.pamirs.core.common.InitializationUtil; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.meta.api.dto.meta.Meta; import pro.shushi.pamirs.meta.api.dto.meta.MetaData; import pro.shushi.pamirs.meta.domain.model.ModelDefinition; import pro.shushi.pamirs.meta.enmu.ActionContextTypeEnum; import pro.shushi.pamirs.meta.enmu.SystemSourceEnum; import pro.shushi.pamirs.meta.enmu.ViewTypeEnum; import pro.shushi.pamirs.top.api.TopModule; import pro.shushi.pamirs.top.api.model.Teacher; import java.util.*; import java.util.stream.Collectors; @Slf4j @Component public class DemoModuleAppInstall implements MetaDataEditor { @Override public void edit(AppLifecycleCommand command, Map<String, Meta> metaMap) { InitializationUtil util = InitializationUtil.get(metaMap, TopModule.MODULE_MODULE, TopModule.MODULE_NAME); if (util == null) { return; } if (MapUtils.isEmpty(metaMap)) { return; } Set<String> dependencyFileModels = metaMap.values().stream() .filter(v -> v.getData().containsKey(TopModule.MODULE_MODULE)) .map(Meta::getModule) .collect(Collectors.toSet()); for (String module : metaMap.keySet()) { if (!dependencyFileModels.contains(module)) { // 不依赖本模块,不生成跳转动作 continue; } Meta meta = metaMap.get(module); MetaData metaData = meta.getCurrentModuleData(); List<ModelDefinition> modelList = metaData.getDataList(ModelDefinition.MODEL_MODEL); for (ModelDefinition data : modelList) { makeDefaultModelViewAction(meta, data, dependencyFileModels); } } } // 跳转的xml模版 name public static final String DEFAULT_VIEW_NAME = "fixed_teacher_table"; private void makeDefaultModelViewAction(Meta meta, ModelDefinition data, Set<String> dependencyFileModels) { if (!dependencyFileModels.contains(data.getModule())) { // 当前模块使用了其他模块的模型,对方模块未依赖本模块,不生成跳转动作 return; } Map<String, Object> context = new HashMap<>(); context.put("model", "'" + data.getModel() + "'"); // 创建 跳转表格页面 viewAction,根据实际需求更改 makeDefaultViewAction(meta, data, "teacherListDialog", "教师表格", null, ActionContextTypeEnum.CONTEXT_FREE, ViewTypeEnum.TABLE, 99, Teacher.MODEL_MODEL, DEFAULT_VIEW_NAME,…

    2025年10月22日
    45100
  • 如何使用源码的方式配置表达式

    自定义占位符定义表达式 数据权限定义表达式: ${thisTeacherName} 界面设计器查询条件定义:$#{thisTeacherName}需要加上#号 以上配置都可以通过查看queryWrapper里面的originRsql查看占位符是否被正确替换。 显隐、过滤条件表达式定义 显隐、过滤都可以加载字段注解里以及xml定义里 显隐: invisible="$!{activeRecord.deadline}" / invisible = true @Field.String @Field(displayName = "视图/页面", invisible = true) private String viewName; 过滤。 domain = " code == ${activeRecord.id} " / domain = "code == '111' " @Field.one2many @Field(displayName = "子订单列表", summary = "子订单列表") @Field.Relation(relationFields = {"code"}, referenceFields = {"code"}, domain = "code != '1234'") private List<ChildOrder> orderList; 更多获取视图数据的写法参考文章上下文在字段和动作中的应用 rsql表达式定义 参考:oinone的rsql与传统sql语法对照表

    2025年3月13日
    88300
  • 开放应用中的ip黑白名单

    IP白名单配置 入口:集成应用>开放管理>应用>授权调整>IP白名单配置 IP白名单取请求头中的X-Forwarded-For属性的最后一个值,X-Forwarded-For以英文,分割ip地址。 X-Forwarded-For: clientIP, proxy1IP, proxy2IP, …, proxyNIP 根据 RFC 7239 标准所述,X-Forwarded-For含义如下: clientIP:最左边的 IP,表示最初发起请求的客户端 IP(即真实用户 IP)。 proxyXIP:从左往右依次为中间各级代理服务器的 IP。 最右边的 IP:表示离当前 Web 服务器最近的一层代理服务器(IP白名单拦截的此IP)。 Nginx配置示例 以Nginx为例,为确保X-Forwarded-For拿到的是真实的IP地址,需要增加配置。 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; IP黑名单配置 入口:集成应用>开放管理>应用>黑名单 取值来源:从请求头 X-Real-IP 中提取客户端 IP。 验证逻辑: 若 X-Real-IP 不存在,直接拦截并返回异常提示:未获取到真实IP地址。 检查提取的 IP 是否在阻止列表中。 相关文章 IP黑白名单实现拦截三方用户

    2025年5月15日
    56800

Leave a Reply

登录后才能评论