# 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。**
