0 关注者 · 44 帖子

Caché 服务器页面 (CSP) 是一种架构和工具集,用于通过 InterSystems 数据平台构建交互式 Web  应用程序。

文章 YuCheng Hu · 八月 1, 2024 3m read

针对 InterSystems IRIS 数据库的一些基本概念。

InterSystems IRIS 是什么

InterSystems IRIS 是基于 Caché/M 语言开发的一个数据库,这个数据库被大量使用在医疗系统中,也是北美地区医疗系统病历和文件管理中默认使用的事实标准。

Caché/M 是什么

Caché/M 是 MUMPS 程序语言 开发的数据库,Caché/M 提供了代码接口,可以直接使用 Caché/M 对数据库来进行操作。

Caché 是一个法语单词,但是又非常容易和英语使用的 Cache 搞混,所以大部分时候使用的是 Caché/M 来表达。

M 表达的意思是 Mumps 程序语言,简称 M 语言,Caché 使用 M 语言构建了不少函数。

InterSystems IRIS 不是开源数据库

因为涉及到医疗系统数据的使用,所以 InterSystems IRIS 并不是一个开源的数据库,你可以使用下面的链接:Evaluate InterSystems Products 8 来下载评估版本。

本文就是根据下载的链接来进行 Windows 安装的教程。

上面的页面在下载之前,你需要进行注册,并且提供你的信息。

双击运行

当下载完成后,可以双击运行安装程序。

我们会看到下面的安装界面:

同意许可协议后,单击下一步继续安装。

配置实例名

我们在这里使用默认的就可以,通常使用的 IRIS。

选择安装目录

0
0 346
文章 Michael Lei · 二月 18, 2024 11m read

1. IRIS RAG Demo

IRIS RAG Demo

这是 IRIS 与 RAG(检索增强生成)示例的一个简单演示。 后端是使用 IRIS 和 IoP用 Python 编写的,LLM 模型是 orca-mini 并由 ollama 服务器提供。 前端是用 Streamlit 编写的聊天机器人。

    1. IRIS RAG 演示](#1-iris-rag-demo)
    • 1.1. 什么是 RAG](#11-what-is-rag)
    • 1.2. 如何工作?
    • 1.3. 安装演示](#13-installation-the-demo)
    • 1.4. 使用方法
    • 1.5. 演示如何运行](#15-演示如何运行)
      • [1.5.1. 前端](#151-前端)
      • 1.5.2. 后台
        • [1.5.2.1. 业务服务](#1521-业务服务)
        • [1.5.2.2. 业务流程](#1522-业务流程)
        • [1.5.2.3. LLM 操作](#1523-the-llm-operation)
        • 1.5.2.4. 矢量操作](#1524-the-vector-operation)
    • 1.6. 一般性说明](#16-一般性说明)

1.1. 什么是 RAG?

RAG 是 Retrieval Augmented Generation(检索增强生成)的缩写,它带来了使用带有知识库的 LLM 模型(GPT-3.5/4、Mistral、Orca 等)的能力。

为什么它很重要? 因为它允许使用知识库来回答问题,并使用 LLM 来生成答案。

例如,如果你直接向 LLM 询问**"grongier.pex 模块是什么?"**,它将无法回答,因为它不知道这个模块是什么(也许你也不知道🤪)。

但是,如果你向 RAG 提出同样的问题,它就能回答,因为它会使用知识库,知道 grongier.pex 模块是什么,从而找到答案。

既然你已经知道什么是 RAG,那就让我们来看看它是如何工作的。

1.2. 它是如何工作的?

首先,我们需要了解 LLMS 的工作原理。LLMS 经过训练,可以根据前一个单词预测下一个单词。因此,如果你给它一个句子,它就会尝试预测下一个词,以此类推。很简单吧?

要与 LLM 交互,通常需要给它一个提示,它就会生成句子的其余部分。例如,如果你给它一个提示 "什么是 grongier.pex 模块?

很抱歉,我对您提到的 Pex 模块并不熟悉。能否请您提供有关它的更多信息或上下文?

好的,不出所料,它不知道什么是 grongier.pex 模块。但如果我们给它一个包含答案的提示呢?例如,如果我们提示``什么是 grongier.pex 模块?它是一个可以让你做 X、Y 和 Z 的模块。`",它就会生成剩下的句子,看起来就像这样:

grongier.pex 模块是一个可以让你执行 X、Y 和 Z 的模块。

好了,现在它知道什么是 grongier.pex 模块了。

但如果我们不知道 grongier.pex 模块是什么呢?我们怎样才能给它一个包含答案的提示呢? 这就需要知识库了。

RAG

RAG 的整个思路是使用知识库找到上下文,然后使用 LLM 生成答案。

为了找到上下文,RAG 将使用一个**知识库。

1.3.安装演示

只需克隆存储库并运行“docker-compose up”命令即可。

git clone https://github.com/grongierisc/iris-rag-demo
cd iris-rag-demo
docker-compose up

⚠️ 一切都是本地的,没有任何东西发送到云端,所以请耐心等待,可能需要几分钟才能开始。

1.4.用法

演示开始后,您可以在 http://localhost:8051 访问前端。

![Frontend](https://github.com/grongierisc/iris-rag-demo/blob/master/misc/iris_chat.png?raw=true)

你可以提出有关「综合注册资讯系统」的问题,例如:

  • 什么是grongier.pex模块?

![Question](https://github.com/grongierisc/iris-rag-demo/blob/master/misc/without_rag.png?raw=true)

正如你所看到的,答案不是很好,因为 LLM 不知道什么是 grongier.pex 模块。

现在,让我们尝试使用 RAG:

上传“grongier.pex”模块文档,它位于“docs”文件夹中,文件“grongier.pex.md”。

并问同样的问题:

  • 什么是grongier.pex模块?

![Question](https://github.com/grongierisc/iris-rag-demo/blob/master/misc/with_rag.png?raw=true)

正如你所看到的,答案要好得多,因为 LLM 现在知道什么是 grongier.pex 模块。

您可以在日志中看到详细信息:

转到管理门户, http://localhost:53795/csp/irisapp/EnsPortal.ProductionConfig.zen?$NAMESPACE=IRISAPP&$NAMESPACE=IRISAPP&,然后单击“消息”选项卡。

首先,您将看到发送到 RAG 进程的消息:

![Message](https://github.com/grongierisc/iris-rag-demo/blob/master/misc/trace_query.png?raw=true)

然后是知识库(向量数据库)中的搜索查询:

![Message](https://github.com/grongierisc/iris-rag-demo/blob/master/misc/trace_result_vector.png?raw=true)

最后,发送给 LLM 的新提示:

![Message](https://github.com/grongierisc/iris-rag-demo/blob/master/misc/trace_new_query.png?raw=true)

1.5.这个Demo如何工作?

该演示由 3 个部分组成:

  • 前端,用 Streamlit 编写
  • 后端,用 Python 和 IRIS 编写
  • 知识库 Chroma 向量数据库
  • LLM,Orca-mini,由 Ollama 服务器提供服务

1.5.1.前端

前端是用 Streamlit 编写的,它是一个简单的聊天机器人,可让您提出问题。

这里没什么花哨的,只是一个简单的聊天机器人。

import os
import tempfile
import time
import streamlit as st
from streamlit_chat import message

from grongier.pex import Director

_service = Director.create_python_business_service("ChatService")

st.set_page_config(page_title="ChatIRIS")


def display_messages():
    st.subheader("Chat")
    for i, (msg, is_user) in enumerate(st.session_state["messages"]):
        message(msg, is_user=is_user, key=str(i))


def process_input():
    if st.session_state["user_input"] and len(st.session_state["user_input"].strip()) > 0:
        user_text = st.session_state["user_input"].strip()
        with st.spinner(f"Thinking about {user_text}"):
            rag_enabled = False
            if len(st.session_state["file_uploader"]) > 0:
                rag_enabled = True
            time.sleep(1) # help the spinner to show up
            agent_text = _service.ask(user_text, rag_enabled)

        st.session_state["messages"].append((user_text, True))
        st.session_state["messages"].append((agent_text, False))


def read_and_save_file():

    for file in st.session_state["file_uploader"]:
        with tempfile.NamedTemporaryFile(delete=False,suffix=f".{file.name.split('.')[-1]}") as tf:
            tf.write(file.getbuffer())
            file_path = tf.name

        with st.spinner(f"Ingesting {file.name}"):
            _service.ingest(file_path)
        os.remove(file_path)

    if len(st.session_state["file_uploader"]) > 0:
        st.session_state["messages"].append(
            ("File(s) successfully ingested", False)
        )

    if len(st.session_state["file_uploader"]) == 0:
        _service.clear()
        st.session_state["messages"].append(
            ("Clearing all data", False)
        )

def page():
    if len(st.session_state) == 0:
        st.session_state["messages"] = []
        _service.clear()

    st.header("ChatIRIS")

    st.subheader("Upload a document")
    st.file_uploader(
        "Upload document",
        type=["pdf", "md", "txt"],
        key="file_uploader",
        on_change=read_and_save_file,
        label_visibility="collapsed",
        accept_multiple_files=True,
    )

    display_messages()
    st.text_input("Message", key="user_input", on_change=process_input)


if __name__ == "__main__":
    page()

💡 我只是在用 :

_service = Director.create_python_business_service("ChatService")

来创建一个前后端之间的绑定.

ChatService 只是互操作性生产中的简单业务服务BS。

1.5.2.后端

后端是用 Python 和 IRIS 编写的。

它由3个部分组成:

  • 业务服务BS
    • 前端的入口点
  • 业务流程BP
    • 如果需要,在知识库中执行搜索
  • 拖曳业务运营BO
    • 一个用于知识库
      • 摄取文档
      • 搜索文档
      • 清除文件
    • 一个用于LLM大模型
      • 生成答案

1.5.2.1.业务服务BS

业务服务是一个简单的业务服务,它允许:

  • 上传文件
  • 提出问题
  • 清除向量数据库
from grongier.pex import BusinessService

from rag.msg import ChatRequest, ChatClearRequest, FileIngestionRequest

class ChatService(BusinessService):

    def on_init(self):
        if not hasattr(self, "target_chat"):
            self.target_chat = "ChatProcess"
        if not hasattr(self, "target_vector"):
            self.target_vector = "VectorOperation"

    def ingest(self, file_path: str):
        # build message
        msg = FileIngestionRequest(file_path=file_path)
        # send message
        self.send_request_sync(self.target_vector, msg)

    def ask(self, query: str, rag: bool = False):
        # build message
        msg = ChatRequest(query=query)
        # send message
        response = self.send_request_sync(self.target_chat, msg)
        # return response
        return response.response

    def clear(self):
        # build message
        msg = ChatClearRequest()
        # send message
        self.send_request_sync(self.target_vector, msg)

基本上,它只是操作和过程之间的传递。

1.5.2.2.业务流程

业务流程是一个简单的过程,允许在需要时搜索知识库

from grongier.pex import BusinessProcess

from rag.msg import ChatRequest, ChatResponse, VectorSearchRequest

class ChatProcess(BusinessProcess):
    """
    the aim of this process is to generate a prompt from a query
    if the vector similarity search returns a document, then we use the document's content as the prompt
    if the vector similarity search returns nothing, then we use the query as the prompt
    """
    def on_init(self):
        if not hasattr(self, "target_vector"):
            self.target_vector = "VectorOperation"
        if not hasattr(self, "target_chat"):
            self.target_chat = "ChatOperation"

        # prompt template
        self.prompt_template = "Given the context: \n {context} \n Answer the question: {question}"


    def ask(self, request: ChatRequest):
        query = request.query
        prompt = ""
        # build message
        msg = VectorSearchRequest(query=query)
        # send message
        response = self.send_request_sync(self.target_vector, msg)
        # if we have a response, then use the first document's content as the prompt
        if response.docs:
            # add each document's content to the context
            context = "\n".join([doc['page_content'] for doc in response.docs])
            # build the prompt
            prompt = self.prompt_template.format(context=context, question=query)
        else:
            # use the query as the prompt
            prompt = query
        # build message
        msg = ChatRequest(query=prompt)
        # send message
        response = self.send_request_sync(self.target_chat, msg)
        # return response
        return response

这真的很简单,它只是向知识库发送一条消息来搜索文档。

如果 知识库 返回文档,则会使用文档内容作为提示,否则会使用查询作为提示。

1.5.2.3.LLM 操作

LLM 操作是一个简单的操作,可以生成答案。


class ChatOperation(BusinessOperation):

    def __init__(self):
        self.model = None

    def on_init(self):
        self.model = Ollama(base_url="http://ollama:11434",model="orca-mini")

    def ask(self, request: ChatRequest):
        return ChatResponse(response=self.model(request.query))

这一步也很简单,它只是向 LLM 发送一条消息来生成答案。

1.5.2.4.Vector 操作

向量操作是一个简单的操作,允许摄取文档、搜索文档和清除向量数据库。


class VectorOperation(BusinessOperation):

    def __init__(self):
        self.text_splitter = None
        self.vector_store = None

    def on_init(self):
        self.text_splitter = RecursiveCharacterTextSplitter(chunk_size=1024, chunk_overlap=100)
        self.vector_store = Chroma(persist_directory="vector",embedding_function=FastEmbedEmbeddings())

    def ingest(self, request: FileIngestionRequest):
        file_path = request.file_path
        file_type = self._get_file_type(file_path)
        if file_type == "pdf":
            self._ingest_pdf(file_path)
        elif file_type == "markdown":
            self._ingest_markdown(file_path)
        elif file_type == "text":
            self._ingest_text(file_path)
        else:
            raise Exception(f"Unknown file type: {file_type}")

    def clear(self, request: ChatClearRequest):
        self.on_tear_down()

    def similar(self, request: VectorSearchRequest):
        # do a similarity search
        docs = self.vector_store.similarity_search(request.query)
        # return the response
        return VectorSearchResponse(docs=docs)

    def on_tear_down(self):
        docs = self.vector_store.get()
        for id in docs['ids']:
            self.vector_store.delete(id)
        
    def _get_file_type(self, file_path: str):
        if file_path.lower().endswith(".pdf"):
            return "pdf"
        elif file_path.lower().endswith(".md"):
            return "markdown"
        elif file_path.lower().endswith(".txt"):
            return "text"
        else:
            return "unknown"

    def _store_chunks(self, chunks):
        ids = [str(uuid.uuid5(uuid.NAMESPACE_DNS, doc.page_content)) for doc in chunks]
        unique_ids = list(set(ids))
        self.vector_store.add_documents(chunks, ids = unique_ids)
        
    def _ingest_text(self, file_path: str):
        docs = TextLoader(file_path).load()
        chunks = self.text_splitter.split_documents(docs)
        chunks = filter_complex_metadata(chunks)

        self._store_chunks(chunks)
        
    def _ingest_pdf(self, file_path: str):
        docs = PyPDFLoader(file_path=file_path).load()
        chunks = self.text_splitter.split_documents(docs)
        chunks = filter_complex_metadata(chunks)

        self._store_chunks(chunks)

    def _ingest_markdown(self, file_path: str):
        # Document loader
        docs = TextLoader(file_path).load()

        # MD splits
        headers_to_split_on = [
            ("#", "Header 1"),
            ("##", "Header 2"),
        ]

        markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
        md_header_splits = markdown_splitter.split_text(docs[0].page_content)

        # Split
        chunks = self.text_splitter.split_documents(md_header_splits)
        chunks = filter_complex_metadata(chunks)

        self._store_chunks(chunks)

如果文档太大,那么向量数据库将无法存储它们,因此我们需要将它们拆分为块。

如果文档是 PDF,那么我们将使用“PyPDFLoader”来加载 PDF,否则我们将使用“TextLoader”来加载文档。

然后,我们将使用“RecursiveCharacterTextSplitter”将文档拆分为块。

最后,我们将块存储到向量数据库中。

如果文档是 Markdown,那么我们将使用“MarkdownHeaderTextSplitter”将文档拆分为块。我们还使用标题将文档拆分为块。

1.6.A. 总 论

所有这些都可以通过“langchains”来完成,但我想向你展示如何使用互操作性框架来做到这一点。并让每个人都更容易理解它是如何工作的。

1
0 587
问题 huang YG · 二月 27, 2024
02/21/24-18:32:48:515 (7568) 3 InterSystems IRIS Internal Failure

Access Violation (0xC0000005) occurred at 00007FFF3DE9C4E8
Process = 00001D90 Thread = 0000203C
Exception Count=1 b_msyslog=0 b_DumpVar=0 b_DumpVar2=0
b_GRelease=0 b_GRelease2=0 b_DeqRes=0 b_DeqRes2=0
Job Type = CSP server
ContextFlags = 0010005F Registers:
RAX=00007FFF3DE9C4E8 RBX=000000C2E5554800 RCX=000000C2E7AFFC00
RDX=000000C2E7B00000 RSI=00000000FFFFFFC0 RDI=00000000000003FF
RSP=000000C25CEBD2F8 RBP=0000000000000000 R8 =0000000000000001
R9 =00007FFF3DE60000 R10=000000C2E7AFFC00 R11=000000C2E7AFFC00
1
0 145
文章 Michael Lei · 八月 17, 2023 1m read

受到@Evgeny Shvarov 的问题@Ashok Kumar T 的回复的启发
我已经创建了一个用于把Global下载成 XML 文件的基础

如何使用:
只需调用 http://<your_server>/csp/samples2/dc.Gdown.cls ?GBL=global_name之类的页面

gbl-name 不带首字母 ^(插入符号)
输出具有默认名称 <global_name>.XML 您可以选择。

已知限制:

  • 你必须将它安装在你的源服务器上
  • 它没有经过测试/跨命名空间工作
  • 没有部分下载
  • 错误处理只是基本或缺失

个人还有提升空间。
就是这样:

0
0 152
文章 Weiwei Gu · 八月 14, 2023 2m read

在提交的 WRC case中(Intersystems 全球技术支持响应中心),我们经常看到客户提出有关新 Web 网关设置的问题,其中管理门户加载一半,但不显示图像。本文将解释为什么会出现此错误,以及如何修复它。本说明主要针对服务 InterSystems IRIS 实例的 Web 网关,但相同的说明也应适用于服务 Caché 实例的 CSP 网关。

问题:

您刚刚在独立的 Web 服务器上安装了 Web Gateway。当你去加载管理门户时,你发现它无法显示或加载图像,如下所示:

为什么会发生这种情况:

问题是,为了完整加载管理门户,InterSystems IRIS 必须加载许多 .js、.css 和 .png 文件(静态文件)。如果您看到像上面这样的管理门户页面,请随时打开浏览器的开发人员工具小程序,导航到“网络”选项卡,并确认未提供各种 .js、.css 和 .png 组件:

最初安装 Web Gateway 时,我们仅为以下扩展设置映射:

.csp .cls .zen .cxw

这些是客户在自己的自定义应用程序中最常使用的文件扩展名类型,以及用于为 Web Gateway 管理门户提供服务的 .cxw 扩展名。如果您想要加载其他管理门户组件,则必须注册其他文件类型以由 Web 网关提供服务。

如何解决该问题:

0
0 151
文章 Weiwei Gu · 八月 7, 2023 1m read

InterSystems 常见问题解答标题

您可以为以下 Web Gateway 错误消息/系统响应设置单独的错误页面:

  • 服务器错误
  • 服务器繁忙
  • 服务器无法使用
  • 服务器超时
  • 连接关闭

在 Web Gateway 管理界面上进行设置([Management Portal] > [System Administration] > [Configuration] > [Web Gateway Management] > [Configuration] > [Default Parameters])。

在“默认参数”(Default Parameters  )菜单的“错误页面”部分中,设置要显示的 html 页面的文件名或发生错误时要重定向到的 URL。

 

0
0 149
文章 姚 鑫 · 十月 23, 2022 3m read

第二十五章 CSP Session 管理 - 选择策略时的注意事项

组的注意事项

本节包含创建身份验证组时要考虑的一些要点。

  1. 仅当决定必须通过会话对象共享数据时才使用会话共享。 By-ID 和登录 Cookie 共享更加强大和可预测。
  2. 创建组时,尽可能保持一致,以便为目标用户创建统一的行为。不要将应用程序同时放在 By-ID 组和 By-Session 组中。使用不同的身份验证策略可能会导致意外行为。 By-ID 优先于 By-Session。因此,如果一个应用程序两者都有,它会保持同步 By-ID
  3. 对组的所有成员使用相同的身份验证类型。特别是,如果组中的某些应用程序允许登录 Cookie 而其他应用程序不允许,则通过用户名/密码进入组会验证整个组,而通过登录 cookie 输入只会验证部分应用程序。这可能会导致用户对为什么有时需要登录而有时不需要登录感到困惑。
  4. CSP 服务器认为每个应用程序都在一个身份验证组中。会话中的一个单独的应用程序形成一个单实体 By-Session 身份验证组。)
  5. 尽量不要将未经身份验证的应用程序放在 By-ID 组中。
  6. By-Session 组很脆弱;使用 By-ID 是一种更稳健的方法。由于有关组的所有信息(包括其共享数据)都包含在单个会话中,因此该组很容易丢失。这是因为会话可能会超时,即在特定时间后会话会自动销毁。如果用户离开他的计算机或使用不在 By-Session 组中的应用程序,则会话可能会超时。如果组中的应用程序之一标记 ENDSESSION=1,则该组被分散。
  7. 如果浏览器打开的选项卡包含来自分散的应用程序的页面,单击它们可能需要多次登录,特别是如果它们最初使用 CSPSHARE=1 进行分组。在任何情况下,来自原始会话的数据都会永久消失。
  8. 当组失去其身份验证时,刷新或从组应用程序转到打开的页面需要用户重新登录。
  9. 结束包含 By-Session 应用程序的会话要求用户在刷新该 by-session 组中的任何应用程序的任何页面时重新登录。终止包含 By-ID 应用程序的会话不需要任何登录,除非该会话的应用程序是该组的唯一成员。
  10. 注销会话会注销会话组的所有成员,即使他们在不同的会话中。刷新任何组的页面都需要重新登录。但是,对于按 ID 组,一个登录会登录整个组。对于 By-Session 组,只要 CSP 网关能够将分散的应用程序引导回新构建的会话对象,一个登录就会登录整个组。
  11. 注销不会破坏会话,因此任何会话数据都会继续存在。
  12. 不能让同一应用程序在同一浏览器的不同选项卡中登录到两个不同的用户。
  13. 身份验证仅在单个浏览器中共享。此运行时标识符存储在 %Session 对象中。
  14. 分组允许与同一组 (By-ID) 或同一会话 (By-Session) 中的用户共享身份验证。如果想从指定组之外的应用程序共享身份验证,请使用登录 Cookie。如果要将身份验证发送到指定组之外的应用程序,请使用 CSPSHARE=1。 (请参阅本书中的“关于 CSPSHARE 的注意事项”部分。)

关于 CSPSHARE 的注意事项

使用 CSPSHARE 作为最后的手段。

在以下情况下,按会话应用程序链接不需要 CSPSHARE=1

  • 如果源应用程序和目标应用程序具有相同的组 ID
  • 如果目标页面与源页面在同一个应用程序中。
  • 如果目标页面应用程序的会话 Cookie 路径与源应用程序的会话 Cookie 路径匹配。

共享数据

By-Session 组可以通过会话对象共享数据。

By-ID 组必须管理自己的数据。例如,如果数据存储在全局中,则可以使用当前用户 $Username 或组的运行时 ID 对数据进行键控。 CSP 服务器为每个浏览器分配一个 browser-id cookie。创建 By-Id 组时,会为其分配一个键,该键是与组 ID 连接的浏览器 ID。这将创建一个唯一键 %CSP.Session.BrowserId,它可以用作存储数据的键。

0
0 54
文章 姚 鑫 · 十月 22, 2022 4m read

第二十四章 CSP Session 管理 - 认证架构

认证架构

安全上下文和粘性登录

应用程序在会话中运行。会话需要运行应用程序的安全上下文。安全上下文包含身份验证状态。

By-SessionsBy-ID Groups 有一个粘性登录,它会记住会话或组中使用的最后一个应用程序的安全上下文。如果组应用程序中的用户以其他用户身份登录,则会更新粘性登录。 (如果用户登录到未经身份验证的应用程序,则粘滞登录不会更新。)

在会话中跳转到应用程序时,会话会尝试使用适合目标应用程序的粘性登录。如果粘性登录与会话的当前安全上下文不匹配,并且应用程序可以接受粘性登录中的身份验证方法,则会话的安全上下文切换到粘性上下文中的安全上下文。

会话结束时,会话的粘性登录会丢失。当包含该组的任何应用程序的所有会话都结束时,该组的粘性登录将丢失。

初始登录后,组有一个关联的粘性登录对象,它在进入组的一个应用程序时尝试使用该对象。当组中的应用程序输入为 UnknownUser 时,粘性登录不会更新,因为这会将组中的所有其他应用程序移动到未经身份验证的安全上下文中。

如果粘性登录包含经过双重身份验证的用户,则该双重身份验证用于非双重身份验证应用程序,只要用户名身份验证在两个应用程序中匹配即可。

级联认证

CSP 服务器在尝试获取应用程序的身份验证信息时使用优先级。它尝试在以下每个事件中获取新的身份验证信息:

  • 对于新会话的第一个请求;
  • 当会话内有应用程序更改时;
  • 当应用程序是 By-id 组的一部分并且会话的当前安全上下文与组的粘性上下文不匹配时;
  • 当请求包含用户名/密码对时。

它尝试按以下顺序依次获取新的身份验证信息:

  1. 显式登录:检查用户是否输入了经过身份验证的用户名/密码。如果他们这样做了,系统会更新应用程序的身份验证组的上下文。 (这将设置组的粘滞登录。)
  2. 粘性登录:获取应用程序组的粘性上下文。如果没有粘性登录和按会话分组,请使用会话的当前上下文。
  3. 登录 Cookie:如果存在并为此应用程序启用,则使用。
  4. 未经身份验证:如果为应用程序启用,则使用未知用户。
  5. 建立登录页面:如果以上都失败,则向用户请求用户名/密码。如果从 %CSP.Session API 调用,则只尝试用户名/密码。登录后,更新组的粘性登录,除非刚刚以 UnknownUser 身份登录。

注销或结束会话

会话注销或结束时身份验证丢失。可以在方法中使用以下 %CSP.Session注销或结束会话:

推荐:CacheLogout=end

注销 CSP 会话的推荐方法是链接到应用程序主页,并传递包含字符串 CacheLogout=endURL。这会在尝试运行主页之前结束当前会话——释放任何获得的许可证、删除现有会话数据并删除会话的安全上下文。

如果此 CSP 应用程序需要身份验证,则没有会话,也没有经过身份验证的用户。在这种情况下,Caché 不运行主页逻辑,而是显示登录页面。当用户提交有效登录时,这将启动这个新会话,然后显示主页。

Set EndSession? =1

这会杀死会话。会话的粘性上下文被破坏。 OnEndSession 被调用。如果会话包含 By-Session 组,则该组被销毁。如果会话包含 By-Id 应用程序,则该应用程序将从继续存在的组中删除,除非这是该组中的唯一应用程序。登录 cookie 不受影响。按会话组丢失其数据。但是,对于 By-Id 组,该组的粘性登录不受单一破坏的影响,并且该组的其他成员保持登录状态。

此外,对于By-Session组,销毁会分散组中的成员,如果成员应用程序重新进入,则不能保证它们将被重新集成到同一个新会话中,或者(如果它们使用CSPSHARE进行分组)发送到不同的会话。

Session Logout

会话已注销。它的粘性上下文被破坏了。如果会话包含按会话组,则该组中的所有应用程序都将失去其身份验证。如果会话包含来自 by-id 组的应用程序,则组会丢失其粘性上下文,并且组中的所有应用程序都将被注销。

此外,还会调用 OnLogout。登录 cookie 被销毁。

会话继续存在,因此为 By-Session 组保留数据。

Session Logout All

可以注销当前作为特定用户身份验证的所有会话。

这会破坏登录 cookie

会话继续存在但没有身份验证。

0
0 77
文章 Yuxiang Niu · 十月 22, 2022 4m read

Cache锁的异常直接影响数据库进程运行,堆积的锁如果处理不及时会造成Cache性能异常,导致数据库访问受限或严重卡顿。本文主要以实例分析介绍Cache中常见锁的作用及其对应的处理方式,包括:系统锁、数据锁、Session锁、仪器锁、程序文件锁。其中数据锁异常需要及时处理。

查询Cache锁有两种方式:

1>可在System Management Protal[Home]>[Locks]中查看;

2>可在terminal端使用Do ^LOCKTAB命令下查看,如下图所示,此方式适用于锁表量达到10000条以上,Protal页面HTTP响应超时无法显示锁表时使用。

 

一、【系统锁】

此类锁在数据库初次搭建时就存在,例如:

1>^%SYS("CSP","Daemon") ---调度锁

2>^TASKMGR  ---守护进程

3>^DBACK ---数据库备份锁

4>^SYS("Task","TaskD",1058) ---执行系统任务锁

主要作用

0
0 369
文章 姚 鑫 · 十月 20, 2022 4m read

第二十二章 CSP Session 管理 - Private Pages

CSP 提供了私有页面的概念。只能从同一 CSP 会话中的另一个页面导航到私有页面。私有页面对于想要限制对某些页面的访问的应用程序很有用。

例如,假设有一个名为 private.csp 的私有页面(CSP 示例页面之一)。用户无法直接导航到 private.csp(例如,通过输入其 URL)。用户只能从另一个 CSP 页面中包含的链接导航到 private.csp。引用 CSP 页面中包含的链接不能是绝对 URL,以 http:// 开头。只有相对于引用页面的路径才被私有页面方法正确加密/标记。即:下面的前两个链接将相同的令牌传递给目标私有页面 test2.csp

<A HREF='test2.csp'>Link to private page - relative path</A> <BR>
<A HREF='/csp/samples/test2.csp'>
       Link to private page - full application path</A> <BR>

此链接的散列方式不同,无法访问。

<A HREF='http://myserver/csp/samples/test2.csp'>
        Link to private page - absolute path</A>

用户也不能为私有页面添加书签以供以后使用,因为用于保护私有页面的加密令牌仅对当前会话有效。

私有页面的工作方式如下。负责该页面的子类中的 %CSP.Page 将其类参数 PRIVATE 设置为 1。请求此页面的 URL 必须在其查询字符串中包含有效的加密 CSPToken 值。 CSP 处理的任何指向此页面的链接都会自动具有加密的 CSPToken 值。

编码 URL 参数

以类似于私有页面的方式,可以通过在类参数 ENCODED 中设置 %CSP.Page 的值来指定要对 CSP 页面的 URL 参数进行编码。 ENCODED 可以设置为 012。任何指向 ENCODED 类参数为 12 的页面的链接都会自动将任何 URL 参数编码在加密的 CSPToken 值中。如果 ENCODED 设置为 2,则必须对值进行编码;如果为 1,则可以混合编码值和未编码值。

ENCRYPTED 的三个设置是:

  • ENCODED=0 — 查询参数未加密
  • ENCODED=1 — 查询参数被加密并在 CSPToken 内传递
  • ENCODED=2 — 与“1”相同,除了在调用 Page 方法之前从 %request 对象中删除任何未加密的参数。这可确保在对象中的 %CSP.Request中只有加密参数可用。

请注意,因为 ENCODED=2 会从 url 中删除未加密的参数,所以它可以禁用 Zen <form> 元素等组件。

ENCODED=2 的示例

例如,假设有两个 .csp 页。一个页面 (list.csp) 将银行帐户列表显示为超链接,第二个页面 (account.csp) 显示有关特定帐户的信息。 account.csp 需要一个名为 ACCOUNTIDURL 参数来确定要显示的帐户。我们不希望在客户端上发布帐号,也不希望未经授权访问 account.csp 或显示任何其他帐号的能力。我们可以通过将 account.csp ENCODED 参数设置为 2 来做到这一点。以下是相关的 .csp 文件:

list.csp 的源

<html>
<body>
Select an account:<br>
<a href="account.csp?ACCOUNTID=100">Checking</a>
<a href="account.csp?ACCOUNTID=105">Saving</a>
</body>
</html>

account.csp 的源

<html>
<csp:class private=1 encoded=2>
<body>
Account Balance: <b>$#(..GetBalance())#</b>
</body>

<script language="Cache" method="GetBalance" arguments=""
        returntype="%Integer">
    // server-side method to lookup account balance
    New id
    Set id = $Get(%request.Data("ACCOUNTID",1))
    If (id = 100) {
        Quit 157
    }
    ElseIf (id = 105) {
        Quit 11987
    }
    Quit 0
</script>

</html>

当请求 list.csp 时,CSP 服务器将以下 HTML 发送到客户端:

<html>
<body>

Select an account:<br>
<a href="account.csp?CSPToken=fSVnWw0jKIs">Checking</a>
<a href="account.csp?CSPToken=1tLL6NKgysXi">Saving</a>
</body>
</html>

请注意,只有 ACCOUNTID 的加密值被发送到客户端。

在处理 account.csp 时,它会看到 ACCOUNTID 的解密值(在其 GetBalance 方法中引用)。

ENCODED=1 的示例

ENCODED=2ENCODED=1 之间的区别在于,如果 ENCODED=2,任何以未加密形式添加到 URL 的文本都会被丢弃。如果使用 ENCODED=1,则未加密的文本将传递到页面。然后,该页面可以包含指定如何处理此未加密文本的代码。

示例页面 protected.cspprotectedentry.csp显示了使用 ENCODED=1 的示例。 protected.csp 的源显示它检查是否已将任何内容添加到未加密的 URL。如果有任何未加密的内容,页面会显示一个滚动字幕,上面写着 HACKER ALERT!

要查看此内容,请转到示例页面在打开。

  1. 双击 protectedentry.csp 在新选项卡中打开。
  2. BALANCE:字段中输入 500
  3. 点击查看余额
  4. protected.csp 页面显示帐户余额为:500
  5. 请注意,该 URL 包含一个加密的 CSPTokenCSPTaken= 之后的所有内容)。这是输入的 500 加密。
  6. 导航到此 URL 的末尾并输入 &BALANCE=8000 并按 Enter
  7. 显示受保护的.csp 页面。它接受了对 URL 的未加密添加,并通过显示 HACKER ALERT!选框。如果 ENCODED 设置为 2,它将忽略未加密的条目。
0
0 96
文章 姚 鑫 · 十月 19, 2022 2m read

第二十一章 CSP Session 管理 - 身份验证和加密

身份验证和加密

在发送到 HTTP 客户端的页面上放置状态信息是很常见的。当从这些页面发出后续请求时,会将状态信息发送回服务器。很多时候,重要的是将状态信息放置在网页上,以便 a) HTTP 源的查看者无法确定状态信息的值,并且 b) 服务器可以验证返回的信息是,实际上,从同一服务器和会话发送出去。通过其加密服务,CSP 提供了一种易于使用的机制来实现这一点。

Session Key

CSP 可以使用加密密钥对服务器上的数据进行加密和解密。每个 CSP session 都有一个唯一的会话密钥(可通对象 Key 属性中的 %CSP.Session 访问),用于加密会话数据。这种机制是安全的,因为会话密钥永远不会发送到 HTTP 客户端;它作为 %CSP.Session 的一部分保留在 CSP 服务器上,位于对象中。

可以在类中使用 %CSP.PageEncrypt 方法手动加密服务器上的值。可以随后使用 Decrypt 方法解密此值。

加密 URL 和 CSPToken

在某些情况下(如下所述),从 .csp 文件生成的类会自动加密发送到客户端的 URL 值(对于手动创建的类,必须在类中调用 %CSP.PageLink 方法才能执行此操作)。

例如,假设 .csp 文件包含定义指向另一个页面的链接的锚标记:

<a href="page2.csp?PI=314159">Page 2</a>

如果此 URL 已加密,则可能会向客户端发送以下内容:

<a href="page2.csp?CSPToken=8762KJH987JLJ">Page 2</a>

当用户选择该链接时,加密参数 CSPToken 被发送到 CSP 服务器。然后服务器对其进行解密并将其解密后的内容放入 %request 对象中。如果加密值已被修改或从不同的会话发送,则服务器将引发错误。可以在类 IsEncrypted 方法中使用 %CSP.Request来确定参数值是否最初是加密的。

CSP 编译器会自动检测 HTML 文档中可能出现 URL 的所有位置,并根据需要执行加密(基于目标页面的类参数,如下节所述)。如果以编程方式创建页面,则可以在类中使用 %CSP.PageLink 方法获得相同的行为。

如果将链接作为参数传递给函数,请始终类中使用 %CSP.PageLink 方法,而不是 #url()# 指令,如下例所示:

window.showModalDialog('#(..Link("locks.csp"))#','',windowFeatures);

这个使用#url()# 作为函数参数的例子不起作用:

window.showModalDialog('#url(locks.csp)#','',windowFeatures); 

如果需要在 .csp 文件中 CSP 编译器未检测到的位置提供加密 URL,请使用 #url()# 指令。例如,在链接是参数的客户端 JavaScript 函数中,可以使用:

<script language=JavaScript>
function NextPage()
{
    // jump to next page
    CSPPage.document.location = '#url(nextpage.csp)#';
}
</script>
0
0 122
文章 姚 鑫 · 十月 19, 2022 7m read

CORS请求Request携带Cookie失败占用License解决方案

起因

  1. 因为是前后端分离的项目,前端使用的vue2,后端iris。需要获取cooikesessionid,每次请求时携带cookie,防止每次请求都占用一个license

  2. 登录认证,保持会话期间全局变量,超时退出。

现象

下图是如果不携带cookie每次请求都会新建一个session并且占用一个license

image

image

解决过程

所以基于上述情况,需要每次获取响应responsecookie,保存下来,下次请求request再携带保存下来的cookie

image

如图Set-Cookie内容:

  • CSPSESSIONID - 为SessionIDCSPSessionCookie
  • CSPWSERVERID - 服务器ID,负载均衡时会用到,如果ID不对应,则会报错。

后端方法为%CSP.ResponseWriteHTTPHeaderCookies()

Method WriteHTTPHeaderCookies()
{
	#Dim c,cookie,port,cookiepath
	s ^yx("yx","UseSessionCookie") = %session.UseSessionCookie
	s ^yx("yx","CSPSessionCookie") = %session.CSPSessionCookie
	s ^yx("yx","SecureSessionCookie") = %session.SecureSessionCookie
	If $isobject($get(%session)),%session.UseSessionCookie,i%OutputSessionToken,%session.CSPSessionCookie'="" {
		Set cookie="CSPSESSIONID",cookiepath=i%CookiePath
		If $extract(cookiepath,*)'="/" Set cookiepath=cookiepath_"/"
		If $data(%request.CgiEnvs("SERVER_PORT"),port) Set cookie=cookie_"-SP-"_port
		Set cookie=cookie_"-UP-"_$translate($extract(cookiepath,2,*),"/","-")
		Set cookie=cookie_"="_$select(i%OutputSessionToken=2:"",1:%session.CSPSessionCookie)_"; path="_$zcvt($zcvt(cookiepath,"O","UTF8"),"O","URL")_"; "_$select(%session.SecureSessionCookie:" secure;",1:"")
		if i%UseHttpOnly {
			Set cookie=cookie_" httpOnly;"
		}
		Write "Set-Cookie: "_cookie,!
		$$$SysLogTag(2,"CSPResponse","[WriteHTTPHeaderCookies] Session cookie: "_cookie,"", %request.RequestId)
		s ^yx("Set-Cookie:") = cookie
		
		Set %session.CookiePath=$get(cookiepath)
	}

	Set c=""
	For {
		Set c=$order(i%Cookies(c)) Quit:c=""
		Set name=i%Cookies(c,"n")
		Write "Set-Cookie: ",name,"=",i%Cookies(c,"v"),";"
		Write:i%Cookies(c,"e")'="" " expires=",i%Cookies(c,"e"),";"
		Write:i%Cookies(c,"p")'="" " path=",i%Cookies(c,"p"),";"
		Write:i%Cookies(c,"d")'="" " domain=",i%Cookies(c,"d"),";"
		Write:i%Cookies(c,"s") " secure;"
		Write:i%Cookies(c,"h") " httpOnly;"
		Write !
	}
}

由于cookiehttpOnly属性导致js无法获取cookie

image

去掉httpOnly属性可以获取到但是又不安全。

后端设置httpOnly=0 默认为1

ClassMethod OnPreHTTP() As %Boolean
{
	#dim %response as %CSP.Response
	s %response.UseHttpOnly = 0
	q 1
}

image

大多数XSS攻击都是针对会话cookie的盗窃。后端服务器可以通过在其创建的cookie上设置HttpOnly标志来帮助缓解此问题,这表明该cookie在客户端上不可访问。

之后修改axios的验证方式如下2种:

  1. 每次想携带cookieaxios请求,加上 withCredentials:true 的属性.
this.$axios.get("", {
        withCredentials:true,
        params: {
          ClassName: "IMP.Login.Api",
          MethodName: "QueryServers"
        }
      }).then(res => {
      
      }
  1. 修改axios默认配置, 使得每次请求默认携带cookie.
// 自动验证session 防止占用lic
axios.defaults.withCredentials = true;

第一种方式应用于大部分请求不想带cookie,只有少部分请求需要带上的情况,一般没有。第二种方式全局生效,所有的axios请求都会带上cookie,比较通用。

  1. 后端要把Access-Control-Allow-Origin的,"*"替换为HTTP-ORIGN
	#;d %response.SetHeader("Access-Control-Allow-Origin", "*")
	d %response.SetHeader("Access-Control-Allow-Origin", ##class(IMP.Common.Data).GetOrigin())
/// desc: 获取源路径
/// w ##class(IMP.Common.Data).GetOrigin().%ToJSON()
ClassMethod GetOrigin()
{
	q:'$d(%request) ""
	q:'$d(%request.CgiEnvs("HTTP_ORIGIN")) ""
	s origin = %request.CgiEnvs("HTTP_ORIGIN")
	q origin
}

image

但是谷歌浏览器最新106版本,每次请求还是不会复用session

image

image

chrome 80版本之后,谷歌把cookie的SameSite属性,从None改成了Lax。这时候,会导致cookie因为跨站而导致不会自动带上!

后端CORS请求都是设置好的。

ClassMethod OnPreHTTP() As %Boolean
{
	#dim %response as %CSP.Response
	
	#; js是否可读cookie
	s %response.UseHttpOnly = 0
	
	#; SameSite=None,配合 Secure使用,https才好使
	d %response.SetCookie("yx","yx; HttpOnly; SameSite=None")
	
	/* 星号表示所有的域都可以接受 */
	#;d %response.SetHeader("Access-Control-Allow-Origin", "*")
	d %response.SetHeader("Access-Control-Allow-Origin", ##class(IMP.Common.Data).GetOrigin())
	
	#; 允许请求方式,例如get。post
	d %response.SetHeader("Access-Control-Allow-Methods", "*")
	
	#; 允许头信息
	d %response.SetHeader("Access-Control-Allow-Headers", "x-requested-with,content-type")
	
	#; 允许验证
	d %response.SetHeader("Access-Control-Allow-Credentials", "true")
	
	#; 额外暴露头信息
	d %response.SetHeader("Access-Control-Expose-Headers", "set-cookie")
	d %response.SetHeader("Access-Control-Expose-Headers", "cookie")
	
	#; 超时时间
	d %response.SetHeader("Access-Control-Max-Age", 3600)
	d %response.SetHeader("Set-Cookie", "HttpOnly; Secure=false; SameSite=None;")
	
	/* 设置返回结构为Json */
	d %response.SetHeader("Content-Type", "application/json")
	
	q 1
}

查了一下资料发现是 Chrome 80之后版本 对 SameSite 为空时的 默认值设置变更了。

以前 None 是默认值,但最近的浏览器版本将 Lax 作为默认值, 以便对某些类型的跨站请求伪造 (CSRF) 攻击具有相当强的防御能力。

SameSite 属性有三个枚举值,分别是 strict/lax/none。Strict 最为严格,完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。Lax 规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。

设置了 Strict 或 Lax 以后,基本就杜绝了 CSRF 攻击。当然,前提是用户浏览器支持 SameSite 属性。Chrome 计划将 Lax 变为默认设置。这时,网站可以选择显式关闭 SameSite 属性,将其设为 None。不过,前提是必须同时设置 Secure 属性(Cookie 只能通过 HTTPS 协议发送),否则无效。

网上解决方案:

谷歌浏览器地址栏输入:chrome://flags/ 找到:SameSite by default cookies``、Cookies without SameSite must be secure 设置上面这两项设置成 Disable

image

或者在报文里面set-cookie,添加SameSite=None;Secure=truehost需要https

问题再一次出现,谷歌浏览器最新106版本没有此设置。无法更改。就此卡住。

之后想到用360浏览器,也是谷歌内核。发现携带cookie成功。

image

image

但是对比谷歌浏览器失败。

image

之后修改谷歌浏览器配置后成功。

image

image

多次请求后端也不会出现的session和占用lic问题。

image

image

总结

  1. 设置前端Vueaxios参数。
  2. 设置后端IRISBorkerAccess-Control-Allow-Origin为请求源。
  3. 高版本谷歌浏览器localhost请求改为ipconfigIP地址。

参考

axios获取不到cookie

axios response Set-Cookie 获取不到信息的情况,无法自动配置

关于 axios.defaults.withCredentials = true 不生效问题

HttpOnly Cookie 怎么讲

SameSite是什么

[FE] Chrome 跨域请求失败:This Set-Cookie didn't specify a "SameSite" attribute, was defaulted to "SameS...

解决新版chrome跨域问题:cookie丢失以及samesite属性问题

Chrome浏览器改变SameSite设置

0
0 426
文章 姚 鑫 · 十月 18, 2022 3m read

第二十章 CSP Session 管理 - 状态管理

状态管理

因为 HTTP 是无状态协议。为 Web 编写的应用程序必须使用特殊技术来管理应用程序上下文或状态。 CSP 提供了许多用于状态管理的机制。这些中的每一个都可能适用于特定情况。

请求之间的跟踪数据

Web 应用程序中状态管理的基本问题是跟踪连续 HTTP 请求之间的信息。有许多可用的技术,包括:

  • 使用隐藏的表单字段或 URL 参数在单个页面上存储数据
  • 将数据存储在客户端的 cookie
  • 将数据存储在服务器上的对象中的 %CSP.Session
  • Caché 数据库中存储数据

在页面中存储数据

要将状态信息存储在页面中,必须放置它以便来自该页面的后续请求包含该信息。

如果页面通过超链接发出请求,则数据应放置在超链接的 URL 中。例如,这是一个包含在 .csp 文件中定义的状态信息的超链接:

<a href="page2.csp?DATA=#(data)#">Page 2</A>

CSP 为包含此链接的页面提供服务时,表达式 #(data)# 将替换为发送给客户端的文本中的服务器变量 data 的值。当用户选择这个指向 page2.csp 的链接时,CSP 服务器可以通过 %request 对象访问 DATA 的值。如果需要,CSP 可以对此类数据进行编码。

如果页面包含表单,可以将状态信息放置在隐藏字段中:

<form>
<input type="HIDDEN" name="DATA" value="#(data)#">
<input type="SUBMIT">
</form>

与超链接示例一样,当将此表单发送到客户端时,表达式#(data)# 将替换为变量 data 的值。当用户提交此表单时,可以通过 %request 对象获得 DATA 的值。

要自动将值插入所有链接和表单,请使用 %response.Context

Cookie 中存储数据

存储状态信息的另一种技术是将其放置在 cookie 中。 cookie 是存储在客户端中的名称-值对。来自客户端的每个后续请求都包含所有先前的 cookie 值。

要设置 cookie 值,请在象中盖 %CSP.Response 中的页面 cookie 值:

Class MyApp.Page Extends %CSP.Page
{
//...

ClassMethod OnPreHTTP() As %Boolean
{
    Do %response.SetCookie("UserName",name)
     Quit 1
}
}

服务器稍后可以使用象的 Cookies 属性中的 %CSP.Request 检索此信息。

cookie 中存储信息对于希望在会话结束后记住的信息很有用。 (为此,必须设置过期日期,默认情况下,cookie 在浏览器关闭时结束。)例如,可以记住 cookie 中的用户名,以便在后续会话中他们不必重新输入此信息。有关不同类型的 cookie 及其格式的信息,请参阅 HTML 手册。

Session中存储数据——数据属性

如前一节所述,可以使用其 Data 属性将会话 %CSP.Session 中的状态信息存储在对象中。放置在 %session 对象中的任何信息都可用于当前会话的剩余部分(或直到它从 %session 对象中删除)。

%session 对象是存储在session期间有用的简单信息的好地方,例如当前用户的姓名。 %session 对象不适用于必须超出当前session范围的信息。对于依赖于用户通过应用程序采用的导航路径的信息,它也不是一个好地方。用户通常可以随意随意跳转 Web 应用程序,如果应用程序对用户采用的特定路径做出假设,这可能会导致麻烦。

在数据库中存储数据

如果有更复杂的信息要与用户关联,最好将其存储在内置的 Caché 数据库中。一种方法是在数据库中定义一个或多个持久类,并将它们的对象 ID 值存储在 %session 对象中以供后续访问。

服务器上下文保留 - 保留属性

通常,CSP 服务器从一个请求到下一个请求保留的唯一处理上下文保存在 %session 对象中。 CSP 服务器提供了一种机制来保存整个处理上下文变量、实例化对象、数据库锁、请求之间的打开设备。这称为上下文保留模式。通过在对象 Preserve 属性中设置 %CSP.Session的值,可以随时在 CSP 应用程序中打开或关闭上下文保留。请注意,将进程绑定到一个会话会导致缺乏可伸缩性。

0
0 104
文章 姚 鑫 · 十月 17, 2022 3m read

第十九章 CSP Session 管理 - %CSP.Session 对象

对象中的 %CSP.Session 包含有关当前Session的信息以及以编程方式控制会话各个方面的方法。

User Session DataData Property

可以使用其 Data 属性将 %CSP.Session 中的应用程序特定信息存储在对象中。数据是一种多维数组属性,可让关联多维数组中的特定信息。该数组的内容会在会话的整个生命周期内自动维护。

可以像使用任何其他 ObjectScript 多维数组一样在对象 Data 属性中使用 %CSP.Session

例如,如果在 OnPage 方法中执行以下代码:

 Set %session.Data("MyData") = 22

然后对同一会话的后续请求(无论哪个类处理请求)在对象中的 %CSP.Session中看到此值:

 Write $Get(%session.Data("MyData")) // this should print 22

注意:理解为全局变量。

%CSP.Session 中存储应用程序特定数据的能力是一项非常强大的功能,但应该正确使用。进一步讨论请参阅“状态管理”部分。

设置用户Session数据 - 设置命令

要在对象的 %CSP.Session 中存储数据(仅文字数据 — 不是对象引用),请使用 Set command Data 数组中的每个节点都可以包含最多 32K 个字符的字符串。

 Set %session.Data("MyData") = "hello"
 Set %session.Data("MyData",1) = 42

检索用户会话数据 - 写入命令

作为 ObjectScript 表达式的一部分,可以从 Data 属性中检索数据:

 Write %session.Data("MyData")
 Write %session.Data("MyData",1) * 5

如果引用 Data 数组中没有值的节点,则在运行时会出现 <UNDEFINED> (未定义)错误。为避免这种情况,请使用 ObjectScript $Get 函数:

 Write $Get(%session.Data(1,1,1)) // return a value or ""

删除用户会话数据——Kill 命令

要从 Data 属性中删除数据,请使用 ObjectScript Kill 命令:

 Kill %session.Data("MyData")

Session Timeout

CSP Session会自动跟踪自收到来自客户端的请求以来已经过去了多少时间。如果此经过的时间超过某个阈值,则会话自动超时。

默认情况下,会话超时设置为 900 秒(15 分钟)。可以在管理门户中更改 CSP 应用程序的此默认设置。导航到系统管理 > 安全 > 应用程序 > Web 应用程序。选择应用程序并单击编辑。还可以通过在对象 AppTimeout 属性中设置 %CSP.Session的值从应用程序中设置它:

 Set %session.AppTimeout = 3600 // set timeout to 1 hour

要禁用会话超时,请将超时值设置为 0

请注意,如果会话在其生命周期内更改 CSP 应用程序,则其超时值将不会根据会话移动到的应用程序中定义的默认超时值进行更新。例如,如果会话从 CSP 应用程序 A 开始,默认超时时间为 900 秒,然后移动到默认超时时间为 1800 秒的 CSP 应用程序 B,则会话在 900 秒后仍将超时。

如果希望应用程序更改导致会话超时更新为新应用程序的超时,请使用会话事件类,覆盖 OnApplicationChange 回调方法,并添加代码来处理 %session 对象的 AppTimeout 属性的更新。

超时通知 - OnTimeout 方法

当发生 CSP 应用程序超时时,CSP 服务器可以通过在调用指定 %CSP.SessionEventsOnTimeout 方法来通知应用程序。可以通过对象中的 %CSP.SessionEventClass 属性指定此类的名称。

默认情况下,没有定义事件类,超时只是结束当前会话。

0
0 84
文章 姚 鑫 · 十月 16, 2022 4m read

第十八章 CSP Session 管理 - 与 CSP.Session 的Sessions

`` 是一种无状态协议;每个请求都不知道以前的请求。虽然这适用于为用户提供简单静态内容的网站,但它使得开发交互式动态 Web 应用程序变得困难。为了解决这个问题,CSP 提供了所谓的Session 管理。

CSP.SessionSessions

Session 话表示在特定时间段内从特定客户端到特定应用程序的一系列请求。

CSP 自动提供会话跟踪;无需执行任何特殊操作即可启用它。 CSP 应用程序可以通过对象中的 %CSP.Session 查询和修改其会话的各个方面。 CSP 服务器通过 ObjectScript %session 变量使该对象可用。

Session 会话创建

HTTP 客户端向 CSP 应用程序发出第一个请求时,会话开始。

创建新session时,CSP 服务器会执行以下操作:

  1. 创建一个新的会话 ID 号。
  2. 酌情执行许可检查。
  3. 在对象(持久)中创建 %CSP.Session的新实例。
  4. 调用当前session事件类(如果存在)的 OnStartSession 方法。
  5. 创建一个 session-cookie 以便在会话过程中跟踪来自 HTTP 客户端的后续请求。如果客户端浏览器禁用了 cookieCSP 会自动使用 URL 重写(在每个 URL 中放置一个特殊值)来跟踪session

对于session的第一个请求,对象中的 %CSP.SessionNewSession 属性设置为 1。对于所有后续请求,它设置为 0

 If (%session.NewSession = 1) {
    // this is a new session
 }

Session ID

CSP 应用程序可以通过对象中的 %CSP.SessionSessionId 属性找到其特定的会话 ID

 Write "Session ID is: ", %session.SessionId

Session TerminationCleanup

Session因以下原因之一而结束。

  1. Session超时,因为它在指定的Session时期限内没有收到任何请求。
  2. 注销 CSP 会话的推荐方法是链接到应用程序主页,并传递包含字符串 CacheLogout=end 的 URL。这会在运行主页之前结束当前会话——释放任何获得的许可证、删除现有会话数据并删除会话的安全上下文。
  3. Session在服务器上以编程方式显式结束(通过将对象的 EndSession 属性中的 %CSP.Session 设置为 1。例如,如果客户端停止或导航到新站点,可能希望结束会话。
  4. 可以对象的 Logout 方法中使用 %CSP.Session注销会话

Session结束时,CSP 服务器会删除对象中的持久性 %CSP.Session 并减少会话许可证计数(如果适用)。如果会话由于超时或服务器操作而结束,它还会调用会话事件类的 OnEndSession 方法(如果存在)。

某些 Zen 组件,尤其是 tablePane,将临时数据存储在 ^CacheTemp.zenData 中,这通常由默认事件类自动清理。但是,如果定义自己的自定义事件类,则必须在事件类的 OnEndSession() 回调方法中显式调用 %ZEN.Controller.OnEndSession()。否则临时数据不会被清理。

保留的 CSP 参数

该表显示了保留参数及其用途。

ParameterUse
CacheUserName从登录页面,包含登录的用户名;例如, CacheUserName="fred"
CachePassword从登录页面,包含 CacheUserName 指定的用户的密码;例如, CachePassword="fredspwd".
CacheOldPassword如果使用 CacheUserNameCachePassword 传入,则它包含用户的当前密码。安全例程将用户密码更改为新值,即来自 CachePassword 的值,例如 CacheOldPassword="fredsAboutToBeChangedPwd"。更改密码后,用户使用新密码登录。
CacheRepeatPassword未使用。
CacheLogin未使用。
CacheLogout没有值或除“cookie”之外的任何值的 CacheLogout 会导致此请求的会话被注销(但不会被破坏)。注销会破坏当前的登录 Cookie 并删除为该session保留的任何两因素安全令牌. CacheLogout="cookie" 销毁当前登录 Cookie
CacheSecurityTokenCacheSecurityToken 包含从登录安全令牌页面提交的安全令牌的值,例如 CacheSecurityToken="12345678"
CacheSecuritySubmit此名称的存在表明用户正在提交一个安全令牌,其值与 CacheSecurityToken 相关联.
CacheSecurityCancel此名称的存在表示用户已取消登录安全令牌页面。
CacheLoginPage登录页面,包括自定义登录页面,包含两个子页面:一个用于登录,一个用于返回安全令牌值。该页面检查 CacheLoginPage 的值以确定要显示的子页面。 CacheLoginPage=1 表示应该显示登录子页面。
CacheNoRedirect请求了一个页面 P,但它未经身份验证,因此显示登录页面。用户从登录页面提交信息后,通常对 P的页面请求被重定向回浏览器。 (这会阻止浏览器在显示 P 之前要求用户按下<Resend> 按钮。)可以通过传递 CacheNoRedirect=1 来缩短此行为。
0
0 122
文章 姚 鑫 · 十月 15, 2022 2m read

第十七章 CSP 中的 HTTP 请求 - %CSP.Response 对象和 OnPreHTTP 方法

%CSP.Response 对象和 OnPreHTTP 方法

可以使用对象中的 %CSP.Response 控制将哪些响应标头发送回 HTTP 客户端。 CSP 服务器自动创建此类的一个实例,并将对它的引用放在变量 %response 中。

由于 %response 对象控制 HTTP 标头,因此通常在类中的 %CSP.PageOnPreHTTP 方法中设置其属性。例如,要重定向传入的 HTTP 请求,请定义以下 OnPreHTTP 方法:

Class MyApp.Page Extends %CSP.Page
{
// ...

ClassMethod OnPreHTTP() As %Boolean
{
    Set %response.ServerSideRedirect = "C:\CacheSys\csp\samples\redirect.csp"
    Quit 1
}
}

如果使用 CSP 类创建 CSP 页面,请使用以下代码设置标题值:

Class MyPage Extends %CSP.Page
{
ClassMethod OnPreHTTP() As %Boolean
{
    Do %response.SetCookie("name","value")
    Quit 1
}

如果使用 HTML 创建 CSP 页面,请使用以下代码设置标题值:

<head></head>
<script language="Cache" method="OnPreHTTP" arguments="" returntype="%Boolean"> 
    Do %response.SetCookie("name","value") 
    Quit 1
</script>

使用 SetCookie 方法提供 Cookie

可以使用 %response 对象的 SetCookie 方法将 cookie 发送到 HTTP 客户端。请参阅“在 Cookie 中存储数据”部分。

访问 Cookie

一旦 cookie 被保存。可以使用以下代码访问其值:

ClassMethod OnPage() As %Status 
{ 
Write "<body>"
Write "<p>COOKIES:</p>" 
Write "<ul>"
Set cookie=%request.NextCookie("")
While cookie'="" { 
      For count=1:1:%request.CountCookie(cookie) { 
   Write "<li>",cookie," - ",..EscapeHTML(%request.GetCookie(cookie,count)),"</li>",! 
   }
Set cookie=%request.NextCookie(cookie)
} 
Write "</ul>"
Write !,"</body>"
Quit $$$OK 
} 

[在 CSP 页面上]

<body>
<p>COOKIES:</p>
<ul>
<script language="Cache" runat="server">
Set cookie=%request.NextCookie("")
While cookie'="" {
    For count=1:1:%request.CountCookie(cookie) {
    Write "<li>",cookie," - ",..EscapeHTML(%request.GetCookie(cookie,count)),"</li>",!
    }
Set cookie=%request.NextCookie(cookie)
}
</script>
</ul>
</body>

cookie 定义可以包含到期日期和以下格式的路径:

Do %response.SetCookie("NAME","VALUE",expireData,path)

空白 expireData 字段定义内存中的 cookie。但是,如果为 expireData 字段指定一个值,这将成为一个永久 cookie,并在指定的时间被删除。 expireData 字段的格式为 Wdy,DD-Mon-YYYY HH:MM:SS GMT,例如:Wednesday, 24-Mar-2004 18:12:00 GMT

提供不同的内容类型

通常,CSP 页面提供text/html 内容。可以通过多种方式指定不同的内容类型:

  • 通过在页面类中的类参数 CONTENTTYPE 中设置 %CSP.Page 的值。
  • 通过在页面的 OnPreHTTP 方法中设置 %response 对象的 ContentType 属性的值。
0
0 81
文章 姚 鑫 · 十月 14, 2022 3m read

第十六章 CSP 中的 HTTP 请求 - %CSP.Request 对象

%CSP.Request 对象

CSP 服务器响应 HTTP 请求时,它将有关传入请求的信息打包到对象中的 %CSP.Request 实例中。可以使用变量 %request 来引用此对象。

URL 属性

要查找传入 HTTP 请求的 URL(不包括查询字符串),对象中使用 %CSP.RequestURL 属性:

 Write "URL: ", %request.URL

数据属性和 URL 参数

URL 可能包含参数列表(也称为 URL 查询)对象中的 %CSP.Request 通过其 Data 属性使这些可用。

例如,假设传入的 URL 包含:

/csp/user/MyPage.csp?A=10&a=20&B=30&B=40

可以使用以下命令在服务器上检索这些参数:

 Write %request.Data("A",1)    // this is 10
 Write %request.Data("a",1)    // this is 20
 Write %request.Data("B",1)    // this is 30
 Write %request.Data("B",2)    // this is 40

数据是一个多维属性,其中存储的每个值都有 2 个下标:参数的名称和参数的索引号(参数可以在 URL 中多次出现,如上面的 B)。请注意,参数名称区分大小写。

另请注意,传入的 HTTP 请求是 GET 还是 POST 请求并不重要:Data 属性以完全相同的方式表示参数值。

可以使用 ObjectScript $Data ($D) 函数来测试是否定义了给定的参数值:

 If ($Data(%request.Data("parm",1))) {
 }

如果您希望引用一个参数但不确定它是否已定义,可以使用 ObjectScript $Get 函数:

 Write $Get(%request.Data("parm",1))

可以在对象中使用 %CSP.RequestCount 方法找出为特定参数名称定义了多少值:

 For i = 1:1:%request.Count("parm") {
    Write %request.Data("parm",i)
 }

CgiEnvs 属性和 CGI环境变量

Web 服务器提供一组值,称为 CGI(通用网关接口)环境变量,其中包含有关 HTTP 客户端和 Web 服务器的信息。可以使用多维属性 CgiEnvs 访问这些 CGI 环境值。可以按照与 Data 属性相同的方式使用它。

例如,要确定发出 HTTP 请求的浏览器类型,请查看 CGI 环境变量 HTTP_USER_AGENT 的值:

 Write %request.CgiEnvs("HTTP_USER_AGENT")

Cookies 属性

如果 HTTP 请求包含任何 cookie,可以使用多维属性 Cookies 检索它们的值。可以按照与 Data 属性相同的方式使用它。

数据也可以保存在 %session 对象中。

请参阅 CSP 示例数据库中 cookie.csp 中使用 cookie 的示例。

MIME 数据属性

传入请求可能包含 MIME(多用途 Internet 邮件扩展)数据。这通常用于较大的信息,例如文件。可以在对象中使用 %CSP.Request 检索 MIME 数据。这将创建并返回一个 Caché 流对象的实例,然后可以使用该实例来读取 MIME 数据。

有关使用 MIME 数据的示例,请参阅 CSP 示例中的 upload.csp

0
0 117
文章 姚 鑫 · 十月 13, 2022 2m read

第十五章 CSP 中的 HTTP 请求 - 处理 CSP 错误

处理 CSP 错误

%CSP.Error是默认的 CSP 错误页面。将其用作创建的任何错误页面的超类。可以使用 %CSP.Error中提供的功能从错误中提取信息。

在授予许可证之前处理 CSP 错误

如果已经有一个现有会话并且用户尝试转到一个未找到的页面,CSP 将显示标准错误页面,因为该会话已经有一个许可证。

如果 CSP 应用程序还没有许可证,并且发生以下任何错误,则 CSP 默认显示标准 Web HTTP/1.1 404 Page Not Found 错误消息。可以通过在错误页面(通常是 %CSP.Error的子类)上为应用程序设置以下参数来更改在授予许可之前遇到错误时显示的页面。

LICENSEERRORPAGE

如果生成以下错误,CSP 会查看 LICENSEERRORPAGE 参数的值:

Cannot grant license.

LICENSEERRORPAGE 可以有以下两个值:

  • "" — 返回 HTTP/1.1 404 Page Not Found 错误(默认)

  • 静态 HTML 页面的路径 — 显示命名的静态页面,例如 /csp/samples/static.html

PAGENOTFOUNDERRORPAGE

如果生成以下任何错误,CSP 会查看 PAGENOTFOUNDERRORPAGE 参数的值:

Class does not exist
Method does not exist
CSP application does not exist (set parameter on default error page)
CSP page does not exist
File does not exist
CSP namespace does not exist
CSP illegal request
File cannot open
CSP session timeout 

PAGENOTFOUNDERRORPAGE 可以有以下三个值:

  • "" — 返回 HTTP/1.1 404 Page Not Found 错误(默认)
  • 1 — 获得许可证并显示标准错误页面。
  • 静态 HTML 页面的路径 — 显示命名的静态页面,例如 /csp/samples/static.html

OTHERSTATICERRORPAGE

如果生成任何其他错误,CSP 会查看 OTHERSTATICERRORPAGE 参数的值。

OTHERSTATICERRORPAGE 可以有以下三个值:

  • "" — 返回 HTTP/1.1 404 Page Not Found 错误(默认)
  • 1 — 输出 404 Page not found 错误,不需要许可证。
  • 静态 HTML 页面的路径 — 显示命名的静态页面,例如 /csp/samples/static.html
0
0 200
文章 姚 鑫 · 十月 12, 2022 3m read

第十四章 CSP 中的 HTTP 请求 - CSP.Page 类

CSP 服务器上,所有 HTTP 请求都通过调用由类中的 %CSP.Page 定义的方法来处理。类中的 %CSP.Page 从不直接处理请求本身;它只是定义了处理 HTTP 请求所需的接口。实际的事件处理的 %CSP.Page 的子类完成(手动创建或作为处理 CSP 源文件的结果)。

%CSP.Page 的子类永远不会被实例化;也就是说,不会在对象中创建 %CSP.Page%CSP.Page 定义的方法都是类方法,不需要对象即可调用。正如我们将看到的,这些方法所需的任何状态信息都由 CSP 服务器管理的其他对象(例如%CSP.Request 和对象中的 %CSP.Session)提供。

Page 方法

CSP 服务器确定类中的哪个 %CSP.Page 应该处理请求后,它会设置适当的处理上下文,然后调用该类的 Page 方法。设置处理上下文包括重定向标准输出设备 ($IO),以便将所有输出(使用 Write 命令)发送回 HTTP 客户端并创建任何所需对象的实例(例如 %request%response%session 对象)或局部变量。

Page 方法处理对 HTTP 请求的完整响应。它通过按顺序调用回调方法 OnPreHTTPOnPageOnPostHTTP 来做到这一点。这些被称为回调方法,因为子类可以覆盖它们以提供自定义行为。

OnPreHTTP 方法负责写出 HTTP 响应的标头。这包括内容类型和 cookie 等信息。默认行为是将内容类型设置为 text/html。在需要更直接地控制响应标头的情况下,通常只需要覆盖 OnPreHTTP 方法。

OnPage 方法执行响应 HTTP 请求的大部分工作。它负责写出请求的主体,例如 HTMLXML 文档。

例如,下面是一个包含 OnPage 方法的示例 CSP 类:

Class MyApp.Page Extends %CSP.Page
{
ClassMethod OnPage() As %Status
{
    Write "<html>",!
    Write "<body>",!
    Write "My page",!
    Write "</body>",!
    Write "</html>",!
    Quit $$$OK
}
}

提供 OnPostHTTP 方法是为了在 HTTP 请求处理完成后执行希望执行的任何操作。

%CSP.Page 类参数

类中的 %CSP.Page 包含许多类参数,可以覆盖这些参数以提供自定义行为,而无需编写代码。

如果正在以编程方式开发应用程序,则可以在创建覆盖 %CSP.Page的子类中的这些类参数(例如,使用 Studio 中的类编辑器)。

如果使用 .csp 文件创建页面,则可以使用 csp:class 标记为这些参数提供值:

<csp:class PRIVATE="1">

按资源限制页面访问

使用 SECURITYRESOURCE 参数来限制对 CSP 页面的访问。 SECURITYRESOURCE 采用逗号分隔的系统资源列表和相关权限。可以使用竖线 (|) 指定 OR 条件,使用逗号 (,) 指定 AND 条件。用户必须拥有对所有指定资源的指定权限才能查看此页面或从客户端调用任何页面的服务器端方法。

Resource[:Permission]

资源是在此系统上设置的任何资源。导航到系统管理 > 安全 > 资源以获取资源列表。

权限是 USEREADWRITE 之一。可选的;默认为USE

R1,R2|R3,R3|R4

此示例意味着用户必须拥有资源 R1 和 (R2 OR R3) 之一和 (R3 OR R4) 之一。如果用户有 R1,R3 他们可以运行页面。如果用户有 R1,R4,他们不能运行页面,因为他们不满足 R2 OR R3 条件。竖线 (|) OR 条件优先于逗号 (,) AND 条件。

0
0 126
文章 姚 鑫 · 十月 11, 2022 3m read

第十三章 CSP 中的 HTTP 请求 - CSP 服务器事件流

CSP 服务器收到来自 CSP 网关的请求时,它会确定该请求是针对静态页面还是针对 CSP 类的。如果是静态页面,它会立即将页面发回。如果是针对 CSP 类,它会执行以下操作:

  1. 确定此请求属于哪个会话。如果没有,它会启动一个新会话。
  2. 确保在正确的 Caché 命名空间中处理请求。
  3. 确保对象中正确的 %CSP.Session可用,并根据 HTTP 请求中包含的信息在对象中创建 %CSP.Request 的实例。如果需要任何解密,它也会这样做。
  4. 构造 %CSP.Response 对象以允许应用程序修改响应标头。
  5. 确定应由哪个类处理请求并调用其 Page 方法(该方法又调用 OnPage 回调方法)。

CSP 服务器 URL 和类名称解析

CSP 服务器通过解释其 URL 来确定将 HTTP 请求分派到哪个类。 CSPURL 分解为以下组件:

ComponentPurpose
http://协议
localhost网络服务器地址
[<port_no>]可选,Web 服务器正在运行的端口号;默认为端口 80
/csp/samples/目录
object.csp文件名和扩展名
?OBJID=2查询

协议和服务器地址由 Web 服务器处理,与 CSP 服务器无关。该目录用于确定 URL 引用的 CSP 应用程序。每个 Caché 配置都可以定义多个 CSP 应用程序,这些应用程序由 URL 的目录部分标识。每个 CSP 应用程序都指定了具有给定 URL 目录的所有请求使用的一些设置。其中最重要的是要在其中执行请求的 Caché 命名空间和身份验证机制,它指定可以建立与应用程序的哪种连接。要创建和修改 CSP 应用程序,请导航到管理门户上的系统管理 > 安全 > 应用程序 > Web 应用程序。

根据 CSP 应用程序,使用以下算法从文件名中确定处理请求的类的名称:

  1. 如果文件扩展名是 .cls,则使用文件名作为类名。
  2. 如果文件扩展名是.csp,则使用csp(或配置中指定的包名)作为包名,文件名作为类名来创建类名。如果此类不存在或已过期,则 CSP 编译器会从 CSP 源文件创建一个类(如果启用了自动编译)。该文件的名称和扩展名与 URL 中的相同。

例如,CSP 服务器分派此 URL

http://localhost:57772/csp/samples/menu.csp

到一个名为 menu 的类,该类包含在与目录 /csp/samples 关联的 Caché命名空间(在本例中为 SAMPLES 命名空间)中运行的包 csp 中。

如果 URL 目录 /csp/accountingCaché 命名空间 ACCOUNTING 相关联,则 CSP 服务器分派此 URL:

http://localhost:57772/csp/accounting/Ledger.csp

到一个名为 ledger 的类,该类包含在 Caché 命名空间 ACCOUNTING 中运行的包 csp 中。

请注意,与 CSP 一起使用的 URL 文件名有许多限制:

  • 它们必须是有效的 Caché 类名称(它们不能包含空格或标点字符(点和百分比字符 (%25) 除外)并且不能以数字字符开头)。
  • 它们不得与已在使用的其他类名冲突。

注意:如果 .csp 文件放置在已定义目录的子目录中,则子目录名称将成为用于页面的类中 %CSP.Page 的包名称的一部分。例如,如果 URL 目录 /csp/samples 定义为 CSP 应用程序,则 /csp/samples/myapp/page.csp 引用名为 csp.myapp.page 的类。

0
0 113
文章 姚 鑫 · 十月 10, 2022 3m read

第十二章 CSP 中的 HTTP 请求 - CSP 运行时环境

CSP 的主要任务是提供动态内容以响应传入的 HTTP(超文本传输协议)请求。本节介绍 CSP 如何处理 HTTP 请求的基础知识。

HTTP 是一种简单的协议,其中客户端向服务器发出请求。 HTTP 是一种无状态协议;客户端和服务器之间的连接只持续为服务请求所需的时间。每个 HTTP 请求都包含一个请求标头,该标头指定请求类型(例如 GETPOST)、一个 URL 和一个版本号。请求还可能包含附加信息。 CSP 自动确定它应该处理哪些 HTTP 请求,将它们分派到运行在 Caché 服务器上的适当类,并将请求信息打包成易于使用的对象(例如对象中的 %CSP.Request)。

CSP 运行时环境

下图显示了 CSPHTTP 请求的架构:

CSP 应用程序的运行时环境包括以下内容:

  • HTTP 客户端(例如 Web 浏览器)
  • HTTP 服务器(Web 服务器,例如 ApacheIIS
  • CSP 网关(Web 服务器的 Caché 插件)
  • Caché 服务器(CSP 服务器在其上运行请求的 CSP 应用程序)

HTTP 请求处理

下图说明了 CSP 处理 HTTP 请求时的事件流:

  1. 浏览器(或类似的 Web 客户端)发出 HTTP 请求。
  2. Web 服务器确定这是一个 CSP 请求并将其分派到 CSP 网关(安装在 Web 服务器上)。 2a Web 服务器可能会提供静态内容,具体取决于应用程序配置。
  3. CSP 网关重新打包请求并将其发送到正确的缓存服务器。
  4. Caché 服务器对消息进行解码并确定请求是针对静态页面还是针对 CSP 类的。

如果请求是针对静态文件(例如 .html.jpg 文件),Caché 服务器会在本地文件系统中找到该文件并将其内容发送回客户端。 (请注意,如果提供的文件包含 Unicode 文本,CSP 会使用 BOM 来确定要使用的正确编码。BOM 必须存在于 Unicode 文本文件中。)

如果是针对一个类,它会确定哪个事件处理类(应用程序的一部分)将处理该事件并调用类 Page 方法。

  1. age 方法或静态页面的输出作为 HTTP 响应发送回 CSP 网关。
  2. CSP 网关将 HTTP 响应传递给 Web 服务器(具体来说,响应通过 CSP 网关流回 Web 服务器)。
  3. Web 服务器将响应返回给 Web 浏览器,然后浏览器处理响应——在 HTML 的情况下,它会显示它。

Web 服务器和 CSP 网关

HTTP 请求在 HTTP 客户端(例如 Web 浏览器)向 Web 服务器发送消息时开始。 CSP 网关是 Web 服务器(例如 IISApache)用来处理特定类型事件的 DLL 或共享库。如果满足以下条件,CSP 网关将处理 HTTP 请求:

  • URL 的目录路径具有在 Web 服务器中定义的正确访问权限。

CSP 网关提供以下功能:

  1. 它提供最少的处理并将其大部分工作卸载到 Caché 服务器,为 Web 服务器提供更多资源。
  2. 它维护一个与命名 CSP 服务器的连接池。
  3. 它提供故障转移选项以允许使用多个互连的 CSP 服务器。

CSP 服务器

CSP 服务器是一个运行在 Caché 服务器上的进程,它专门为来自 CSP 网关的请求提供服务。每个 Caché 服务器可以根据需要运行任意数量的 CSP 服务器进程(受机器类型的限制;CSP 服务器不计入许可证计算)。

在处理无状态请求时,每个 CSP 服务器进程可以支持来自许多不同客户端的请求。在状态保持模式下,一个进程专用于处理来自一个客户端的请求,直到状态保持模式被关闭。

注意:Caché 的主要优势之一是应用程序服务器和数据服务器之间没有真正的区别。可以根据需要将应用程序配置为使用尽可能多或尽可能少的机器。这是独立于应用程序逻辑和数据库模式完成的。特定系统是应用程序服务器还是数据服务器(或两者)只是配置问题。

0
0 129
文章 姚 鑫 · 十月 8, 2022 2m read

第十章 CSP 架构 - Category

Category: AllowPrefix

如果应用程序依赖于调用以同一组字符开头的多个类或包,请使用AllowPrefix选项。

重要提示:如果应用程序依赖于调用上面列出的以外的任何类,则使用它可能是不安全的。建议确定是否需要调用此类,并为部署执行风险评估,以便了解使类可用的含义。

要使给定的Web应用程序能够调用以相同字符集开头的类或程序包,请在%sys命名空间中使用以下命令:

Set ^SYS("Security", "CSP", "AllowPrefix", "web-app-name", "prefix") = value

其中

  • Web-app-nameWeb应用程序的名称,后跟一个尾随斜杠。

要使所有Web应用程序都能使用给定的类或包,请将web-app-name指定为0;在这种情况下,可以省略括起来的引号。

  • 前缀是名称的前几个字符。

  • 值为10

如果将其指定为1,则Web应用程序可以调用这些类(或包)。

如果将其指定为0,则此Web应用程序无法调用这些类(或包)。

例如,要使/csp/webapp应用程序能够调用整个MyApp包,请使用以下命令:

Set ^SYS("Security", "CSP", "AllowPrefix", "/csp/webapps/", "MyApp.") = 1

请注意,前缀是 "MyApp.",前缀中的句点表示Web应用程序无法访问MyAppUtils等包。但是,Web应用程序可以访问包MyApp.UtilsMyApp.UnitTest

再举一个例子,要使所有应用程序都能够访问以my开头的所有包,请使用以下命令:

Set ^SYS("Security", "CSP", "AllowPrefix", 0, "My") = 1 

再举一个例子,假设 /csp/myapp 应用程序应该能够访问 %MyPkg 包中除类 %MyPkg.Class1 之外的所有类。在这种情况下,您将使用以下两个命令:

Set ^SYS("Security", "CSP", "AllowClass", "/csp/myapp/", "%MyPkg.Class1") = 0 
Set ^SYS("Security", "CSP", "AllowPrefix", "/csp/myapp/", "%MyPkg.") = 1

Category: AllowPercent

如果应用程序依赖于调用通常以 % 字符开头的包,则 AllowPercent 选项使这些类可用。

重要提示:如果应用程序依赖于调用上面列出的类以外的任何类,则使用它可能不安全。 建议确定是否需要调用此类,并为部署执行风险评估,以便您了解使该类可用的含义。

要使所有 Web 应用程序能够使用以 % 字符开头的所有包,请在 %SYS 命名空间中使用以下命令:

Set ^SYS("Security", "CSP", "AllowPercent") = 1

注意:或者使用值 0 来明确禁止任何 Web 应用程序访问这些包。

0
0 113
文章 姚 鑫 · 十月 7, 2022 4m read

第九章 CSP 架构 - CSP 应用程序设置

CSP 应用程序设置

CSP 服务器接收到传入的 HTTP 请求时,它使用本地 CSP 应用程序设置来确定如何处理请求。本节介绍 如何使用 CSP 应用程序设置处理 CSP 应用程序请求。

启用对 %CSP 页面的应用程序访问

以下规则管理对页面和类的访问:

  1. 默认情况下,允许用户应用程序访问以下页面:
  • 允许 /csp/sys/ 应用程序及其所有子应用程序的页面
  • 允许使用 isc/studio/templates//isc/studio/usertemplates/ 应用程序的页面
  1. 默认情况下,允许用户应用程序访问所有非 % 类
  2. 用户应用程序还可以访问以下类:
  • %CSP.Broker, %CSP.StreamServer, %CSP.Login, %CSP.PasswordChange, %CSP.PageLookup 被允许
  • %ZEN.SVGComponent.svgPage%ZEN.Dialog.* 是允许的,有以下附加条件
    • 不允许所有其他 %ZEN.*
    • 允许所有其他 %Z*
  • 允许所有 %z*

除了检查 CSP 应用程序中的设置外,还会检查允许的类。可以通过导航到管理门户上的系统管理 > 安全 > 应用程序 > Web 应用程序来查看和更改应用程序设置。因此,类引用必须通过两组测试才能被允许。

要允许访问其他类,请在 %SYS 命名空间中配置Global^SYS("Security","CSP","category") ,其中 categoryAllowClassAllowPrefixAllowPercent。以下部分描述了这些选项

重要提示:首先应用默认规则,然后按列出的顺序应用类别来完成检查。此外,每个关键字都可以被多次调用。这意味着可以使整个包可访问,然后限制对该包中的一个类的访问。

Background Information on the ^SYS Global

^sys global 名称空间中包含配置信息,从查看此全局的相关部分的当前内容开始会很有帮助。为此,请打开终端并切换到%sys命名空间。然后输入以下命令:

zw ^SYS("Security", "CSP")

然后,系统为每个节点显示一行,显示其当前值。例如:

%SYS>zw ^SYS("Security", "CSP")
^SYS("Security","CSP")=3
^SYS("Security","CSP","AllowClass","/api/atelier/","%Api.Atelier")=1
^SYS("Security","CSP","AllowClass","/api/deepsee/","%Api.DeepSee")=1
^SYS("Security","CSP","AllowClass","/api/document/","%Api.Document")=1
^SYS("Security","CSP","AllowClass","/csp/samples/","%CSP.UI.Portal.About")=1
^SYS("Security","CSP","AllowClass","/csp/samples/","%SOAP.WebServiceInfo")=1
^SYS("Security","CSP","AllowClass","/csp/samples/","%SOAP.WebServiceInvoke")=1
^SYS("Security","CSP","AllowPrefix","/csp/samples/","%DeepSee.")=1

Category: AllowClass

如果应用程序依赖于调用特定的类,请使用AllowClass选项使该类可用。

重要提示:如果应用程序依赖于调用任何类,而不是“启用对%CSP页的应用程序访问”部分开头列出的类,则使用它可能是不安全的。建议确定是否需要调用此类,并为部署执行风险评估,以便您了解使类可用的含义。

要使给定的Web应用程序能够调用特定类,请在%sys命名空间中使用以下命令:

Set ^SYS("Security", "CSP", "AllowClass", "web-app-name", "package.class") = value

其中

  • Web-app-nameWeb应用程序的名称,后跟一个尾随斜杠。

要使所有Web应用程序都能使用给定的类或包,请将web-app-name指定为0;在这种情况下,您可以省略括起来的引号。

  • Package.class是类的完全限定名。如果省略类,则允许指定包中的所有类。
  • 值为10

如果将其指定为1,则Web应用程序可以调用此类(或包)。

如果将其指定为0,则此Web应用程序无法调用此类(或包)。

例如,要使/csp/webapp应用程序能够使用类%User.Page,可以使用以下命令:

Set ^SYS("Security", "CSP", "AllowClass", "/csp/webapps/", "%User.Page") = 1

或者,要使所有Web应用程序都能使用%User.Page,可以使用以下命令:

Set ^SYS("Security", "CSP", "AllowClass", 0, "%User.Page") = 1 

再举一个例子,要使/csp/myapp应用程序能够使用%USER程序包中除%user.Other类以外的所有类,可以使用以下两个命令:

Set ^SYS("Security", "CSP", "AllowClass", "/csp/myapp/", "%User") = 1
Set ^SYS("Security", "CSP", "AllowClass", "/csp/myapp/", "%User.Other") = 0
0
0 150
文章 姚 鑫 · 十月 6, 2022 4m read

第八章 CSP 架构 - CSP 网关配置

CSP 网关配置

CSP 网关是安装在 Web 服务器上并由其加载的 DLL 或共享库。 CSP 网关检测对扩展名为 .csp.cls 的文件的任何请求,并将它们发送到定义的 Caché 服务器进行处理。

CSP 网关管理器

可以使用 CSP 网关管理器(CSP Web 网关管理页面)或直接编辑其配置文件 csp.ini 来配置 CSP 网关。

CSP 网关管理器是一个小型 Web 应用程序,可以在浏览器中使用它。您可以通过导航到系统管理 > 配置 > CSP 网关管理来访问 CSP 网关管理器。默认情况下,它会访问专用 Web 服务器的 CSP 网关管理器。

要访问生产 Web 服务器的 CSP 网关管理器,请根据需要通过替换 localhost 或 localhost:<port_no> 来更改 URL

有关配置 CSP 网关的信息,请导航至系统管理 > 配置 > CSP 网关管理,然后单击帮助。有关更多详细信息,请参阅 CSP 网关配置指南。

注意:CSP Web 网关管理页面的本地化仅基于安装的 CSPres.xml 的内容(如果有)。如果不存在本地化文件,则 CSP Web Gateway 管理页面默认使用嵌入的英文文本。浏览器的语言设置对此机制没有影响。

定义服务器访问

注意:为防止运行时错误,对于通过 CS 运行的高可用性配置, 建议使用启用了粘性会话支持的硬件负载平衡器。

可以定义此 CSP 网关可以访问的服务器列表(可能运行 CSP 应用程序的 CachéEnsemble 服务器)。每个服务器都有一个逻辑名称、一个 TCP/IP 地址、一个 TCP/IP 端口号(默认值为 1972)和一个启用或禁用标志。此外,可以配置与此服务器建立的最小和最大连接数以及超时和日志记录值。

由于每个服务器都有一个逻辑名称,因此 CSP 网关可以很容易地通过名称将应用程序连接到特定服务器,然后在一个地方更改服务器的特性,而无需重新配置使用服务器的每个应用程序。

初始安装后,CSP 网关定义了一个逻辑服务器 LOCAL,它被定义为连接到 Caché 的本地副本。

要添加希望 CSP 网关能够访问的一个或多个服务器,请按照上一节中的说明打开 CSP 网关管理器,然后单击服务器访问。

CSP.ini 文件中的默认 LOCAL 服务器示例:

LOCAL=Enabled
...
[LOCAL]
Ip_Address=127.0.0.1
TCP_Port=1972
Minimum_Server_Connections=3

定义应用程序访问

注意:Caché 安装会创建一个新的 /csp 配置。如果已将 /csp 配置为应用程序,那么当安装新版本的 Caché 时,配置将被覆盖。要维护应用程序配置,请输入 /csp 以外的路径。

/csp 下的任何目录都可以正常工作,例如 /csp/myapplication,但路径不能包含任何点(句点)。这些导致 CSP 网关不明确。在此示例中:/csp/samples/menu.csp/csp/aaa/bbb/ccc.clsCSP 网关可以将其解释为对 /csp/samples/menu.csp/csp/aaa/bbb/ccc 的请求.cls 或作为 /csp/samples/menu.cspREST 请求(其中 PATH_INFO/csp/aaa/bbb/ccc.cls)。在 Web 服务器环境中工作的网关无法解决这些歧义。

CSP 区分大小写。在配置 CSP 时一致地指定路径名称。

CSP 应用程序是一组使用给定 URL 寻址的页面或类。例如,所有 CSP 示例页面都是 /csp/samples 应用程序的一部分。应用程序可能包含子目录,例如 /csp/samples/cinema

CSP 网关管理器允许定义 CSP 应用程序用来连接到 Caché 服务器的 URL 路径。 CSP 将特定 URL 目录(或其子目录)中的所有文件视为同一应用程序的一部分。

默认情况下,CSP 网关定义单个应用程序路径 /csp,它将所有 CSP 请求发送到逻辑服务器 LOCAL。对 /csp/samples/csp/user 的请求都发送到本地 Caché 安装。

如果使用以 /csp 开头的 URL 创建新的 CSP 应用程序,则不必更改 CSP 网关配置。新应用程序(例如 /csp/myapp)使用为 /csp 定义的 CSP 应用程序设置。如果不希望 URL 路径以 /csp 开头,那么需要在 CSP 网关中定义一个与 URL 路径相对应的新 CSP 应用程序。

例如,要定义以 URL 路径 /myapp 开头的 CSP 应用程序,请执行以下操作:

  1. 通过在管理门户中导航到系统管理 > 配置 > CSP 网关管理来打开 CSP 网关管理器。

  2. 选择应用程序访问。

  3. 单击添加应用程序。

  4. 在应用程序路径字段中输入/myapp

  5. 从列表中选择应用程序所在的默认服务器(这些在“定义服务器访问”部分中定义)。

  6. 单击提交以保存 /myapp 应用程序访问配置。

CSP 网关参数

CSP 网关有许多可以调整的参数。其中包括超时值、故障转移和负载平衡特性以及 CGI 环境变量。

0
0 95
文章 姚 鑫 · 十月 5, 2022 3m read

第七章 CSP 架构 - URL 的 Web 服务器配置

URLWeb 服务器配置

提供了一个默认的 /csp 虚拟目录来运行 CSP 应用程序。如果正在运行多个 Caché 实例,它还提供了一个默认虚拟目录 /cacheinstance/csp 以供使用。如果正在运行多个 Caché 实例并使用虚拟目录 /csp 访问 CSP 应用程序,而之前没有 /cacheinstance,它将访问最后安装的 Caché 版本。如果计划允许使用以 http://localhost/csp 或 http//localhost/cacheinstance/csp 开头的应用程序路径调用所有 CSP 应用程序,则无需在 Web 服务器中进行任何更改配置。

如果要创建一个应用程序路径不是以 /csp/cacheinstance/csp 开头的 CSP 应用程序,则需要在 Web 服务器配置文件条目中进行更改,别名 /csp

下表显示了需要更改的配置文件。

网络服务器配置文件
ApacheHP 安全网络服务器UNIX®: /etc/httpd/conf/httpd.confWindows: <web-server-install-directory>\conf\httpd.conf
Sunconfig/obj.conf and config/magnus.conf
Microsoft IIS如“在 Microsoft IIS Web 服务器上配置新 URL”中所述定义虚拟目录。

下表显示了使用 Web 服务器和 Caché 实例的组合访问 CSP 页面的示例:

URLweb serverInstance
http://localhost/cache20071/csp/samples/menu.csppubliccache20071
http://localhost/cache52/csp/samples/menu.csppubliccache52
http://localhost/csp/samples/menu.csppublic安装的最新版本的 Caché
http://localhost:57772/csp/sys/UtilHome.cspprivate — 用于访问管理门户和在线文档使用网络服务器端口 57772 安装的 Caché 版本

注意:CSP 区分大小写。在配置 CSP 时一致地指定路径名称。

Microsoft IIS Web 服务器上配置新 URL

Microsoft IIS 是通过定义一系列虚拟目录来配置的。每个虚拟目录都包含一个名称(对应于 URL 的目录部分);物理目录(如果选择从 Web 服务器提供静态文件,则可以在其中存储静态文件(例如 .html.jpg)的本地目录);和一组权限(例如 readexecute )。

CSP 内容的任何请求 (URL) 都包含目录名称。此目录名称必须对应于 Web 服务器定义的虚拟目录或虚拟目录的子目录。此虚拟目录必须至少具有定义的读取和执行权限,才能提供 CSP 内容。

如果选择从 Web服务器提供静态文件,则 Web 服务器会在为虚拟目录定义的物理目录中查找静态文件(例如 .html.jpg)。 Web服务器和 Caché 服务器都不会在物理目录中查找 .csp 文件; .csp 文件存储在运行 Caché 服务器的机器中。如果 Web 服务器和 Caché 都在同一台机器上运行(例如,在应用程序开发期间建议),那么两者可能会巧合地在同一位置查找静态和 .csp 文件 — 这就是 Caché 配置自身和安装期间的本地 Web 服务器。

在安装过程中,会检测 IIS 服务器是否正在运行并尝试对其进行配置以定义名为 /csp 的虚拟目录。这就是对 /csp/samples/csp/user(它们是 /csp 的子目录)的请求都发送到本地 安装的方式。

如果添加新的 CSP 应用程序,如果新应用程序的 URL路径也以 /csp 开头,则无需执行任何 IIS 配置。例如,/csp/myapp 使用为 /csp 定义的 IIS 虚拟目录。如果您不希望应用程序路径以 /csp 开头,那么需要为 IIS 定义一个与应用程序路径相对应的新虚拟目录。

例如,要定义使用 URL 路径 /myappCSP 应用程序,请执行以下操作:

  1. 打开 IIS 管理器(注意:每个版本的 Windows 都有其独特的方法;通常可通过 Windows 控制面板获得)。
  2. 通过右键单击 Default Web Site 并选择 New > Virtual Directory 来定义一个名为 /myapp 的虚拟目录。
  3. 授予此目录的读取和执行权限。
  4. 如果希望 Web 服务器提供静态内容,请指定计划存储静态内容的物理目录。

还必须执行其他 CSP 网关和 CSP 服务器配置,如以下部分所述。

0
0 146
文章 姚 鑫 · 十月 3, 2022 2m read

第五章 CSP 架构 - 组件,信息流

CSP 组件:Web 服务器、CSP 网关、CSP 服务器

CSP 使用三个软件组件:Web 服务器、CSP 网关和 CSP 服务器(在 Caché 服务器上运行):

  • CSP 组件

Web服务器和CSP服务器可以由一台或多台计算机实现。在开发过程中,所有三个组件(Web 服务器、CSP 网关和 CSP 服务器)可能都位于一台 PC 上。在大规模部署中,可能有多个 Web 服务器和 CSP 服务器采用两层或三层配置。

这些组件(Web 服务器、CSP 网关、CSP 服务器)视为各有一个。它将 CSP 描述为好像只提供 HTML 页面,尽管 CSP 也可以提供 XML 页面,以及其他文本格式和二进制格式,例如图像。

每个组件的作用

Web 服务器是一个软件实用程序,它执行以下操作:

  • 接受传入的 HTTP 请求,通常来自浏览器
  • 检查权限
  • 可以提供静态内容
  • CSP 网关发送对 CSP 内容(以 .csp.cls 结尾的 URL)的请求

CSP 网关是一个共享库、一个 .dll 文件或一个 CGI 脚本。它执行以下操作:

  • 确定将请求发送到哪个 Caché 服务器。
  • 将请求发送到正确的 Caché 服务器。
  • 维护与 Caché 服务器的连接(以避免必须不断打开新连接)。

CSP 服务器是一个运行在 Caché 服务器上的进程,专门用于处理来自 CSP 网关的请求。它执行以下操作:

  • 接收应用程序的 HTTP 请求
  • 检查应用程序配置设置(在管理门户中设置并保存在 .cpf 文件中)。
  • 运行关联的类(直接编写或从 CSP 页面生成)将 HTML 发送回 CSP 网关,CSP 网关将其发送到 Web 服务器并返回到浏览器。

信息流

CSP 请求由标准 Web 服务器(支持所有领先的服务器)和标准 HTTP 协议处理。 CSP 管理 Web 服务器和 Caché 之间的通信并调用应用程序代码来生成页面。请求和返回过程如下:

  1. HTTP 客户端(通常是 Web 浏览器)使用 HTTPWeb 服务器请求页面。
  2. Web 服务器将此识别为 CSP 请求,并使用快速服务器 API 将其转发到 CSP 网关。
  3. CSP 网关确定要与之通信的 Caché 服务器,并将请求转发到该目标系统上的 CSP 服务器。
  4. Caché 中运行的 CSP 服务器处理请求并将页面返回给 CSP 网关,CSP 网关将其传递回 Web 服务器。
  5. Web 服务器将其发送到浏览器进行显示。
0
0 162
文章 姚 鑫 · 十月 2, 2022 2m read

第四章 Caché 服务器页面简介 - 通过 html 创建 csp 页面

创建基于 HTML 标记的 CSP 页面

要使用 HTML 文件创建 Hello.World 页面,请执行以下操作:

  1. 启动 Studio 并选择 File > New > CSP File > Caché Server Page
  2. 将新 CSP 文件的内容替换为以下内容:
<html>
<body>
<b>Hello, World!</b>
</body>
</html>
  1. 选择文件 > 保存。
  2. 在显示的另存为窗口中,双击默认 CSP 应用程序 csp/user
  3. 输入文件名 Hello.csp 并单击另存为以保存文件。
  4. Studio 窗口中,选择查看 > 网页。

与前面的示例一样,会看到 Hello, World!显示在浏览器中。

CSP 应用程序可以由单个 CSP 页面或一组页面组成。 CSP 应用程序作为一个单元,使用适用于整个应用程序的设置。系统提供 csp/user 作为默认的 CSP 应用程序。

还可以使用文本编辑器或 HTML 编辑器创建 HTML 文件。将此文件作为 Hello.csp 保存在本地目录 cachesys/csp/user 中(其中 cachesys 是安装 Caché 的位置)。

Hello.csp 页面的工作方式如下:

  1. 浏览器向本地 Web 服务器发送 Hello.csp 请求
  2. Web 服务器将此请求传递给 CSP 网关(连接到 Web 服务器),CSP 网关又将请求传递给 Caché CSP 服务器。
  3. Caché CSP 服务器查找文件 Hello.csp,并将其交给 CSP 编译器。
  4. CSP 编译器创建一个名为 csp.Hello 的新类,该类具有写出 Hello.csp 文件内容的 OnPage 方法。 (它实际上生成一组方法,每个方法依次从 OnPage 方法调用)。此编译步骤仅在 .csp 文件比生成的类更新时发生;随后的请求直接发送到生成的类。
  5. CSP 服务器调用新生成的 OnPage 方法,并将其输出发送到浏览器,如上例所示。

与程序开发的情况一样,这是一个出于教学原因而故意过分简化的示例。 CSP 编译器实际上是一个专门的 XML/HTML 处理引擎,它可以:

  • HTML 页面中处理服务器端脚本和表达式
  • 在识别某些 HTML 标记时执行服务器端操作。

与编程示例一样,可以通过添加编程逻辑使此页面更有趣。例如:

与编程示例一样,生成的页面显示 10 个项目的无序(项目符号)列表。

0
0 194
文章 姚 鑫 · 十月 1, 2022 2m read

第三章 Caché 服务器页面简介 - 通过 cls 创建 csp 页面

创建第一个 CSP 页面

本节介绍如何以两种不同的方式创建 Hello, World CSP 页面:

创建基于类的 CSP 页面

通过创建 %CSP.Page 的子类并覆盖其 OnPage 方法来创建 CSP 页面。通过此方法写入主设备的任何输出都会自动发送到 Web 浏览器并显示为网页。

  1. 打开Studio
  2. 选择 File > New Project 在本地数据库 USER 命名空间中创建一个新项目。
  3. 在向导的第一页上,输入 Test 作为包名,并输入 Hello 作为类名
  4. 在第二页上,选择 CSP 作为类类型。
  5. 单击完成。可以在 Studio 类编辑器中看到新的 CSP 类定义:
Class Test.Hello Extends %CSP.Page
{

ClassMethod OnPage() As %Status
{
	&html<<html>
<head>
</head>
<body>>
	; To do...
	&html<</body>
</html>>
	Quit $$$OK
}

}

  1. OnPage 方法中,替换注释:
 ; To do...

使用 Write 语句:

 Write "<b>Hello, World</b>",!
  1. 使用 Build > Compile 保存并编译新类。
  2. 选择视图 > 网页。

会在浏览器中看到 Hello, World

这个 CSP 页面也是一个 CSP 应用程序,其工作原理如下:

  1. 浏览器向指定命名空间中的本地 Web 服务器发送对 Test.Hello.cls 的请求。

  2. Web 服务器将此请求传递给 CSP 网关,CSP 网关将请求传递给 CSP 服务器。在我们的例子中,浏览器、Web 服务器和 应用程序服务器都运行在同一台机器上。在实际部署中,这些可能位于不同的机器上。

  3. CSP 服务器查找名为 Test.Hello 的类并调用其 OnPage 方法。

  4. OnPage 方法写入主体设备(使用 Write 命令)的任何输出都将发送回浏览器(通过 CSP 网关和 Web 服务器)。

这些例子展示了 CSP 的核心; CSP 的其余功能都建立在此行为之上。

以下是添加更多代码的示例。在包含 Hello, World 的行之后插入以下行:

		w "<ul>",!
		for i = 1 : 1 : 10 {
			w "<LI> This is item ", i,!
		}
		w "</ul>",!
0
0 119
文章 姚 鑫 · 九月 30, 2022 2m read

第二章 Caché 服务器页面简介 - 你应该知道

你应该知道什么

要高效使用 CSP,应该熟悉以下内容:

  • Caché objects and ObjectScript

  • HTML

  • JavaScript

  • SQL

CSP Samples

Caché 附带一组示例CSP 页面。要查看这些:

  1. 打开Caché

  2. 要查看 CSP 示例,请确保计算机上的 Web 服务器正在运行。

  3. 启动浏览器并转到 CSP 示例菜单(使用位于以下位置的私有 Web 服务器:http://localhost:57772/csp/samples/menu.csp或位于以下位置的外部 Web` 服务器:http://localhost /csp/samples/menu.csp 。

  4. 如果安装的 Caché 具有正常或锁的安全功能,则可能会显示登录页面。如果是这样,请登录。

  5. Caché 显示示例 CSP 页面的列表以及每个页面的简短描述。单击感兴趣的任何内容。

CSP 文档

可以在此处找到有关 CSP 的文档:

  • 使用 Caché Server Pages 描述了如何创建 CSP 页面
  • 使用 ZEN 描述了如何使用 ZEN,这是一个在 CSP 之上工作的包,用于使用预构建的页面对象进行更快速的 Web 应用程序开发。 Zen 文档包括使用 Zen 组件、开发 Zen 应用程序和使用 Zen 报告。
  • CSP HTML Tag Reference,对所有 CSP 标签的引用
  • CSP 示例菜单,显示了许多 CSP 页面示例。
  • Caché 服务器页面快速入门教程让入门。
  • CSP Web 应用程序教程是一个深入的教程。
  • 这些类的类参考信息
    • %CSP.Page
    • %CSP.Session

要设置或配置 CSP,请参阅以下内容:

  • CSP Web Gateway 文档是关于配置 CSP Gateway 的在线帮助,可在 CSP Web Gateway 管理页面上找到。在管理门户中,导航到系统管理 > 配置 > CSP 网关管理,然后单击帮助。默认情况下,这会将带到私有 Web 服务器。要查看生产 Web 服务器的 CSP Web 网关管理页面,请用 localhost 或localhost:<port_no> 替换 URL 中的 localhost:57772,例如:
http://localhost/csp/bin/Systems/Module.cxw
  • CSP 网关配置指南,当安装 Caché 时,CSP 网关会自动安装并适用于大多数站点。如果需要手动配置 CSP 网关,请使用高级配置指南。
0
0 175
文章 姚 鑫 · 九月 29, 2022 3m read

第一章 Caché 服务器页面简介 - 什么是CSP

Caché Server Pages (CSP) 既是用于构建交互式 CSP 应用程序的架构又是工具集。 CSP 技术允许构建和部署高性能、高度可扩展的 Web 应用程序。 CSP 允许动态生成网页,通常使用来自 Caché 数据库的数据。 “动态Dynamically”意味着每次从最近更改的数据源请求同一页面时,它可能会提供不同的内容。

CSP 用途广泛。它可以

  • 显示每分钟都在变化的库存数据。
  • 支持拥有数千名活跃用户的网络社区。
  • 根据存储在 Caché 数据库中的用户信息个性化页面。
  • 根据不同用户的要求和安全权限,根据用户数据为不同用户定制页面。
  • 提供 HTMLXML、图像或其他二进制或文本数据。
  • 提供快速性能,因为它与高性能 数据库紧密耦合。

CSP 非常适合数据库应用程序。除了提供对内置 Caché 数据库的快速访问之外,它还提供了许多基于 Web 的数据库应用程序必不可少的功能,包括

  • 会话管理
  • 页面认证
  • 能够从网页中执行交互式数据库操作。

CSP 支持两种风格的 Web 开发。

  • 为了使用类开发应用程序,CSP 提供了一个对象框架。
  • 为了使用 HTML 文件开发应用程序,CSP 提供了一种基于 HTML 的标记语言,允许在网页中包含对象和服务器端脚本。可以在一个应用程序中结合这两种技术以获得最大的灵活性。

CSP 和 Zen

Zen 是一个应用程序框架,用于快速创建包含在 CachéEnsemble 中的数据丰富的 Web 应用程序。如果正在构建新的基于 Web 的应用程序或增强现有的基于 CSP 的应用程序,请查看使用 Zen 中的 Zen 框架。

注意:要运行基于 Zen 的应用程序,必须启用“服务文件Serve Files”选项并正确配置 Web 服务器。

在你开始之前

生产 Web 服务器和 Caché 提供的专用 Web 服务器

Caché 提供运行管理门户的最小 Web 服务器,有时称为专用 Web 服务器。专用 Web 服务器还可以显示提供的 CSP 示例并运行 CSP 页面。它无法在生产环境中运行强大的 CSP 应用程序。为此,需要安装一个单独的完整安装的受支持 Web 服务器,例如 Apache Web 服务器、MicrosoftIIS Web 服务器或 Sun Web 服务器。

私有 Web 服务器基于 Apache Web 服务器的最小构建。它被配置为侦听非标准 TCP 端口,默认端口号为 57772(或另一个不是通常的、众所周知的 HTTP 服务器端口 80 的端口)。专用 Web 服务器不会干扰在同一主机上运行的任何其他 Web 服务器安装。

配置 Web 服务器和 CSP 网关

Caché 安装为常见的 Web 服务器和操作系统执行 Web 服务器和 CSP 网关配置。

安装 CachéCSP 网关后,请参阅 CSP 网关配置指南以映射系统的文件扩展名。本书还包含非典型 CSP 网关配置的配置信息。

要在远程服务器(即未运行 Caché 实例的系统)上安装 CSP 网关,可以使用两种方法之一。在远程服务器上,可以运行

  • Caché 安装脚本并选择仅安装 Web 服务器或
  • 独立的 CSPGateway 安装脚本。该脚本询问有关远程 Caché 服务器的信息:名称、地址、端口和可选密码。该脚本会根据此信息自动配置 csp.ini

安装 CSP 网关后,请参阅 CSP 网关配置指南以映射系统的文件扩展名。

注意:为防止运行时错误,对于通过 CSP 运行的高可用性配置, 建议使用启用了粘性会话支持的硬件负载平衡器。

0
0 396