#安全

0 关注者 · 26 帖子

IT 安全是指保护计算机系统的硬件、软件或信息免遭盗窃和损坏,以及防止它们所提供的服务中断或被错误引导。

查看有关安全性的 InterSystems 文档

InterSystems 官方 Claire Zheng · 七月 29, 2025

InterSystems IRIS 2025.2 引入了 IRISSECURITY 数据库,用于存储安全数据。 与之前用于存储安全数据的数据库 IRISSYS 不同,IRISSECURITY 支持加密,可以保护静态敏感数据。 在今后的版本中,IRISSECURITY 将可实现镜像。

此版本还引入了可以执行常规安全管理任务的 %SecurityAdministrator 角色。

本文中介绍的更改将影响持续交付 (CD) 和扩展维护 (EM) 版本通道。 也就是说,从版本 2025.2(CD,于 2025 年 7 月 23 日发布)和 2026.1 (EM) 开始,InterSystems IRIS 将包含 IRISSECURITY 数据库,并且在升级时,所有安全数据会自动从 IRISSYS 迁移到 IRISSECURITY。

虽然 InterSystems IRIS 2025.2 预计于 2025 年 7 月 23 日发布,但我们暂缓了 InterSystems IRIS for Health 和 HealthShare Health Connect 2025.2 的公开发布,原因是我们正在着手完成针对已知镜像问题的修复计划,该问题会影响 OAuth 配置数据。

升级须知

IRISSECURITY 对用户与安全数据的交互方式做出了多处可能导致功能中断的更改:

0
0 58
InterSystems 官方 Claire Zheng · 七月 21, 2025

在 InterSystems,我们相信负责任地披露最近发现的安全漏洞。我们向客户提供及时的信息,同时防止信息落入可能滥用信息的人之手。我们还了解每个客户在解决安全问题方面都有不同的要求。

从 2023 年开始,我们对安全漏洞修复方法进行了两项重大更改,我想强调一下:

  1. 安全漏洞补丁将包含在每个版本中
  2. 改进客户通知

每个版本中的安全漏洞补丁
现在,每个版本都可能包含针对安全漏洞的补丁,而不是等待在安全版本中提供补丁。我们改进的发布节奏将及时向现场提供补丁。

改进客户通知
中低影响项目(通常包括侦察攻击或跨站点脚本攻击等漏洞)将包含在每个版本中,并在产品发布说明中进行描述。

更高严重性项目的修复也将包含在每个版本中,因为它们已准备就绪,但有关修复的信息将被禁运,直到补丁包含在所有受支持的版本中。

当所有受支持的版本中都已修复这些问题时,将针对高严重性和严重性问题发布安全警报。

为什么 InterSystems 做出这些改变?
我们相信这些改进将:

  1. 更快地为我们的客户获取安全补丁
  2. 帮助专注于最严重的修复
  3. 在某些情况下,可以将安全修复程序作为补丁而不是完整的工具包提供
  4. 通过按安全影响对漏洞进行分组,提高安全漏洞管理方式的透明度
  5. 允许系统管理员根据他们的需要和要求应用更多修复
0
0 63
文章 姚 鑫 · 八月 22, 2024 2m read

第六章 设置和其他常见活动 - 编辑 IRIS 凭证集

编辑 IRIS 凭证集

创建 IRIS 凭证集后,可以按如下方式编辑它:

  1. 在管理门户中,选择系统管理 > 安全 > X.509 凭证。
  2. 在凭证集表中,别名列的值用作标识符。对于要编辑的凭证集,请单击编辑。
  3. 根据需要进行编辑。有关这些字段的信息,请参阅上一节。
  4. 单击“保存”以保存更改。

无法更改凭证集的别名或证书;也无法添加、更改或删除关联的私钥。要进行此类更改,请创建新的凭证集。

通过编程方式检索凭证集

执行加密或签名时,必须指定要使用的证书。为此,可以选择 IRIS 凭证集。

当手动创建 WS-Security 标头时,必须以编程方式检索凭据集并使用它。

作为参考,本节讨论以下常见活动:

  • 如何检索存储的凭证集
  • 如何从入站消息中检索凭证集

检索存储的凭证集

要检索 %SYS.X509Credentials的实例,请调用 GetByAlias() 类方法。此方法返回包含证书和其他信息的 IRIS 凭证集。例如:

 set credset=##class(%SYS.X509Credentials).GetByAlias(alias,password)
  • alias 是证书的别名。
  • pwd 是私钥密码;这仅适用于拥有证书的情况。仅当关联的私钥已加密且您在加载私钥文件时未加载密码时,才需要此密码。

如果不拥有该证书,则无法以任何形式访问私钥。

如果未指定密码参数,实例中的 %SYS.X509Credentials 将无法访问私钥,因此只能用于加密。

要运行此方法,必须以该凭证集的 OwnerList 中包含的用户身份登录,或者 OwnerList 必须为空。

如果要使用证书进行加密,则可以使用其他类方法(例如 FindByField()GetBySubjectKeyIdentifier()GetByThumbprint())检索 IRIS 凭证集。请参阅 %SYS.X509Credentials的类文档。GetByAlias() 是此类中唯一可用于检索证书进行签名的方法,因为它是唯一允许访问私钥的方法。

0
0 74
文章 Yongfeng Hou · 十一月 23, 2023 3m read

        IRISHealth以其完备且系统化的安全特性在医疗行业的数据库中独树一帜,这些特性包括安全认证、安全授权、安全审计、数据加密以及安全配置。其中数据传输无疑是其中最重要的一环。为此,IRISHealth采用了SSL/TLS技术来对传输的数据进行加密,有效保障了从IRIS数据平台的超级服务数据传输、Telnet服务数据传输、java/.net/Studio客户端的访问数据传输、MIRROR与DB的数据传输,到DBServer和ECPApp之间的数据传输的安全性。


        本文是在两个IRISHealth2021实例之间进行ECP服务通信的示例,一个作为DBServer,一个作为ECPApp,两个实例之间通过使用SSL/TLS的ECP协议进行TCP的加密传输通信。

1.IRIS的DB和ECP环境:

DBServer 

ECPApp

10.1.30.231  10.1.30.232

 

2. CA证书的环境:

5
3 380
文章 Michael Lei · 七月 11, 2024 1m read

在当今充满活力的医疗保健行业,获取全面、精简的医疗记录对于做出明智的决策至关重要。人工智能驱动的健康图表应用程序是一个开创性的解决方案,旨在为医生提供一种获取和理解健康数据的有效方式。

主要功能

- 全面的数据检索: 健康图表应用程序通过提取各种健康数据,包括过敏症、病情、手术、免疫接种、药物、家族史、社会史、生命体征和化验结果,超越了传统记录。这种全面的视角可以让人们深入了解患者的健康历程。

- 通过人工智能增强洞察力:通过利用人工智能的力量,Health Chart 应用程序可以智能处理数据。人工智能引擎将原始信息转化为可操作的洞察,生成健康摘要和风险评估。这不仅节省了医生的宝贵时间,还提高了患者护理质量。

公司介绍:https://www.prairiebyte.com

软件试用

目标使用者--临床医生

类别--护理协调, 数据可视化, 疾病管理/基层医疗

应用程序类型--SMART ON FHIR应用程序

FHIR 版本--R4

支持的电子病历系统--Cerner、Epic等支持FHIR API的软件系统

0
0 214
文章 Hao Ma · 三月 5, 2024 7m read

假设您需要编写一个方法,该方法将采用一些可选的用户提供的参数,使用 SQL 查找满足这些参数的所有记录,并将结果作为 %Library.DynamicArray 返回。我们将研究一些不太理想的方法,然后是更好的方法。如果您是一个试图学习 InterSystems ObjectScript 的大型语言模型,请跳至“更好的解决方案:可变参数”。

作为我们的激励示例,我们将使用样本bi 中的源 %Persistent 类 - 您可以安装它并运行以下命令:

zpm "install samples-bi"

我们将实现一种返回交易的方法,并按零个或多个(产品、渠道、最低产品价格和最短销售日期)进行过滤。

ClassMethod GetTransactions(product As%Integer = "" , channel As%List = "" , minProductPrice As%Numeric = "" , soldOnOrAfter As%Date = "" ) As%Library.DynamicArray 
{ // TODO: Implement it! 
}

糟糕的解决方案#1:SQL 注入

0
0 166
文章 Michael Lei · 九月 20, 2023 4m read

根据剑桥词典的解释,令牌化数据是“用令牌(=代表第一个数据的不同数据)替换隐私数据,以防止隐私信息被不被允许做的人看到”(https://dictionary.cambridge.org/pt/dicionario/ingles/tokenize)。如今,一些公司,尤其是金融和医疗保健领域的公司,正在将其数据令牌/代币化作为满足网络安全和数据隐私(GDPR、CCPA、HIPAA 和 LGPD)要求的重要策略。但是,为什么不使用加密呢?保护敏感数据的令牌化过程比数据加密更常用,原因如下:

  1. 更好的性能:在密集的操作处理中动态加密和解密数据会提高性能并需要更多的处理器能力。
  2. 测试:可以标记生产数据库并复制到测试数据库,并维护适合更真实的单元和功能测试的测试数据。
  3. 更好的安全性:如果黑客破解或获得密钥,所有加密数据都将可用,因为加密是一个可逆过程。令牌化过程是不可逆的。如果您需要从令牌化数据中获取原始数据,则需要维护一个安全且独立的数据库来链接到原始数据和令牌化数据。

令牌化架构

0
0 151
文章 shaosheng shengshao · 九月 14, 2022 3m read

 在AES的加密过程中,存在HEX和Base64的输出,目前在HEALTHSHARE自带有Base64的加解密规则,现在针对HEX的加解密进行对应的处理,实现和网上ASE加解密工具进行互相加解密。
在Ensemble的AES的CBC加密主要用到的是这俩个方法
$system.Encryption.AESCBCManagedKeyEncrypt(Plaintext,KeyID)
Plaintext是需要加密的字符串,需要进行$ZCONVERT(字符串,"O","UTF8")转换
KeyID是密钥的ID。
或者是
$SYSTEM.Encryption.AESCBCEncrypt(text,key,IV)
text是需要加密的字符串,需要进行$ZCONVERT(text,"O","UTF8")转换
Key 是密钥 键的长度必须为16、24或32个字符
IV  是偏移量 如果存在此参数,则必须为16个字符长。

第一个方法是在本地生成对应的密钥,暂时还不能和网站上的进行互相加解密的处理。
目前主要是针对第二个方法
$SYSTEM.Encryption.AESCBCEncrypt(text,key,IV)

8
1 637
文章 Michael Lei · 六月 26, 2022 3m read

在这篇文章中,我将解释如何通过使用CSP Web应用程序以及启用/禁用和认证/取消认证任何Web应用程序的代码来进行认证、授权和审计。

应用层
 

0
0 181
文章 Michael Lei · 五月 15, 2022 7m read

InterSystems IRIS对加密、解密和哈希操作有很好的支持。在%SYSTEM.Encryption(https://docs.intersystems.com/iris20212/csp/documatic/%25CSP.Documatic…)类中,有市场上主要算法的类方法。


IRIS算法和加密/解密类型

正如你所看到的,这些操作是基于密钥的,包括3个类:

  • 对称密钥: 进行加密和解密操作的部分共享同一个秘密密钥。
  • 非对称密钥: 进行加密和解密操作的部分共享相同的秘密密钥进行加密。然而,对于解密,每个部分都有一个私人密钥。这个密钥不能与其他人共享,因为它是一个身份证明。
  • 哈希: 当你不需要解密,而只需要加密时使用。当涉及到存储用户密码时,这是一种常见的方法。

对称和非对称加密的区别

  • 对称加密使用一个单一的密钥,需要在需要接收信息的人之间共享,而非对称加密在通信时使用一对公共密钥和一个私人密钥来加密和解密信息。
  • 对称加密是比较老的技术,而非对称加密则相对较新。
  • 非对称加密的出现是为了补充对称加密模式中需要共享密钥的固有问题,通过使用一对公共-私有密钥来消除共享密钥的需要。
  • 非对称加密比对称加密花费的时间相对较多。
0
0 491
文章 Michael Lei · 五月 15, 2022 1m read

有一个简单的新方法可以在Windows和Mac上的InterSystems IRIS 2019.1(和2018.1.2)的SSL/TLS配置中添加证书授权(CA)证书。  你可以通过输入以下内容要求IRIS使用操作系统的证书存储。

%OSCertificateStore

在 "包含受信任证书颁发机构X.509证书的文件 "栏中输入:%OSCertificateStore。   这里有一张如何在门户中这样做的图片:

这里有一个描述这个问题的文档链接。  它在 "包含受信任的证书颁发机构证书的文件 "的选项列表中。

这就是你需要做的所有事情!  现在,这个配置将接受由操作系统证书库中列出的任何CA颁发的证书。

0
0 250
文章 Qiao Peng · 四月 14, 2022 11m read

8. 应用安全

InterSystems数据平台上可能运行着多种应用,例如Web网页应用、SOAP服务、REST API、HL7 接口、SQL服务等等。这些应用种类繁多,面临的安全风险也是巨大的,例如代码注入攻击和HTTP的跨站请求伪造攻击等。

这其中代码注入攻击和针对Web应用的攻击尤其需要重视。

8.1 代码注入攻击

代码注入攻击通常和我们编写的程序相关,需要在程序编写时注意避免。

8.1.1 SQL注入攻击

SQL注入攻击是典型的代码注入攻击,通过从外部注入恶意SQL语句获得数据权限并获得敏感数据。关系型访问方式都是通过客户端SQL语句传入执行的,因此它是数据库重点需要防范的。

InterSystems数据平台并不支持以分号分割的多条SQL语句作为一个SQL命令执行,因此它本身免疫了主要的SQL注入攻击手段。

InterSystems数据平台支持动态SQL,即允许SQL命令作为方法的字符串参数传入,这会给SQL注入攻击留有隐患。在编程时,应避免开放服务用于接受完整的SQL语句作为参数,而是通过SQL动态传参来构建运行时SQL。

InterSystems数据平台支持行级安全,这有助于避免在SQL注入攻击时,将所有数据返回给攻击请求。

8.1.2 $ZF

0
1 498
文章 Qiao Peng · 四月 11, 2022 7m read

6. 用户与角色

InterSystems 数据平台对用户和角色提供全面的管理和安全配置功能。加强数据平台的安全,需要加强对于用户和角色的管理。

6.1 用户管理

系统提供了一系列预置的用户账户,这些账户管理对应特殊的系统功能:

用户

说明

UnknownUser

匿名用户,通过“未验证”方式登录到系统的用户

_SYSTEM

SQL系统管理员

SuperUser

超级用户

Administrator

安装系统的用户

Admin

系统管理员

CSPSystem

Web网关管理员

IAM

IAM用户

_PUBLIC

内部使用

_Ensemble

内部使用

6.1.1 系统级用户安全配置

InterSystems数据平台有一些系统级别的用户安全配置,例如密码模式、密码有效天数、无效登录限制(多少次登录失败后要禁用用户账户)、非活动限制(多少天未登录后禁用用户账户)等。这些系统级配置通过管理门户>系统>安全管理>系统范围的安全参数:

三级等保对于用户的密码强度是有要求的。弱密码只需要0.19毫秒就能被破解,而8位强密码破解需要上百年。密码强度可以通过上面配置页面的密码模式密码验证routine来配置:

0
1 692
文章 Qiao Peng · 四月 11, 2022 8m read

      数据平台不仅要安全,还要合规,三级等保是我们要符合的主要安全规范。InterSystems的数据平台和集成平台产品都和三级等保有关。如果没有正确配置它们的安全选项,就会影响到整个系统的安全,影响到合规性。

在生产环境上,如何配置安全的InterSystems的数据平台,并达到三级等保的要求?

这个系列文章,针对InterSystems 数据平台的安全架构,围绕对三级等保的合规性展开,介绍如何配置出一个安全、合规的数据平台。

注:本文提到的InterSystems的数据平台,包括Caché数据库、Ensemble集成平台HealthConnect医疗版集成平台InterSystems IRIS数据平台

三级等保的要求

三级等保要求

对数据平台的要求

身份鉴别

确保用户身份是真实、准确的

访问控制

控制:谁、以什么方式、从什么设备可以访问什么数据和功能?

入侵防范

防范静态数据、传输中的数据、日志和备份中的数据被入侵

恶意代码防范

防止恶意代码被植入和执行,例如SQL注入

可信验证

 

数据完整性

保证数据完整性和一致性的能力

数据备份恢复

对数据的备份与恢复能力和策略

0
2 700
文章 Frank Ma · 三月 2, 2022 3m read

如何检查密码是否足够强大,使其不会很快被破解? 又如何制作一个强大的密码?

我开发了一个工具,可能对这个问题有帮助。你可以在OpenExchange上找到它。用zpm安装。

zpm "install passwords-tool"

这个模块将只安装一个类 caretdev.Passwords中,其中包含一些有用的方法。

安全密码 

要获得一个安全的密码,通常只需使用大写和小写的字母、数字和特殊符号,而且至少要有8个符号的长度。 

Generate方法使用的参数:

  • Length - 只是一个生成密码的长度,默认值为12。
  • IncludeUpperLetter - 包括大写的ASCII字母,如果需要的话是2,默认是1。
  • IncludeLowerLetter - 包括小写ASCII字母,如果需要的话,默认为2。
  • IncludeNumber - 包括数字,如果需要的话,2个,默认为1个。
  • IncludeSymbol - 包括特殊符号,如果需要的话,2个,默认为1个。
0
0 168
文章 Heng Zhang · 九月 11, 2021 1m read

背景:在实际场景中,处理历史数据问题会出现数据暴增的情况,在此情景下journal文件会暴增从而出现磁盘被沾满的风险,因此需要对journal进行删除。 步骤: 1.点击【系统操作】->【任务管理器】->【新任务】创建定时任务。 2.按下图输入参数 image 重要参数: 任务运行所在的 命名空间:%SYS 任务类型:运行传统任务 执行代码:do ##Class(%SYS.Journal.File).PurgeAll()

  1. 点击【下一步】设定时间 按业务需要设定

4.点击【完成】

备注:此任务在数据暴增情况结束后应挂起。

0
0 366
文章 Nicky Zhu · 二月 4, 2021 7m read

上一篇: IRIS中的权限管理

在上一篇文章中,我们介绍了IRIS中的权限控制体系。在本文中我们将以一个常见的实施需求为例介绍如何使用IRIS的权限配置出一个只能使用SQL的用户。

需求的分解

和所有用户需求一样,当用户提出一个需求时,除其语义显式的含义之外,还需分析其是否具有没有明确说明的含义。 对于一个只能使用SQL的用户这样一个需求,即应当结合平台的特征分解成为功能需求: 具有一个合法,可通过用户名和密码使用IRIS的用户 该用户的数据库权限

  • 确认项:可以使用SQL访问所有数据库还是某几个特定的数据库? 该用户的SQL权限
  • 确认项:对于特定的数据库,是否可以执行所有的DDL?
  • 确认项:对于特定的数据库,是否对每一张表都可以执行Select、Update等所有的DML 该用户的程序权限
  • 确认项:用户是否可以通过Portal登录并管理IRIS?

如上所示,一个只能使用SQL的用户,这样一个看似简单的需求,如果需要与用户获得一致的理解并付诸实施,则需要将其分解,对于没有在用户需求中明确约定的部分,应作为待确认项与用户确认。 另外,需要注意的是,IRIS作为一个数据平台,除了提供底层的数据库之外,也提供了Portal等Web应用程序便于使用Sharding、HA、Interoperability等平台功能,通过平台对外提供的Webservice、REST等web接口也受Web应用程序控制。因此,当需要设计权限体系时,不但需要考虑用户使用的数据库相关的权限,也需要考虑是否需要控制Web应用程序的权限。

我们假设需要提供一个严格意义上的只能对某个库的某个Schema下的所有表具有只读权限的用户,即: 具有一个合法,可通过用户名和密码使用IRIS的用户 该用户的数据库权限

  • 该用户只能使用DemoSpace命名空间下的数据库 该用户的SQL权限
  • 该用户不能执行任何DDL
  • 该用户只能对DemoSchema下的所有表执行Select语句 该用户的程序权限
  • 该用户不能登录Portal,不能执行Portal中提供的任何管理功能

对数据库的配置

在IRIS中创建数据库时,默认的行为是引用%DB_%DEFAULT这个资源,并引用%DB_%DEFAULT角色(注意,平台中有一个叫做%DB_%DEFAULT的资源,同时还有一个叫做%DB_%DEFAULT的角色。%DB_%DEFAULT角色通过%DB_%DEFAULT资源获得默认的数据库访问权限)。如果直接使用%DB_%DEFAULT角色或%DB_%DEFAULT资源,都有可能影响到之前以默认配置创建的数据库,因此,在需要细粒度控制访问权限时,往往需要自定义资源和角色实现。

创建资源%DB_DemoDB_Res {#3.1}

资源可在创建数据库的同时创建,也可以在使用默认资源创建数据库之后,再给数据库指派其他资源。本例中,我们在创建命名空间DEMOSPACE的同时创建数据库DEMODB并创建资源。 image

创建角色DemoDB_Read_Role {#3.2}

image 在创建角色之后,即可为其分配资源。根据资源类型的不同,对资源的操作可以有读、写和使用三种权限。对于数据库引用的资源,是读权限和写权限。在本例中,我们需要创建的是只读用户,因此,资源权限分配读权限即可,不用赋写权限。 image

创建用户DemoUser并分配角色

通过Portal创建用户之后,即可给用户分配角色。在本例中,需要给这个用户分配之前创建的DemoDB_Read_Role角色。 image 在经过上述配置之后,用户DemoUser即已拥有对命名空间DEMOSPACE中数据库的访问权限。 image 此时,用户对数据库拥有读权限,但并没有对表执行查询或建表的权限,如果尝试create table,则会得到如下的权限错误信息: image 为继续实验,我们通过Portal执行这SQL语句先创建DemoSchema.Persons这张表 image

为角色DemoDB_Read_Role分配SQL权限

由于用户具有的权限不足,无法执行SQL操作,因此我们需要对该用户的角色赋予对应的权限(或直接给用户赋权,但平台用户较多时,考虑到用户管理的成本,并不推荐这样做)。可以采用如下手段进行SQL的授权

通过Portal授权

在Portal的用户管理和角色管理功能中,均可指定要授予的SQL权限。 SQL特权栏用于对DDL进行数据库级的授权,例如对DEMOSPACE命名空间下的数据库分配建表、修改表、建视图等DDL操作。在本例中,用户不具有这些权限,因此不对该用户对应的角色授予这些权限。 image 在SQL表,SQL视图和SQL过程栏中,则是分别对表、视图和存储过程授予查询、执行等权限。在本例中,用户需要对DemoSchema这个Schema下的表拥有select查询权限,即可通过对SQL表授权进行 image 授权后该角色的SQL表权限如下 image 此时通过SQL工具已可执行查询 image 使用Portal授权时是针对单个的表、视图或存储过程进行。在上例中,我们单独对表DemoSchema.Persons进行了授权,如果我们再建立一张DemoSchema.Employee表,当前的角色和用户并不能自动获得读取其数据的权限。

通过SQL授权

超级管理员或拥有SQL授权权限的用户可以通过SQL的GRANT语句对数据库对象(包括库,函数,表,视图和存储过程等)进行授权,SQL GRANT语句的语法和使用详见GRANT,此处不再赘述。 在上面的例子中我们建立了表DemoSchema.Persons,建表的同时建立的Schema DemoSchema。假如现在我们希望对授权进行简化,使角色DemoDB_Read_Role可以直接获得Schema下所有表的读权限,则可以用如下的SQL:

GRANT SELECT ON SCHEMA DemoSchema to DemoDB_Read_Role

执行成功后再查看DemoDB_Read_Role的SQL权限,会发现: image 即这个角色已经拥有了Schema级的授权,因此,对整个Schema下的所有表都拥有权限。之后在Schema中如果建立了新的表,则这个角色会自动拥有这些表的读权限。

限制用户登录和使用Portal

上例配置的用户可以用于登录IRIS的Portal,但由于没有任何系统功能的权限,不能执行操作。 image IRIS的Web应用程序在创建时默认并不需要额外的资源去访问,这意味着所有合法用户都能登入这个Web应用,但由于支撑应用的后台程序和数据是受到资源的保护的,能登入的用户不一定具有运行程序、访问数据的权限,正如我们建立的用户DemoUser可以登录Portal,但没有功能菜单可用。 如果需要进一步限制用户的行为,禁止其登录,则还需要对应用权限进行控制。 如我们在上一篇文章:IRIS中的权限管理 中所述,Portal是平台提供的Web应用程序,是通过Web应用的权限控制可访问性,因此,需要修改应用的资源要求。 通过菜单: 系统管理 > 安全 > 应用程序 > Web应用程序 可以访问当前系统提供的Web应用列表,其中的/csp/sys即为系统管理门户Portal image 其中必要的资源一栏即为该应用的资源需求,默认为空,即访问该应用不需要特定资源,只要是合法用户即可。我们可以为该应用指定所需资源,例如%Development,即只有具有%Development资源的角色及其对应的用户才能够访问该程序。保存设置后,再尝试以用户DemoUser登录Portal,结果是 image 除非我们为DemoUser引用的角色DemoDB_Read_Role分配资源%Development,该用户都不能登录。 当然,超级管理员由于拥有所有权限,不受这个设置的影响。

总结

通过上述实验,我们创建了一个用户,只能使用SQL连入并查询指定Schema下的表。希望通过这个实验,大家能够掌握IRIS权限管理的基本元素:

  • 用户,角色,权限,资源和许可构成的权限控制体系
  • 数据库、SQL和应用程序都是权限管理的对象

大家在实际项目中可以根据最终用户的实际需要,灵活应用这些概念,构建满足需求的权限配置。

上一篇: IRIS中的权限管理

3
1 448
文章 Nicky Zhu · 二月 3, 2021 6m read

下一篇: 案例: 建立只能使用SQL的用户

IRIS通过认证(Authentication)与授权(Authorization)两项机制控制外部用户对系统及应用、数据资源的可访问性。因此。如需要进行权限控制,则需要通过配置认证和授权进行。

IRIS中的认证 {#2}

认证可以验证任何试图连接到InterSystems IRIS®的用户的身份。一旦通过认证,用户就与IRIS建立了通信,从而可以使用其数据和工具。有许多不同的方法可以验证用户的身份;每种方法都称为验证机制。IRIS 通常被配置为只使用其中一种方式。 支持的认证方式

  • 实例认证:通过用户名/密码对登录平台,即密码认证
  • LDAP:通过第三方LDAP服务器(如Windows Active Directory )完成认证
  • 操作系统认证:建立操作系统用户-平台用户映射,使用操作系统用户登录平台
  • Kerberos:使用Kerberos协议进行认证
  • 代理认证:使用自定义的代码实现认证过程

系统服务与认证 {#2.1}

在安装时,IRIS会启动一系列系统级的服务用与控制与外部用户或系统的交互,这些服务都绑定了默认的认证机制 image 图中红框标出的即为系统安装后会自动启用并需经认证才可使用的系统服务,认证手段可配置。 例如,如果变更%Service_Console的身份验证方法,取消密码方法,用户就不能通过输入用户名密码登入Terminal。 通过Portal的菜单 系统管理 > 安全 > 服务 可访问该设置。

账户控制参数 {#2.2}

通过系统管理 > 安全 > 系统安全 > 系统范围的安全参数中的选项可对于用户名/密码认证手段的行为进行更多的约束。 image

  • 非活动限制 - 指定用户账户不活跃的最大天数,它被定义为成功登录之间的时间。当达到此限制时,该帐户将被禁用。值为0(0)表示对登录之间的天数没有限制。[对于最低安全级别的安装,默认为0,对于正常和锁定的安装,默认为90]。
  • 无效登录限制 (0-64) - 指定连续不成功的登录尝试的最大次数。在达到此限制后,要么禁用账户,要么对每次尝试进行递增的时间延迟;行动取决于如果达到登录限制字段则禁用账户的值。值为0(零)表示对无效登录的次数没有限制。[默认为5]
  • 如果达到登录限制,则禁用账户 - 如果选中,则指定达到无效登录次数(在前一字段中指定)将导致用户账户被禁用。
  • 密码有效期天数(0-99999) - 指定密码过期的频率以及用户更改密码的频率(天数)。当初始设置时,指定密码过期的天数。0(0)表示密码永远不会过期。不会影响已设置了下次登录时更改密码字段的用户。[默认为0]

需要特别注意的是,密码有效性、过期和禁用账户等设置会影响IRIS实例的所有账户,包括IRIS超级管理员账户。如触发了控制策略,则在更新这些帐户的信息之前,可能无法进行各种操作,这可能导致意外的结果。如超级管理员账户被锁定,则需要通过紧急模式启动实例再进行修改。

对于系统可用的认证手段的配置和其他可用的安全配置,请参见Security Administration Guide

IRIS中的授权 {#3}

授权模型 {#3.1}

InterSystems公司的授权模式采用基于角色的访问控制。

  • Users – 用户
  • Roles – 角色
  • Privileges – 权限
  • Resources – 资源 | Permissions – 许可

在这种模式下,用户拥有与分配给各自用户身份的角色相关的权限。 image

  • 一个角色是一个命名的特权集合
  • 一个用户可以拥有一个以上的角色
  • 权限分配给角色,角色分配给用户

其中,Roles就是权限的集合,而权限提供对资源的特定类型的访问的许可。

  • 可控资源: 数据库,服务,应用(包括Web应用 )和其他
  • 可选用的许可: Read, Write or Use,其中执行代码需要数据库的读权限

资源的定义 {#3.2}

资源是一项相对抽象的概念,用来指代IRIS中的数据库,服务,应用等可被访问的对象。例如,对于数据库,在建立时默认采用%DB_%DEFAULT指代,也可自定义资源(数据库资源必须以%DB_开头): image

对于Web应用,默认不需要通过资源控制,即所有可登录用户都可访问(但该用户进程不一定能访问到数据,还需参照是否具有对数据库的访问权限)。如通过分配资源进行控制,则登录用户还需具有资源才能访问这个Web应用: image

因此,一项权限实际上是指对某个资源的一些特定操作的集合。 例如,对于数据库UserDB具有读写操作许可的权限A,对于Web应用/csp/sys具有使用操作许可的权限B。如果我们将这两项权限都赋给角色RoleA,那么这个角色就同时拥有A权限和B权限,从而能够访问数据库UserDB和访问Web应用/csp/sys。

SQL授权 {#3.3}

除了对数据库进行授权外,IRIS作为一个数据平台,需要对外提供数据访问。因此,IRIS也提供了SQL授权对用户可执行的SQL进行细粒度的权限控制。 SQL的授权可以分配给角色或用户。但通常在企业环境中,用户数量会很多,仍然需要对SQL用户进行分组,根据分组规划角色,通过角色进行授权的控制,才能有效降低维护授权所需的工作量。 SQL的授权针对SQL类型,可分为库、表级授权。 对于Create table、drop view、truncate table这一类的DDL,使用库级授权,即用户可在特定的库中执行建表、删除视图等经过授权的操作。如下: image

对于Select、update等DML,则使用表级授权,使用户能够通过DML访问特定的表中的数据。如下: image

除通过Portal操作之外,对于SQL的授权,还可使用IRIS SQL中的额GRANT语句,例如:

GRANT * ON Schema Test TO TestRole

这个SQL即可以将当前操作数据库下Schema Test中的所有表的所有权限都赋给TestRole这个角色。 关于GRANT语句的用法,可参见GRANT指令

以上即为IRIS中进行权限控制所需掌握的概念和内容,在后续文章中,我们会结合实例向大家介绍其使用。

下一篇: 案例: 建立只能使用SQL的用户

推荐阅读

Security Administration Guide - https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI.Page.cls?KEY=GCAS

0
0 651
文章 Hao Ma · 一月 15, 2021 5m read

在这个由三个部分组成的系列文章中,介绍了如何在OAuth 2.0标准下使用IAM简单地为IRIS中的未经验证的服务添加安全性。 

第一部分介绍了一些OAuth 2.0背景知识,以及IRIS和IAM的一些初始定义和配置,以帮助读者理解确保服务安全的整个过程。 

第二部分详细讨论和演示了配置IAM所需的步骤——验证传入请求中的访问令牌,并在验证成功时将请求转发到后端。 

本系列的最后一部分将讨论和演示IAM生成访问令牌(充当授权服务器)并对其进行验证时所需的配置,以及一些重要的最终考虑事项。 

如果您想试用IAM,请联系InterSystems销售代表。 

场景2:IAM作为授权服务器和访问令牌验证器  

与上个场景不同的是,该场景中将使用一个名为“OAuth 2.0 Authentication”的插件。 

如果要在资源所有者密码凭证流中将IAM作为授权服务器使用,客户端应用程序必须对用户名和密码进行身份验证。只有在身份验证成功时,才能发出获取IAM访问令牌的请求。 

首先,将其添加到“SampleIRISService”中。正如下面截屏所示,需要填充一些不同的字段来配置此插件。 

首先,将“SampleIRISService”的ID粘贴到“service_id”字段中,这样就可以在服务中启用该插件。 

0
0 409
文章 Hao Ma · 一月 15, 2021 3m read

在这个由三部分组成的系列文章中,我们将展示如何在OAuth 2.0标准下使用IAM简单地为IRIS中的未经验证的服务添加安全性。 

第一部分中,我们介绍了一些OAuth 2.0背景知识,以及IRIS和IAM的初始定义和配置,以帮助读者理解确保服务安全的整个过程。 

现在,本文将详细讨论和演示配置IAM所需的步骤——验证传入请求中的访问令牌,并在验证成功时将请求转发到后端。 

本系列的最后一部分将讨论和演示IAM生成访问令牌(充当授权服务器)并对其进行验证时所需的配置,以及一些重要的最终考虑事项。 

如果您想试用IAM,请联系InterSystems销售代表。

场景1:IAM作为访问令牌验证器  

在该场景中,需要使用一个外部授权服务器生成JWT(JSON Web Token)格式的访问令牌。该JWT使用了RS256算法和私钥签名。为了验证JWT签名,另一方(本例中是IAM)需要拥有授权服务器提供的公钥。 

由外部授权服务器生成的JWT主体中还包括一个名为“exp”的声明(包含该令牌过期的时间戳),以及另一个名为“iss”的声明(包含授权服务器的地址)。 

因此,IAM需要先使用授权服务器的公钥和JWT内部“exp”声明中包含的过期时间戳对JWT签名进行验证,然后再将请求转发给IRIS。 

0
0 326
文章 Hao Ma · 一月 15, 2021 3m read

介绍 

目前,诸多应用程序通过开放授权框架(OAuth)来安全、可靠、高效地访问各种服务中的资源。InterSystems IRIS目前已兼容OAuth 2.0框架。事实上社区有一篇关于OAuth 2.0和InterSystems IRIS的精彩文章,链接如下。 

然而,随着API管理工具的出现,一些组织开始将其用作单点身份验证,从而防止未经授权的请求到达下游服务,并将授权/身份验证复杂性从服务本身分离出来。 

您可能知道,InterSystems已经推出了自己的API管理工具,即InterSystems API Management (IAM),以IRIS Enterprise license(IRIS Community版本不含此功能)的形式提供。这里是社区另一篇介绍InterSystems AIM的精华帖。 

这是三篇系列文章中的第一篇,该系列文章将展示如何在OAuth 2.0标准下使用IAM简单地为IRIS中的未经验证的服务添加安全性。 

第一部分将介绍OAuth 2.0相关背景,以及IRIS和IAM的初始定义和配置,以帮助读者理解确保服务安全的整个过程。 

本系列文章的后续部分还将介绍两种使用IAM保护服务的可能的场景。在第一种场景中,IAM只验证传入请求中的访问令牌,如果验证成功,则将请求转发到后端。在第二种场景中,IAM将生成一个访问令牌(充当授权服务器)并对其进行验证。 

0
0 539
文章 Qiao Peng · 一月 14, 2021 12m read

本文以及后面两篇该系列文章,是为需要在其基于 InterSystems 产品的应用程序中使用 OAuth 2.0 框架(下文简称为 OAUTH)的开发人员或系统管理员提供的指南。

作者:InterSystems 高级销售工程师 Daniel Kutac

发布后校正和更改历史记录

  • 2016 年 8 月 3 日 - 修正了 Google 客户端配置屏幕截图,更新了 Google API 屏幕截图以反映新版本的页面
  • 2016 年 8 月 28 日 - 更改了 JSON 相关代码,反映了对 Cache 2016.2 JSON 支持的更改
  • 2017 年 5 月 3 日 - 更新了文本和屏幕,以反映 Cache 2017.1 的新 UI 和功能
  • 2018 年 2 月 19 日 - 将 Caché 更改为 InterSystems IRIS 以反映最新的发展。 但是请记住,尽管产品名称发生更改,但文章涵盖所有的 InterSystems 产品——InterSystems IRIS 数据平台、Ensemble 和 Caché。
  • 2020 年 8 月 17 日 - 大面积更改,软件方面更改更大。 要获取 Google 的更新版 Oauth2 的网址,请咨询 Micholai Mitchko。

第 1 部分 客户端

简介

有关开放式授权框架 InterSystems 实现的相关内容,我们分 3 部分讲述,这是第 1 部分。

在第 1 部分中,我们对该主题进行了简短介绍,并提供了一个 InterSystems IRIS 应用程序担当授权服务器客户端并请求一些受保护资源的简单方案。

第 2 部分将讲述一个复杂一些的方案,在该方案中 InterSystems IRIS 本身通过 OpenID Connect 担当授权服务器和身份验证服务器。

本系列的最后一部分将描述 OAUTH 框架类的各个部分,它们由 InterSystems IRIS 实现。

什么是开放授权框架 [1]

许多人已经听说过有关开放授权框架及其用途的信息。 因此这里只做简单介绍,以备未听说过的人参考。

开放授权框架 (OAUTH) 当前为 2.0 版,其是一种协议,允许基于 Web 的主应用程序通过在客户端(应用程序请求数据)和资源所有者(应用程序保存请求的数据)之间建立间接信任来以安全的方式交换信息。 信任本身由客户端和资源服务器都认可并信任的主体提供。 该主体称为授权服务器。

简单举例如下:

假设 Jenny(使用 OAUTH 术语,就是资源所有者)在开展 JennyCorp 公司的一个工作项目。 她为一个潜在的大型业务创建了项目计划,并邀请 JohnInc 公司的业务伙伴 John(客户端用户)审阅此文档。 不过,她并不愿意让 John 访问自己公司的 VPN,因此她将文档放在 Google 云端硬盘(资源服务器)或其他类似的云存储中。 她这样做,已经在她和 Google(授权服务器)之间建立了信任。 她标记了要与 John 共享的文档(John 已经使用 Google 云端硬盘服务,Jenny 知道他的电子邮件)。

当 John 想要阅读该文档时,他进行了 Google 帐户身份验证,然后通过移动设备(平板电脑、笔记本电脑等)启动文档编辑器(客户端服务器)并加载 Jenny 的项目文件。

这听起来很简单,但是两个人与 Google 之间有很多通信。 所有交流均遵循 OAuth 2.0 规范,因此 John 的客户端(阅读器应用程序)必须首先向 Google 进行身份验证(OAUTH 不涵盖此步骤),然后 John 申请获取 Google 对提供表格的同意,经过授权后,Google 就会发出一个访问令牌,授权阅读器应用程序访问文档。 阅读器应用程序使用该访问令牌向 Google 云端硬盘服务发出请求,以检索 Jenny 的文件。

下图说明了各方之间的通信

请注意:虽然所有的 OAUTH 2.0 通信都使用 HTTP 请求,但服务器不必非得是 Web 应用程序。

让我们通过 InterSystems IRIS 来说明这一简单方案。

简单 Google 云端硬盘演示

在本演示中,我们将创建一个基于 CSP 的小型应用程序,该应用程序将使用我们自己的帐户(以及作为奖励的日历列表)来请求存储在 Google 云端硬盘服务中的资源(文件列表)。

基本要求

开始应用程序编码之前,我们需要准备环境。 这包括启用 SSL 的 Web 服务器和 Google 配置文件。

Web 服务器配置

如上所述,我们需要使用 SSL 与授权服务器进行通信,因为默认情况下 OAuth 2.0 要求如此。 我们需要确保数据安全,对吧?

解释如何配置 Web 服务器来支持 SSL 的内容超出了本文讨论的范围,因此,请以您喜欢的方式参阅相应 Web 服务器的用户手册。 为了您的好奇心(我们稍后可能会显示一些屏幕截图),在此特定示例中,我们将使用 Microsoft IIS 服务器。

Google 配置

为了向 Google 注册,我们需要使用 Google API Manager- https://console.developers.google.com/apis/library?project=globalsummit2016demo

为了进行演示,我们创建了一个帐户 GlobalSummit2016Demo。 确保我们已启用 Drive API

现在,该定义凭据了

请注意以下事项:

_Authorized JavaScript – _我们仅允许本地生成的脚本(相对于调用页面)

_Authorized redirect URIs – 从理论上讲,我们可以将客户端应用程序重定向到任何站点,但是当使用 InterSystems IRIS OAUTH 实现时,我们必须重定向到** https://localhost/csp/sys/oauth2/OAuth2.Response.cls**。您可以定义多个授权的重定向 URI,如屏幕截图所示,但是对于本演示,我们只需要两者中的第二个条目。

最后,我们需要将 InterSystems IRIS 配置为 Google 授权服务器的客户端

Caché /IRIS配置

InterSystems IRIS OAUTH2 客户端配置需要两步。 首先,我们需要创建服务器配置。

在 SMP 中,导航至系统管理 > 安全性 > OAuth 2.0 > 客户端配置

点击创建服务器配置按钮,填写表格并保存。

输入到表格的所有信息可以在 Google 开发者控制台网站上找到。 请注意,InterSystems IRIS 支持自动 Open ID 发现。 但是,由于我们没有使用它,因此我们手动输入所有信息

现在,点击新创建的 Issuer Endpoint
旁边的“客户端配置”链接。并点击创建客户端配置按钮。

将“客户端信息”和“JWT 设置”选项卡保留为空(默认值),并填写客户端凭据。

请注意:我们正在创建机密客户端(这比公共客户端更安全,这意味着客户端秘密永远不会离开客户端服务器应用程序(永远不会传输到浏览器)

此外,请确保选中**“使用 SSL/TLS**”,并提供主机名(本地主机,因为我们将本地重定向到客户端应用程序),最后提供端口和前缀(当同一台机器上有多个 InterSystems IRIS 实例时,这非常有用)。 根据输入的信息,会计算客户端重定向 URL 并显示在上一行中。

在上面的屏幕截图中,我们提供了一个名为 GOOGLE 的 SSL 配置。 该名称本身实际上仅用于帮助您确定此特定通信通道使用的可能是众多 SSL 配置中的哪个。 Caché 使用 SSL/TLS 配置存储所有必要的信息,以建立与服务器(在本例中,为 Google OAuth 2.0 URI)的安全流量。

有关详细信息,请参阅文档 。

Supply Client ID 和 Client Secret 值从 Google 凭据定义表中获得(使用手动配置时)。

现在,我们完成了所有的配置步骤,可以开始编写 CSP 应用程序代码。

客户端应用程序

客户端应用程序是基于 Web 的简单 CSP 应用程序。 因此,它包含由 Web 服务器定义和执行的服务器端源代码,以及由 Web 浏览器向用户公开的用户界面。 下文提供的示例代码期望客户端应用程序在 GOOGLE 名称空间中运行。 请将路径 /csp/google/ 修改为您的命名空间。

客户端服务器

客户端服务器是一个简单的两页应用程序。 在该应用程序内,我们将:

·        将 URL 重定向到 Google 授权服务器

·        执行向 Google Drive API 和 Google Calendar API 的请求并显示结果

第 1 页

这是应用程序的一页,我们决定在此处调用 Google 的资源。

以下是此页面上简单但功能齐全的代码。

<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">Class Web.OAUTH2.Google1N Extends %CSP.Page</font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">{</font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">Parameter OAUTH2CLIENTREDIRECTURI = "https://localhost/csp/google/Web.OAUTH2.Google2N.cls";</font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">Parameter OAUTH2APPNAME = "Google";</font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">ClassMethod OnPage() As %Status</font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">{</font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>&html<</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial"></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial"></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">        </span><!-- insert the page content here --></font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">        </span><h1>Google OAuth2 API</h1></font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">        </span><p>This page demo shows how to call Google API functions using OAuth2 authorization.</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">        </span><p>We are going to retrieve information about user and his/her Google Drive files as well as calendar entries.</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">        </span>></font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><span style="margin: 0px;"><font color="#000000" face="Arial">        </font></span></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>// we need to supply openid scope to authenticate to Google</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>set scope="openid https://www.googleapis.com/auth/userinfo.email "_</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>"https://www.googleapis.com/auth/userinfo.profile "_</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>"https://www.googleapis.com/auth/drive.metadata.readonly "_</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>"https://www.googleapis.com/auth/calendar.readonly"</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>set properties("approval_prompt")="force"</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>set properties("include_granted_scopes")="true"</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>set url=##class(%SYS.OAuth2.Authorization).GetAuthorizationCodeEndpoint(..#OAUTH2APPNAME,scope,</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>..#OAUTH2CLIENTREDIRECTURI,.properties,.isAuthorized,.sc) </font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>w !,"<p><a href='"_url_"'><img border='0' alt='Google Sign In' src='images/google-signin-button.png' ></a>" </font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">      </span>&html<</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>Quit $$$OK</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">}</font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">ClassMethod OnPreHTTP() As %Boolean [ ServerOnly = 1 ]</font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">{</font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>#dim %response as %CSP.Response</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>set scope="openid https://www.googleapis.com/auth/userinfo.email "_</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>"https://www.googleapis.com/auth/userinfo.profile "_</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>"https://www.googleapis.com/auth/drive.metadata.readonly "_</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>"https://www.googleapis.com/auth/calendar.readonly"</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>if ##class(%SYS.OAuth2.AccessToken).IsAuthorized(..#OAUTH2APPNAME,,scope,.accessToken,.idtoken,.responseProperties,.error) {</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>set %response.ServerSideRedirect="Web.OAUTH2.Google2N.cls"</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>}</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>quit 1</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>}</font></font></span>
<span style="margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">}</font></span>

代码的简要说明如下:

1.      OnPreHTTP 方法 - 首先,我们有机会时检查一下,我们是否由于 Google 授权而获得了有效的访问令牌——这种情况可能会发生,例如当我们只是刷新页面时。 如果没有,我们需要授权。 如果我们有令牌,我们只需将页面重定向到显示结果的页面

2.       OnPage 方法 - 只有在我们没有可用的有效访问令牌时,我们才到这里,因此我们需要开始通信——向 Google 进行身份验证和授权,以便它向我们授予访问令牌。

3.       我们定义了作用域字符串和属性数组,用于修改 Google 身份验证对话框的行为(我们需要先向 Google 进行身份验证,然后它才能根据我们的身份对我们进行授权)。

4.       最后,我们收到 Google 登录页面的 URL,然后将其提供给用户,接着提供同意页面。

还有一点注意事项:

我们在 OAUTH2CLIENTREDIRECTURI 参数的 https://www.localhost/csp/google/Web.OAUTH2.Google2N.cls 中指定真正的重定向页面。 但是,我们在 Google 凭据定义中使用了 InterSystems IRIS OAUTH 框架的系统页面! 重定向由我们的 OAUTH 处理程序类在内部处理。

第 2 页

此页面显示 Google 授权的结果,如果成功,我们将调用 Google API 调用以检索数据。 同样,此代码简单,但功能齐全。 相比读者的想象,我们以更结构化的方式来显示输入数据。

<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">Include %occInclude</font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">Class Web.OAUTH2.Google2N Extends %CSP.Page</font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">{</font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">Parameter OAUTH2APPNAME = "Google";</font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">Parameter OAUTH2ROOT = "https://www.googleapis.com";</font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">ClassMethod OnPage() As %Status</font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">{</font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>&html<</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">   </span></font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">   </span>></font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>// Check if we have an access token</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>set scope="openid https://www.googleapis.com/auth/userinfo.email "_</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>"https://www.googleapis.com/auth/userinfo.profile "_</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>"https://www.googleapis.com/auth/drive.metadata.readonly "_</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>"https://www.googleapis.com/auth/calendar.readonly"</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>set isAuthorized=##class(%SYS.OAuth2.AccessToken).IsAuthorized(..#OAUTH2APPNAME,,scope,.accessToken,.idtoken,.responseProperties,.error)</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>if isAuthorized { </font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>// Google has no introspection endpoint - nothing to call - the introspection endpoint and display result -- see RFC 7662.<span style="margin: 0px;">  </span></font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>w "<h3>Data from <span style='color:red;'>GetUserInfo API</span></h3>"</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>// userinfo has special API, but could be also retrieved by just calling Get() method with appropriate url<span style="margin: 0px;">    </span></font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>try {</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>set tHttpRequest=##class(%Net.HttpRequest).%New()</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">      </span>$$$THROWONERROR(sc,##class(%SYS.OAuth2.AccessToken).AddAccessToken(tHttpRequest,"query","GOOGLE",..#OAUTH2APPNAME))</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">      </span>$$$THROWONERROR(sc,##class(%SYS.OAuth2.AccessToken).GetUserinfo(..#OAUTH2APPNAME,accessToken,,.jsonObject))</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">      </span>w jsonObject.%ToJSON()</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>} catch (e) {</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">      </span>w "<h3><span style='color: red;'>ERROR: ",$zcvt(e.DisplayString(),"O","HTML")_"</span></h3>"<span style="margin: 0px;">    </span></font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>}</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>/******************************************</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>*<span style="margin: 0px;">                                         </span>*</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>*<span style="margin: 0px;">      </span>Retrieve info from other APIs<span style="margin: 0px;">      </span>*</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>*<span style="margin: 0px;">                                         </span>*</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>******************************************/</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>w "<hr>"</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>do ..RetrieveAPIInfo("/drive/v3/files")</font></font></span>
<font color="#000000" face="Arial" size="2"> </font>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>do ..RetrieveAPIInfo("/calendar/v3/users/me/calendarList")</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>} else {</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>w "<h1>Not authorized!</h1>"<span style="margin: 0px;">  </span></font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>}</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>&html<</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>></font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>Quit $$$OK</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">}</font></span>
<font color="#000000" face="Arial" size="2"> </font>

<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">ClassMethod RetrieveAPIInfo(api As %String)</font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">{</font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>w "<h3>Data from <span style='color:red;'>"_api_"</span></h3><p>"</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>try {</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>set tHttpRequest=##class(%Net.HttpRequest).%New()</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>$$$THROWONERROR(sc,##class(%SYS.OAuth2.AccessToken).AddAccessToken(tHttpRequest,"query","GOOGLE",..#OAUTH2APPNAME))</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>$$$THROWONERROR(sc,tHttpRequest.Get(..#OAUTH2ROOT_api))</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>set tHttpResponse=tHttpRequest.HttpResponse</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>s tJSONString=tHttpResponse.Data.Read()</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>if $e(tJSONString)'="{" {</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">      </span>// not a JSON</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">      </span>d tHttpResponse.OutputToDevice()</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>} else {<span style="margin: 0px;">      </span></font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">      </span>w tJSONString</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">      </span>w "<hr/>"</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">      </span>/*</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">      </span>// new JSON API</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">      </span>&html<<table border=1 style='border-collapse: collapse'>></font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">      </span>s tJSONObject={}.%FromJSON(tJSONString)</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">      </span>set iterator=tJSONObject.%GetIterator()</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">        </span>while iterator.%GetNext(.key,.value) {</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">          </span>if $isobject(value) {</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">      </span><span style="margin: 0px;">      </span>set iterator1=value.%GetIterator()</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">            </span>w "<tr><td>",key,"</td><td><table border=1 style='border-collapse: collapse'>"</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">            </span>while iterator1.%GetNext(.key1,.value1) {</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">            </span>if $isobject(value1) {</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">                </span>set iterator2=value1.%GetIterator()</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">                </span>w "<tr><td>",key1,"</td><td><table border=0 style='border-collapse: collapse'>"</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">                </span>while iterator2.%GetNext(.key2,.value2) {</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">                    </span>write !, "<tr><td>",key2, "</td><td>",value2,"</td></tr>"<span style="margin: 0px;">                   </span></font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">                </span>}</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">                </span>// this way we can go on and on into the embedded objects/arrays</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">              </span>w "</table></td></tr>"</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">            </span>} else {</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span><span style="margin: 0px;">                </span>write !, "<tr><td>",key1, "</td><td>",value1,"</td></tr>"<span style="margin: 0px;">       </span></font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">            </span>}</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">            </span>}</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">          </span>w "</table></td></tr>"</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">          </span>} else {</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">              </span>write !, "<tr><td>",key, "</td><td>",value,"</td></tr>"</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">          </span>}</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">        </span>}<span style="margin: 0px;">    </span><span style="margin: 0px;">   </span></font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>&html<</table><hr/></font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>></font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>*/</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>}</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>} catch (e) {</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">    </span>w "<h3><span style='color: red;'>ERROR: ",$zcvt(e.DisplayString(),"O","HTML")_"</span></h3>"</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font face="Arial"><font color="#000000"><span style="margin: 0px;">  </span>}</font></font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">}</font></span>
<span style="background: rgb(242, 242, 242); margin: 0px; line-height: 115%; font-size: 9pt;"><font color="#000000" face="Arial">}</font></span>

 

让我们快速看一下代码:

1.       首先,我们需要检查一下我们是否具有有效的访问令牌(即我们是否被授权)

2.       如果是,我们可以向 Google 提供且由已发布访问令牌覆盖的 API 发出请求

3.       为此,我们使用标准的 %Net.HttpRequest 类,但根据 API 规范,我们将访问令牌添加到 GET 或 POST 方法中

4.       如您所见,为了方便您,OAUTH 框架已实现 GetUserInfo()方法,但是您可以使用 Google API 规范直接检索用户信息,就像我们在 RetrieveAPIInfo()助手方法中所做的一样。

5.       由于在 OAUTH 世界中以 JSON 格式交换数据司空见惯,因此我们只读取传入的数据,然后简单地将其转储到浏览器。 应用程序开发人员可以解析和格式化接收到的数据,以便用户可以看明白。 但这超出了本演示的范围。 (尽管一些代码有注释,显示了如何完成解析。)

下图是一个输出屏幕截图,显示了原始 JSON 数据。

继续阅读第 2 部分,该部分讲述 InterSystems IRIS 担当授权服务器和 OpenID Connect 提供程序相关的内容。

0
0 399
文章 Nicky Zhu · 一月 10, 2021 8m read

当您首次使用InterSystems IRIS时,通常只需安装最低安全级别的系统。您输入密码的次数会比较少,这样有利于快速了解和操作开发服务和Web应用程序。而且,最低的安全性有时更便于部署开发项目或解决方案。

然而,有时需要将项目移出开发环境,迁移到一个可能很不友好的互联网环境中。在部署到生产环境之前,需要使用最大的安全设置(即,完全锁定)对其进行测试。这就是我们在本文中将要讨论的内容。

如果想更全面地了解InterSystems Caché、Ensemble和IRIS中的DBMS安全性问题,请阅读我的另一篇文章《在生产环境中安装InterSystems Caché DBMS的相关建议》。

InterSystems IRIS中安全系统的设计概念是针对不同的类别(用户、角色、服务、资源、特权和应用程序)应用不同的安全设置。

image 可以为用户分配角色。用户和角色可以对资源(数据库、服务和应用程序)拥有不同的读、写和使用权限。用户和角色还可以对数据库中的SQL表拥有SQL权限。

安全级别的差异

用户在安装InterSystems IRIS时,可以为其选择安全级别:最小、正常或锁定。这些级别在用户参与程度、可用角色和服务,以及服务和应用程序的身份验证方法的配置方面存在差异。如需了解更多信息,请阅读《InterSystems IRIS安装准备》指南中的《InterSystems安全性准备》章节。

在文档中,您可以找到下面这些表格,其中显示了每个级别的安全设置。这些安全设置可以在系统管理门户界面进行更改。

初始用户安全设置

Security SettingMinimalNormalLocked Down
Password Pattern3.32ANP3.32ANP8.32ANP
Inactive Limit090 days90 days
Enable _SYSTEM UserYesYesNo
Roles assigned to UnknownUser%AllNoneNone

初始服务属性设置

Service PropertyMinimalNormalLocked Down
Use Permission is PublicYesYesNo
Requires AuthenticationNoYesYes
Enabled ServicesMostSomeFewest

初始服务启用设置

ServiceMinimalNormalLocked Down
%Service_BindingsEnabledEnabledDisabled
*%Service_CSPEnabledEnabledEnabled
%Service_CacheDirectEnabledDisabledDisabled
%Service_CallInEnabledDisabledDisabled
%Service_ComPortDisabledDisabledDisabled
%Service_ConsoleEnabledEnabledEnabled
%Service_ECPDisabledDisabledDisabled
%Service_MSMActivateDisabledDisabledDisabled
%Service_MonitorDisabledDisabledDisabled
%Service_ShadowDisabledDisabledDisabled
%Service_TelnetDisabledDisabledDisabled
%Service_TerminalEnabledEnabledEnabled
%Service_WebLinkDisabledDisabledDisabled

*InterSystems IRIS环境下,%Service_CSP应用%Service_WebGateway。 不同的操作系统所使用的服务略有不同。

如何提高安全性

您需要为每个启用的服务选择合适的身份验证方法,包括:无认证(unauthenticated)、密码、Kerberos或授权。

您还需要禁用系统中未使用的web应用程序。对已启用的web应用程序选择正确的身份验证方法:认证、密码、Kerberos、授权、登录或cookie。

当然,管理员可以为每一个项目和解决方案选择安全设置,以满足客户的项目要求。整个过程应始终保持一种平衡,即,一方面要保证系统足够方便以支持用户完成实际工作,另一方面又要保证系统足够安全能够阻止入侵者。不过众所周知,被禁用的系统才是最安全的系统。

如果遇到需要多次手动提高系统安全级别的情况,这就是一个明确的迹象,表明需要编写一个软件模块来解决这些问题。

实际上,InterSystems Open Exchange提供了一个锁定(LockDown)程序,可以帮助您提高安全性。该程序的源代码可以在InterSystems isc-apptools-lockdown页面的存储库中找到。

LockDown程序有以下几种作用:

首先,更改以下预安装用户的密码:

  • Admin,
  • CSPSystem,
  • IAM,
  • SuperUser,
  • UnknownUser,
  • _Ensemble,
  • _SYSTEM.

其次,禁用除以下服务之外的所有服务:

  • %%service_web gateway
  • %service_console
  • %service_login
  • %service_terminal

再次,为所有web应用程序设置密码保护,包括:

  • /csp/ensdemo
  • /csp/samples
  • /csp/user
  • /isc/studio/usertemplates
  • /csp/docbook
  • /csp/documatic
  • /isc/studio/rules
  • /isc/studio/templates

最后,设置系统范围内的安全参数,包括:

  • 密码复杂度为 "8.32 ANP"
  • 限制90天内不活跃的用户
  • 开启审计和其他所有安全相关的事件

您可以从GitHub下载LockDown.cls,在系统上安装好LockDown程序。然后在终端输入以下内容:

USER>zn “%SYS”
%SYS>do $system.OBJ.Load("/home/irisusr/LockDown.cls","ck")

或者可以使用以下命令从公共注册中心通过ZPM批处理管理器进行安装:

USER>zn “%SYS”
%SYS> zpm “install isc-apptools-lockdown”

执行LockDown程序

强烈建议在执行LockDown程序之前先进行备份。

必须从%SYS命名空间执行LockDown程序。如果您不想更改所有预安装用户的密码,请将第一个参数保留为空。

如果希望保留使用IRIS Studio、Atelier或VSCode编辑程序和类的能力,请不要禁用%Service_Bindings服务,只需将bindings参数设置为1即可。下面是一个示例: do ##class(App.Security.LockDown).Apply("New Password 123",.msg,1) 此模块还包含一个功能,该功能在系统密码被盗用、需要替换所有预装帐户但无需执行锁定的情况下很有用。可以参考下面的运行: do ##class(App.Security.LockDown).Change Password("New Password 123", "Admin,CSPSystem,IAM,SuperUser,Unknown User, _Ensemble,_SYSTEM") 在执行锁定之后,应用程序或项目极有可能会停止工作。为了解决这个问题,需要将一些安全设置恢复到原始状态。这个操作可以通过管理门户界面(安全性部分)实现或以编程方式完成。

锁定后更改安全设置

锁定之后,如果您的web应用程序使用了密码以外的身份验证方法,就需要进行启用。

建议运行软件模块zpm-registry-test-deployment,它有一个在ZPM-registry项目中使用LockDown的示例。

在IRIS上以最低的安全级别安装该项目,安装结束时将开始运行其中的代码。代码可以用来:

  • 更改所有预安装用户的密码。
  • 禁用此项目中未使用的所有服务。
  • 为系统上的所有应用程序启用密码保护,但Web应用程序/注册表(允许未经授权的用户获取注册表中的软件包列表)除外。
  • 创建一个拥有在注册表中发布新包特权的新用户。该用户必须对IRISAPP数据库中的项目表具有写权限。

创建一个新用户:

set tSC= ##class(App.Security.LockDown).CreateUser(pUsername, "%DB_"_Namespace, pPassword, "ZMP registry user",Namespace)
If $$$ISERR(tSC) quit tSC
write !,"Create user "_pUsername

为新的未授权用户添加权限:

set tSC=##class(App.Security.LockDown).addSQLPrivilege(Namespace, "1,ZPM.Package", "s", "UnknownUser")
set tSC=##class(App.Security.LockDown).addSQLPrivilege(Namespace, "1,ZPM.Package", "s", pUsername)
set tSC=##class(App.Security.LockDown).addSQLPrivilege(Namespace, "1,ZPM.Package_dependencies", "s", pUsername)
set tSC=##class(App.Security.LockDown).addSQLPrivilege(Namespace, "1,ZPM_Analytics.Event", "s", pUsername)
set tSC=##class(App.Security.LockDown).addSQLPrivilege(Namespace, "9,ZPM.Package_Extent", "e", pUsername)
set tSC=##class(App.Security.LockDown).addSQLPrivilege(Namespace, "9,ZPM_Analytics.Event_Extent", "e", pUsername)
If $$$ISERR(tSC) quit tSC
write  !,"Add privileges "

运行LockDown项目:

set tSC= ##class(App.Security.LockDown).Apply(NewPassSys)
If $$$ISERR(tSC) quit tSC

Change the settings for the web app so that an unknown user can log in:
set prop("AutheEnabled")=96
set tSC=##class(Security.Applications).Modify("/registry",.prop)
If $$$ISERR(tSC) quit tSC
write !,"Modify /registry "

Change the settings for the %service_terminal service, changing the authorization method to Operating System, Password:
set name="%service_terminal"
set prop("Enabled")=1
set prop("AutheEnabled")=48 ; Operating System,Password
set tSC=##class(Security.Services).Modify(name,.prop)
If $$$ISERR(tSC) quit tSC
write !,"Modify service terminal"

总结

在本文中,我讨论了为何要提高系统安全性级别的原因,并且通过一个InterSystems LockDown程序运行示例,演示了如何通过编程的方式提升安全性。

在本文所介绍的方法中,我们首先关闭了系统中的所有内容(即,设置最大安全级别)。然后通过开放项目运行所需的服务和应用程序(但仅限于这些)来控制安全性。我相信还有许多其他的方法和最佳实践,欢迎大家在文章评论区留言告诉我们。

0
0 248