• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
黄洪波写点东西的地方
博客园    首页    新随笔    联系   管理    订阅  订阅
OpenWebUI单点登录之解决动态参数问题

我一直以为自己已经写了这一篇,直到好几位网友留言我才发现自己其实没有写。

本篇分为四章
1.动态传参失败的原因

2.解决方案

3.剩余问题

4.过程中踩的坑放最后

 

言归正传:

动态传参失败的原因:
构造登录地址:http://owl.tyl.com/?name=zxx&email=zxx@xxx.com.cn

跳转到http://owl.tyl.com/

真正问题出在从页面点击链接到后面真正的跳转登录过程中请求头没有往下带,跳转过程中参数丢了。
我是看了无数遍nginx日志才看出来的,也是官网中没有写的地方。
解决方案:
这个时候就得想办法保持会话,gpt告诉我用cookie。
于是各种折腾,想到了一个办法,既然cookie可行,那就用以下思路:
1.当发现请求地址携带参数 email 和name时,将其放置到 会话cookie中,后续所有的请求获得该cookie,并在所有请求将其放入请求头X-User-Email 和X-User-Name中,允许跨站访问,在nginx日志中将cookie的值以及X-User-Email X-User-Name打印出来
调试的过程不必细讲,最终解决方案如下:
最终解决方案如下,基本完美解决
新的逻辑如下:
1.当发现请求地址携带参数 email 和name时,将其放置到 会话cookie中,后续所有的请求获得该cookie,
2.并在请求/auth或/api/v1/auths/或/api/v1/auths/signin时将其放入请求头X-User-Email 和X-User-Name中,允许跨站访问,
3.在/api/v1/auths/signout时删除会话中存储的name和email。
4.在nginx日志中将cookie的值以及X-User-Email X-User-Name打印出来
之所以说完美解决,只剩下几个很小的问题了
a.用户a点击了登出,用户b登录后再登出,此时用户a在登录,失败,必须关掉浏览器重新登录--无需处理
b.浏览器地址栏把用户名和邮箱明文展示,--问题不大
c.官方说明:启动的时候只能选择一种模式,经测试,使用trust header的模式,第一个登录的账户被默认为管理员账户(我使用了docker模式,没有测试本地安装模式)
d.trusted header模式不会自动创建用户,得管理员自己去后台创建或导入用户
 
解决方案:
浏览器请求地址:http://owl.xyz.com/?name=zmm&email=zmm@t.c
1.配置nginx
2.自定义open webui的login页面(还有一个更6的解决方案,在单点来源的系统登录的之后,种下基于*.xyz.com的全局cookie X-User-Email X-User-Name,这样连login.html都不用写了。
)
配置文件:
server {
    listen 80;
    server_name owl.tyl.com;

    # 添加访问日志
    access_log C:/nginx-1.26.2/logs/owl.tyl.com.access.log custom_format;

    location /ws/socket.io/ {
        access_log off;  # 禁用访问日志
        proxy_pass http://127.0.0.1:3000;
        # WebSocket 特殊头
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # 其他配置
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#         proxy_set_header X-Forwarded-Proto $scheme;
        # 防止 WebSocket 超时
        proxy_read_timeout 600s;
    }


    # 静态页面
    location /login.html {
        root C:/nginx-1.26.2/html/tyl; # 静态文件存放目录
        index login.html;

        # CORS 配置,支持跨域访问
        add_header Access-Control-Allow-Origin * always;
        add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
        add_header Access-Control-Allow-Headers "Content-Type" always;
    }


    location / {
        proxy_pass http://127.0.0.1:3000;

        # 从 Cookie 提取用户信息
        set $user_email "";
        set $user_name "";

        if ($http_cookie ~* "X-User-Email=([^;]+)") {
            set $user_email $1;
        }

        if ($http_cookie ~* "X-User-Name=([^;]+)") {
            set $user_name $1;
        }

        proxy_set_header X-User-Email $user_email;
        proxy_set_header X-User-Name $user_name;

        # CORS 配置
        add_header Access-Control-Allow-Origin * always;
        add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
        add_header Access-Control-Allow-Headers "X-User-Email, X-User-Name, Content-Type" always;
#         add_header Access-Control-Max-Age 3600 always;

        if ($request_method = OPTIONS) {
            return 204;
        }

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }


    location /api/v1/auths/signout {
        # 删除 Cookie 中的 X-User-Name 和 X-User-Email
        add_header Set-Cookie "X-User-Name=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0;";
        add_header Set-Cookie "X-User-Email=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0;";

        # 代理到后端的注销逻辑
        proxy_pass http://127.0.0.1:3000;

        # 传递常见的请求头设置
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
日志格式文件:
    # 配置访问日志格式
    include       mime.types;
    default_type  application/octet-stream;
    

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  logs/access.log  main;
    
    
    # 配置访问日志格式
    log_format custom_format '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for" '
                          'Request-URI=$request_uri '
                          'X-User-Email=$arg_email '
                          'X-User-Name=$arg_name '
                          'Accept=$http_accept '
                          'Content-Type=$http_content_type '
                          'User-Agent=$http_user_agent '
                          'X-Real-IP=$http_x_real_ip '
                          'X-Forwarded-For=$http_x_forwarded_for '
                          'X-Forwarded-Proto=$http_x_forwarded_proto'
                          'cookie: "$http_cookie"';

                              
                              
    # 引用独立的配置文件
    include C:/nginx-1.26.2/conf/conf.d/*.conf;'cookie: "$http_cookie"';
自定义login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Redirect tyl</title>
    <script>
        function redirectToSubmit() {
            // 动态获取用户信息,api,
            const userEmail = "zmm@t.c";
            const userName =  "zmm";
            // 设置用户信息到 Cookie
            document.cookie = `X-User-Email=${userEmail}; Path=/;`;
            document.cookie = `X-User-Name=${userName}; Path=/;`;

            window.location.href = "http://owl.xyz.com";

    </script>
</head>
<body>
<h1>Redirect index</h1>
<button onclick="redirectToSubmit()">Go to index</button>
</body>
</html>

完毕。

 

 

插入分割线,折腾的全过程:

问题1:为啥在nginx中配置静态常量的参数,就能单点成功,变成动态从地址栏取 $arg_parameter就失败呢
location / {
    
    
        proxy_pass http://127.0.0.1:3000;  # 后端服务地址
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # 添加自定义请求头
                # 失败 ,一直报授权错误                 
                # Your provider has not provided a trusted header. Please contact your administrator for assistance.
        #proxy_set_header X-User-Email $arg_email;
        #proxy_set_header X-User-Name $arg_name;
        # 添加自定义请求头
                # 成功
        proxy_set_header X-User-Email "zmm@t.c";
        proxy_set_header X-User-Name "zmm";
        
    }
按说语法不可能有错,浏览器访问地址:http://owl.tyl.com/auth?name=xxx&email=xxx@tt.com.cn
尝试了无数次,从一开始就是卡在这个问题上,兜了一个大圈,压根儿就不是-e ENABLE_OAUTH_SIGNUP=true的事情
 
后来想办法日志打出来
1.最开始以为是语法问题,没拿到相关的变量,甚至写了个SpringBoot后端,打印日志发现没错
2.后来又想办法在nginx日志中打印出来
# 配置访问日志格式
# 配置访问日志格式
    log_format custom_format '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for" '
                          'Request-URI=$request_uri '
                          'X-User-Email=$arg_email '
                          'X-User-Name=$arg_name '
                          'Accept=$http_accept '
                          'Content-Type=$http_content_type '
                          'User-Agent=$http_user_agent '
                          'X-Real-IP=$http_x_real_ip '
                          'X-Forwarded-For=$http_x_forwarded_for '
                          'X-Forwarded-Proto=$http_x_forwarded_proto'
                  'cookie: "$http_cookie"';
发现只有零星几个日志中正常拿到了参数X-User-Name的值,大量都是 X-User-Name= -
3.这个时候仍然以为是语法错误
4.千辛万苦,日志一遍一遍看
日志就不放了

于是第一个转机出现了

识到真正问题出在从页面点击链接到后面真正的跳转过程中请求头没有往下带,跳转过程中丢了
这个时候就得想办法保持会话,gpt告诉我用cookie。
于是各种折腾,想到了一个办法,既然cookie可行,那就用以下思路:
1.当发现请求地址携带参数 email 和name时,将其放置到 会话cookie中,后续所有的请求获得该cookie,并在所有请求将其放入请求头X-User-Email 和X-User-Name中,允许跨站访问,在nginx日志中将cookie的值以及X-User-Email X-User-Name打印出来
搞出来第一个配置文件。
但是新问题又来了,这个思路下,用户没法注销或者登出啊,在一个浏览器上永远只有一个用户,除非openwebui默认的auth机制失效,那就得另想办法了。
登出时日志如下,因为按照以上逻辑,所有请求都会读取cookie,并将cookie放入header中,可以看到auth被触发了。
 
于是又各种看日志,找到了sign out相关的api,于是在该请求里面将cookie清除即可解决。
posted on 2025-03-10 22:45  红无酒伤  阅读(602)  评论(8)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3