SSO项目参考:一款基于.NET Core的认证授权解决方案-葫芦藤1.0开源啦
本文仅提供后端接口实现,不包含前端实现
流程分析
1、用户访问dify,前端服务判断用户身份信息(是否存在dify token,dify token是否过期)。
2、用户身份无效,将页面导航到SSO系统登录页面,用户登录后,重定向到dify前端页面,该页面请求SSO系统获取用户信息接口,拿到SSO用户信息。
3、调用dify后端提供的定制登录接口,获取dify token。(该接口内部自动创建用户和租户绑定关系,最终返回dify token)
最终实现效果
SSO单点登录(授权码登录)
拿到access_token后获取SSO用户信息
调用自定义dify登录接口获取dify token
自定义登录接口的实现
这里邮箱我们使用163邮箱,格式 手机号@163.com
密码使用用户id,如果用sso登录用不上,dify默认登录页面可用
dify/api/services/account_service.py
创建用户:get_create_account
创建租户成员:create_normal_tenant_member
class AccountService:
@staticmethod
def get_create_account(
id:str,
email: str,
name: str,
interface_language: str,
password: Optional[str] = None,
interface_theme: str = "light"
) -> Account:
"""get create account"""
account = db.session.query(Account).filter_by(id=id).first()
if not account:
account = Account()
account.email = email
account.name = name
if password:
# generate password salt
salt = secrets.token_bytes(16)
base64_salt = base64.b64encode(salt).decode()
# encrypt password with salt
password_hashed = hash_password(password, salt)
base64_password_hashed = base64.b64encode(password_hashed).decode()
account.password = base64_password_hashed
account.password_salt = base64_salt
account.interface_language = interface_language
account.interface_theme = interface_theme
# Set timezone based on language
account.timezone = language_timezone_mapping.get(interface_language, "UTC")
account.id = id
db.session.add(account)
db.session.commit()
return account
return account
class TenantService:
@staticmethod
def create_normal_tenant_member(tenant_id: str, account_id: str) -> TenantAccountJoin:
"""Create normal tenant member"""
role="normal"
ta = db.session.query(TenantAccountJoin).filter_by(tenant_id=tenant_id, account_id=account_id).first()
if not ta:
ta = TenantAccountJoin(tenant_id=tenant_id, account_id=account_id, role=role)
db.session.add(ta)
db.session.commit()
return ta
通过Fiddler查看 /console/api/workspaces的请求响应包,得到默认租户id
dify/api/controllers/console/auth/login.py
class SsoUserLoginApi(Resource):
@setup_required
def post(self):
parser = reqparse.RequestParser()
parser.add_argument("user_id", type=str, required=True, location="json")
parser.add_argument("mobile", type=str, required=True, location="json")
parser.add_argument("language", type=str, required=False, location="json")
args = parser.parse_args()
if args["language"] is None:
language = "zh-Hans"
user_id = args["user_id"]
mobile = args["mobile"]
tenant_id="0ac2808e-b7eb-4a74-88f2-26817af9717a"
account = AccountService.get_create_account(user_id, mobile+"@163.com", mobile,language, user_id)
TenantService.create_normal_tenant_member(tenant_id, account.id)
token_pair = AccountService.login(account, ip_address=extract_remote_ip(request))
return {"result": "success", "data": token_pair.model_dump()}
api.add_resource(SsoUserLoginApi, "/sso-login")
上传代码文件打包发版
sudo -s
cd /usr/local/dify/api/services/
rm account_service.py
rz
cd /usr/local/dify/api/controllers/console/auth/
rm login.py
rz
cd /usr/local/dify/
docker build -t langgenius/dify-api:1.0.1-xxx ./api
cd /usr/local/dify/docker/
docker compose down
rm docker-compose.yaml
rz
docker compose up -d
http://xxx/console/api/sso-login
Content-Type: application/json
{
"user_id":"628xxxxx-xxxx-xxxx-xxxx-xxxx10e2b0",
"mobile":"xxxx"
}