21
2025
03
16:52:39

Open-WebUI Ubuntu环境下的开发部署及自定义登录(下)

前一篇已经大致安装部署本地的open-webui的基础环境了,我们使用deepseek过程中,可能涉及到与已有系统的账号统一登录的情况。官方文档有一些关于如何对接的文档,稍显复杂,一时半会不太能掌握,自己就试着做一点简单的修改,实现一键登录需求。


大致思如如下:

  • A系统有test@email.com 的账号,在A系统页面增加一个按钮,自动登录到B系统;

  • B(open-webui)系统,前端通过url传递参数,然后提交到B系统后端;

  • B系统后端校验参数,识别到系统也存在test@email.com的账号,然后后端带着传递的参数请求A系统,

  •  A系统校验参数合法,返回给B系统后端,

  •  B系统后端接受到返回,如果校验无误,结果返回给B系统前端

  •  B系统前端接收到后端返回结果,完成登录

大致流程如下:


作为示例,只做简单的md5校验

前端访问    http://127.0.0.1:8080/auth?token=c8da6b4e734be162&email=test@email.com

前端把 token和email两个参数传递到后端。

后端对email的md5值和token做比较完成用户的校验


这个是open-webui的标准登录页面。

我们参考这个页面的登录逻辑,做一下修改



前端部分

1 . 首先我们看下 open-webui/src/routes/auth/+page.svelte 文件

这个是前端的登录页,

关注下如下部分的代码

    const signInHandler = async () => {
        const sessionUser = await userSignIn(email, password).catch((error) => {
            toast.error(`${error}`);
            return null;
        });

        await setSessionUser(sessionUser);
    };

这个是用户登录的函数,

主要是通过userSignIn这个函数完成用的登录动作。

userSignIn函数在文件open-webui/src/lib/apis/auths/index.ts文件里实现的

export const userSignIn = async (email: string, password: string) => {
    let error = null;

    const res = await fetch(`${WEBUI_API_BASE_URL}/auths/signin`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        credentials: 'include',
        body: JSON.stringify({
            email: email,
            password: password
        })
    })
        .then(async (res) => {
            if (!res.ok) throw await res.json();
            return res.json();
        })
        .catch((err) => {
            console.log(err);

            error = err.detail;
            return null;
        });

    if (error) {
        throw error;
    }

    return res;
};

我们可以参考这部分代码,写出自己的实现如下:


在open-webui/src/routes/auth/+page.svelte文件增加如下代码

#自定义登录函数
    const mySignIn = async () => {
        const params = new URLSearchParams(window.location.search);
        const token  = params.get('token');
        const email  = params.get('email');
    
        const sessionUser = await mySignIn (email, token).catch((error) => {
            return null;
        });
        await setSessionUser(sessionUser);
    };

#从url里获取token、email两个参数,传递到函数userTokenSignIn里完成后台请求完成登录

#onMount增加我们的函数
    onMount(async () => {
                await mySignIn ();
                ......
    });


在open-webui/src/lib/apis/auths/index.ts文件参考userSignIn函数增加mySignIn 函数:

只需要修改后端请求的URL,其他部分都是照抄

export const mySignIn = async (email: string, token: string) => {
    let error = null;

    const res = await fetch(`${WEBUI_API_BASE_URL}/auths/mySignIn`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        credentials: 'include',
        body: JSON.stringify({
            email: email,
            token: token
        })
    }) .then(async (res) => {
            if (!res.ok) throw await res.json();
            return res.json();
        })
        .catch((err) => {
            error = err.detail;
            return null;
        });

    if (error) {
        throw error;
    }
    return res;
};

前端部分大部分已经完成。


后端部分

后端的登录在open-webui/backend/open_webui/routers/auths.py文件里面

首先看先之前的用户登录代码

############################
# SignIn
############################


@router.post("/signin", response_model=SessionUserResponse)
async def signin(request: Request, response: Response, form_data: SigninForm):

        ......
        user = Auths.authenticate_user(form_data.email.lower(), form_data.password)

        ......

主要是通过SigninForm接受参数,

然后通过 Auths.authenticate_user实现email和password的校验

这个部分代码在 open-webui/backend/open_webui/models/auths.py里实现

    def authenticate_user(self, email: str, password: str) -> Optional[UserModel]:
        log.info(f"authenticate_user: {email}")
        try:
            with get_db() as db:
                auth = db.query(Auth).filter_by(email=email, active=True).first()
                if auth:
                    if verify_password(password, auth.password):
                        user = Users.get_user_by_id(auth.id)
                        return user
                    else:
                        return None
                else:
                    return None
        except Exception:
            return None

我们参考这两部分代码,不需要做太大的修改就可以实现我们的需求

在open-webui/backend/open_webui/routers/auths.py增加我们的实现mySignIn函数

只需要修改用户校验部分,其他部分都原封不动的照抄signin的函数

......

from open_webui.models.auths import (
    AddUserForm,
    ApiKey,
    Auths,
    Token,
    LdapForm,
    SigninForm,
    MySigninForm, #引入自定义的表单接受
    SigninResponse,
    SignupForm,
    UpdatePasswordForm,
    UpdateProfileForm,
    UserResponse,
)

############################
# mySignIn
#form_data用的是自定义的 MySigninForm
#用户验证用的是自定义的 authenticate_user_mySignIn
############################
@router.post("/mySignIn", response_model=SessionUserResponse)

async def mySignIn(request: Request, response: Response, form_data: MySigninForm):
       ......

       user = Auths.authenticate_user_mySignIn(form_data.email.lower(), form_data.token)

       ......

#自定义的函数最后一行,错误提示,也可以自定义下,
#不改也可以,就是报错情况下,提示的是用户名或者密码错误,有点不完美
raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)

增加自定义报错在文件open-webui/backend/open_webui/constants.py中修改



open-webui/backend/open_webui/models/auths.py里实现用户的校验函数

authenticate_user_mySignIn

还需要定义一个 MySigninForm 用于接受参数

import hashlib  #需要用到计算md5
......

#自定义接受参数
class MySigninForm(BaseModel):
    email: str
    token: str
#自定义用户登录参数校验
def authenticate_user_mySignIn(self, email: str, token: str) -> Optional[UserModel]:
    try:
        with get_db() as db:
            auth = db.query(Auth).filter_by(email=email, active=True).first()
            if auth:
                #校验token,这里只简单的做md5的校验
                md5hash    = hashlib.md5(email.encode())
                md5_token  = md5hash.hexdigest()
                if token == md5_token:
                    user = Users.get_user_by_id(auth.id)
                    return user
                else:
                    return None
            else:
                return None
    except Exception:
        return None
......

至此这个简单的校验email的md5值的token登录方式就实现了

我们访问地址

http://127.0.0.1:8080/auth?token=c8da6b4e734be162&email=test@email.com

如果token是email的md5值。就可以实现用户自动登录

chrome浏览器,打开开发者工具->网络->Fetch/XHR;检查是否完成


关于修改后的部署

如果你是通过docker部署的open-webui,代码修改后需要如下操作:

前端编译后会成才一个build的目录,覆盖到docker里原先的build目录;

----------前端覆盖docker的build文件夹操作-----------

1.压缩前端编译后的build文件夹

 tar -czvf build.tar.gz ./build/

2,压缩文件复制到docker并解压缩

#复制压缩文件到docker
docker cp ./build.tar.gz open-webui:/app/backend/
#进入docker
docker exec -it  open-webui /bin/bash
#返回docker根目录、移动压缩文件到根目录
cd ../
mv ./backend/build.tar.gz ./

#备份前端
mv build build_b

#解压缩前端
tar -zxvf build.tar.gz ./




后端直接把修改的文件覆盖到docker里对应的文件;

然后重启docker。


最后总结下需要修改的几个文件

前端代码:

open-webui/src/routes/auth/+page.svelte

open-webui/src/lib/apis/auths/index.ts

后端代码:

open-webui/backend/open_webui/routers/auths.py

open-webui/backend/open_webui/models/auths.py

这4个文件修改就可以了。

---------------------------------------------------------------------------------------------

可以本地写一个python文件,用与生成email和token

def create_md5_token(email):
    md5hash   = hashlib.md5(email.encode())
    md5_token = md5hash.hexdigest()
    
    print("auth?token="+md5_token+"&email="+email)
    
    return md5_token


emails = [
    "a@test.com", 
    "b@test.com", 
    "c@test.com", 
    "d@test.com", 
    "e@test.com", 
    "f@test.com", 
]
    
for email in emails:
    create_md5_token(email)

---------------------------------------------------------------------------------------------



后续发现,前端代码的修改直接放在

open-webuib/src/routes/+layout.svelte

里实现更好,同时需要增加setSessionUser函数。


---------------------------------------------------------------------------------------------

至此,可以把这个链接集成到原有的系统,实现单点登录。

token的加密方式可以更复杂一些,加入一些其他参数




推荐本站淘宝优惠价购买喜欢的宝贝:

image.png

本文链接:https://hqyman.cn/post/9576.html 非本站原创文章欢迎转载,原创文章需保留本站地址!

分享到:
打赏





休息一下~~


« 上一篇 下一篇 »

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

请先 登录 再评论,若不是会员请先 注册

您的IP地址是: