> For the complete documentation index, see [llms.txt](https://meetbill.gitbook.io/butterfly-user-doc/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://meetbill.gitbook.io/butterfly-user-doc/fe/butterfly-admin/sso.md).

# sso

## 1 单点登录时序图

```
                 +-----------------+  +---------------------+                     +-----------+
                 |~* /static/      |  |= /auth/verification |                     |/          |
                 |= /index_sso.html|  |= /butterfly_401     |                     |           |  Nginx 配置
                 |= /              |  |= /auth/ssologin     |                     |           |
                 +-----------------+  +---------------------+                     +-----------+
                          |                     |                                       |
+----------+      +---------------+    +--------------+        +----------+       +-----------+
|web browse|      |butterfly-admin|    |butterfly-auth|        |cas-server|       |app-backend|  服务
+----------+      +---------------+    +--------------+        +----------+       +-----------+
     |                    |                    |                     |                   |
     +-------route------->|/                   |                     |                   |
     |<-------page--------+/index_sso.html     |                     |                   |
     |                    |                    |                     |                   |
     ====================================================================not  have token
     |                    |                    |                     |                   |
     +--V----------------request api---------------------------------------------------->| 1 客户端请求后端接口返回 401
     |  +-sub request-header not have token--->|(/auth/verification) |                   |
     |<-code=401,targetURL=/auth/ssologin------+                     |                   |
     |                    |                    |                     |                   |
     +--window.location.herf=directurl-------->|(/auth/ssologin)     |                   | 2 客户端设置当前页面的 URL 地址为 /auth/ssologin
     |<----code=302,Location=cas-server--------+                     |                   |
     |                    |                    |                     |                   |
     +-----302 http://cas-server/login  login page ----------------->|(/login)           | 3 客户端根据 butterfly-auth 返回内容重定向到 cas-server
     |<-------------code=302,set Cookie TGT=xxx ---------------------+                   |
     |                    |                    |                     |                   |
     +-----302 /auth/ssologin?ticket=xxx ----->|(/auth/ssologin)     |                   | 4 客户端根据 cas-server 返回内容重定向到 /auth/ssologin
     |                    |                    +-------check st----->|(/session/validate)|
     |                    |                    |<-------st vaild-----+                   |
     |<--code=302 set Cookie butterfly_token---+                     |                   |
     |                    |                    |                     |                   |
     +--302 / ----------->|                    |                     |                   | 5 客户端重新请求首页
     |<-------page--------+/index_sso.html     |                     |                   |
     |                    |                    |                     |                   |
     ======================================================================== have token
     |                    |                    |                     |                   |
     +---V----------------request api--------------------------------------------------->|
     |   +-sub request-header have token------>|(/auth/verification) |                   |
     |<-------------------response-------------------------------------------------------+
```

## 2 butterfly-admin 关键点

### 2.1 nginx auth request

[Authentication Based on Subrequest Result（基于子请求结果的认证）](https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-subrequest-authentication/)

Nginx auth 子请求是通过 HTTP GET 方法发送的，即使原请求是 POST 请求，auth 子请求也会转为 GET 请求，GET 请求没有 body , body 被丢弃。

> nginx 配置

```
# 认证接口
location = /auth/verification {
    internal;
    proxy_pass http://127.0.0.1:8001;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    # 真实的请求路径
    proxy_set_header X-Original-URI $request_uri;
    proxy_set_header Host  $host:$server_port;
    proxy_set_header X-Real-IP  $remote_addr;
    proxy_set_header X-Real-PORT $remote_port;
    proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
}


#对 / 所有做负载均衡 + 反向代理
location / {
    auth_request        /auth/verification;
    auth_request_set    $butterfly_location $upstream_http_location;
    auth_request_set    $butterfly_username $upstream_http_username;
    error_page 401    = /butterfly_401;
    proxy_redirect off;
    proxy_set_header  X-Username        $butterfly_username;
    # 后端的 Web 服务器可以通过 X-Forwarded-For 获取用户真实 IP
    proxy_set_header Host  $host:$server_port;
    proxy_set_header  X-Real-IP  $remote_addr;
    proxy_set_header  X-Forwarded-For  $proxy_add_x_forwarded_for;
    proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
    proxy_pass        http://backend;
}

location  = /butterfly_401 {
    internal;
    default_type application/json;
    if ($butterfly_location) {
        return 401 '{"success":false,"message":"You are not authorized","data":{"Target_url":"$butterfly_location"}}';
    }
}
```

### 2.2 amis 全局适配器

```
// 全局请求适配器。参考：官网 -> 快速开始 -> 控制 amis 的行为
requestAdaptor(api) {
    api.headers["Authorization"] = "Bearer: " + Cookies.get('butterfly_token');
    //console.log("全局请求适配器", api);
    return api;
},
// 全局响应适配器。参考：官网 -> 快速开始 -> 控制 amis 的行为
responseAdaptor(api, payload, query, request, response) {
    //console.log("全局响应适配器", response);
    if (response.status == 401) {
        window.location.href = "/auth/ssologin";
        return {
        status: 401,
        msg: "请登录"
    }
  }
  return payload;
},

```

### 2.3 Token 存储方式

单点登录时，需要跳转到 butterfly-admin 单页之外进行登录，则无法使用拦截器进行拦截，故而需要使用 Cookie

```
浏览器获取 Cookie 方式:
F12，Console，在命令框内输入：document.cookie，显示返回
```

### 2.4 username 传递

作用：后端服务可以获取到 username 用来鉴权及审计等操作

{% hint style="info" %}
/xingqiao/ 前缀转发到 butterfly\_xingqiao

```
    location ^~ /xingqiao/ {
        auth_request        /auth/verification;
        auth_request_set    $butterfly_location $upstream_http_location;
        auth_request_set    $butterfly_username $upstream_http_username;
        error_page 401    = /butterfly_401;
        proxy_redirect off;
        proxy_set_header  X-Username        $butterfly_username;
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        proxy_pass        http://butterfly_xingqiao;
    }
```

{% endhint %}

**`/auth/verification` 认证完成后，返回("username", "meetbill") header**

**`auth_request` 返回的 `username` 头默认不会自动传递给 `backend`**

* Nginx 默认只会转发客户端请求的 headers，而 `auth_request` 的响应头（如 `username`）需要手动提取并传递给后端。
* 需要通过 `auth_request_set` 提取该值，然后使用 `proxy_set_header` 显式传递给 `backend`。

**后端(butterfly\_xingqiao) 就可以看到 ("HTTP\_X\_USERNAME", "meetbill") 的 header。**


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://meetbill.gitbook.io/butterfly-user-doc/fe/butterfly-admin/sso.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
