diff --git a/backend/.env.example b/backend/.env.example index 6b93f068..03e90cc3 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -26,6 +26,8 @@ CELERY_RABBITMQ_PASSWORD='guest' # [ Plugin ] oauth2 OAUTH2_GITHUB_CLIENT_ID='test' OAUTH2_GITHUB_CLIENT_SECRET='test' +OAUTH2_GOOGLE_CLIENT_ID='test' +OAUTH2_GOOGLE_CLIENT_SECRET='test' OAUTH2_LINUX_DO_CLIENT_ID='test' OAUTH2_LINUX_DO_CLIENT_SECRET='test' # [ Plugin ] email diff --git a/backend/common/enums.py b/backend/common/enums.py index 9cfaa3af..b01d8ac1 100644 --- a/backend/common/enums.py +++ b/backend/common/enums.py @@ -113,6 +113,7 @@ class UserSocialType(StrEnum): """用户社交类型""" github = 'GitHub' + google = 'Google' linux_do = 'LinuxDo' diff --git a/backend/core/conf.py b/backend/core/conf.py index 9d40bcae..b8df6c74 100644 --- a/backend/core/conf.py +++ b/backend/core/conf.py @@ -223,6 +223,8 @@ class Settings(BaseSettings): # .env OAUTH2_GITHUB_CLIENT_ID: str OAUTH2_GITHUB_CLIENT_SECRET: str + OAUTH2_GOOGLE_CLIENT_ID: str + OAUTH2_GOOGLE_CLIENT_SECRET: str OAUTH2_LINUX_DO_CLIENT_ID: str OAUTH2_LINUX_DO_CLIENT_SECRET: str diff --git a/backend/plugin/oauth2/api/router.py b/backend/plugin/oauth2/api/router.py index 1fff32a9..52fda6b1 100644 --- a/backend/plugin/oauth2/api/router.py +++ b/backend/plugin/oauth2/api/router.py @@ -4,9 +4,11 @@ from fastapi import APIRouter from backend.core.conf import settings from backend.plugin.oauth2.api.v1.github import router as github_router +from backend.plugin.oauth2.api.v1.google import router as google_router from backend.plugin.oauth2.api.v1.linux_do import router as linux_do_router v1 = APIRouter(prefix=f'{settings.FASTAPI_API_V1_PATH}/oauth2') v1.include_router(github_router, prefix='/github', tags=['Github OAuth2']) +v1.include_router(google_router, prefix='/google', tags=['Google OAuth2']) v1.include_router(linux_do_router, prefix='/linux-do', tags=['LinuxDo OAuth2']) diff --git a/backend/plugin/oauth2/api/v1/github.py b/backend/plugin/oauth2/api/v1/github.py index 33b0a794..10eb33b2 100644 --- a/backend/plugin/oauth2/api/v1/github.py +++ b/backend/plugin/oauth2/api/v1/github.py @@ -12,13 +12,12 @@ from backend.plugin.oauth2.service.oauth2_service import oauth2_service router = APIRouter() -_github_client = GitHubOAuth20(settings.OAUTH2_GITHUB_CLIENT_ID, settings.OAUTH2_GITHUB_CLIENT_SECRET) -_github_oauth2 = FastAPIOAuth20(_github_client, redirect_route_name='github_oauth2_callback') +github_client = GitHubOAuth20(settings.OAUTH2_GITHUB_CLIENT_ID, settings.OAUTH2_GITHUB_CLIENT_SECRET) @router.get('', summary='获取 Github 授权链接') async def get_github_oauth2_url(request: Request) -> ResponseSchemaModel[str]: - auth_url = await _github_client.get_authorization_url(redirect_uri=f'{request.url}/callback') + auth_url = await github_client.get_authorization_url(redirect_uri=f'{request.url}/callback') return response_base.success(data=auth_url) @@ -32,11 +31,11 @@ async def github_oauth2_callback( request: Request, response: Response, background_tasks: BackgroundTasks, - oauth2: FastAPIOAuth20 = Depends(_github_oauth2), + oauth2: FastAPIOAuth20 = Depends(FastAPIOAuth20(github_client, redirect_route_name='github_oauth2_callback')), ): token, _state = oauth2 access_token = token['access_token'] - user = await _github_client.get_userinfo(access_token) + user = await github_client.get_userinfo(access_token) data = await oauth2_service.create_with_login( request=request, response=response, diff --git a/backend/plugin/oauth2/api/v1/google.py b/backend/plugin/oauth2/api/v1/google.py new file mode 100644 index 00000000..db8c36d5 --- /dev/null +++ b/backend/plugin/oauth2/api/v1/google.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from fastapi import APIRouter, BackgroundTasks, Depends, Request, Response +from fastapi_limiter.depends import RateLimiter +from fastapi_oauth20 import FastAPIOAuth20, GoogleOAuth20 +from starlette.responses import RedirectResponse + +from backend.common.enums import UserSocialType +from backend.common.response.response_schema import ResponseSchemaModel, response_base +from backend.core.conf import settings +from backend.plugin.oauth2.service.oauth2_service import oauth2_service + +router = APIRouter() + +google_client = GoogleOAuth20(settings.OAUTH2_GOOGLE_CLIENT_ID, settings.OAUTH2_GOOGLE_CLIENT_SECRET) + + +@router.get('', summary='获取 google 授权链接') +async def get_google_oauth2_url(request: Request) -> ResponseSchemaModel[str]: + auth_url = await google_client.get_authorization_url(redirect_uri=f'{request.url}/callback') + return response_base.success(data=auth_url) + + +@router.get( + '/callback', + summary='google 授权自动重定向', + description='google 授权后,自动重定向到当前地址并获取用户信息,通过用户信息自动创建系统用户', + dependencies=[Depends(RateLimiter(times=5, minutes=1))], +) +async def google_oauth2_callback( + request: Request, + response: Response, + background_tasks: BackgroundTasks, + oauth2: FastAPIOAuth20 = Depends(FastAPIOAuth20(google_client, redirect_route_name='google_oauth2_callback')), +): + token, _state = oauth2 + access_token = token['access_token'] + user = await google_client.get_userinfo(access_token) + data = await oauth2_service.create_with_login( + request=request, + response=response, + background_tasks=background_tasks, + user=user, + social=UserSocialType.google, + ) + return RedirectResponse( + url=f'{settings.OAUTH2_FRONTEND_REDIRECT_URI}?access_token={data.access_token}&session_uuid={data.session_uuid}' + ) diff --git a/backend/plugin/oauth2/api/v1/linux_do.py b/backend/plugin/oauth2/api/v1/linux_do.py index c4f8014b..b654bdd9 100644 --- a/backend/plugin/oauth2/api/v1/linux_do.py +++ b/backend/plugin/oauth2/api/v1/linux_do.py @@ -12,16 +12,12 @@ from backend.plugin.oauth2.service.oauth2_service import oauth2_service router = APIRouter() -_linux_do_client = LinuxDoOAuth20( - settings.OAUTH2_LINUX_DO_CLIENT_ID, - settings.OAUTH2_LINUX_DO_CLIENT_SECRET, -) -_linux_do_oauth2 = FastAPIOAuth20(_linux_do_client, redirect_route_name='linux_do_oauth2_callback') +linux_do_client = LinuxDoOAuth20(settings.OAUTH2_LINUX_DO_CLIENT_ID, settings.OAUTH2_LINUX_DO_CLIENT_SECRET) @router.get('', summary='获取 LinuxDo 授权链接') async def get_linux_do_oauth2_url(request: Request) -> ResponseSchemaModel[str]: - auth_url = await _linux_do_client.get_authorization_url(redirect_uri=f'{request.url}/callback') + auth_url = await linux_do_client.get_authorization_url(redirect_uri=f'{request.url}/callback') return response_base.success(data=auth_url) @@ -35,11 +31,11 @@ async def linux_do_oauth2_callback( request: Request, response: Response, background_tasks: BackgroundTasks, - oauth2: FastAPIOAuth20 = Depends(_linux_do_oauth2), + oauth2: FastAPIOAuth20 = Depends(FastAPIOAuth20(linux_do_client, redirect_route_name='linux_do_oauth2_callback')), ): token, _state = oauth2 access_token = token['access_token'] - user = await _linux_do_client.get_userinfo(access_token) + user = await linux_do_client.get_userinfo(access_token) data = await oauth2_service.create_with_login( request=request, response=response, diff --git a/backend/plugin/oauth2/model/user_social.py b/backend/plugin/oauth2/model/user_social.py index c01a46ae..31d359ba 100644 --- a/backend/plugin/oauth2/model/user_social.py +++ b/backend/plugin/oauth2/model/user_social.py @@ -19,7 +19,7 @@ class UserSocial(Base): __tablename__ = 'sys_user_social' id: Mapped[id_key] = mapped_column(init=False) - sid: Mapped[str] = mapped_column(String(20), comment='第三方用户 ID') + sid: Mapped[str] = mapped_column(String(255), comment='第三方用户 ID') source: Mapped[str] = mapped_column(String(20), comment='第三方用户来源') # 用户社交信息一对多 diff --git a/backend/plugin/oauth2/plugin.toml b/backend/plugin/oauth2/plugin.toml index 597a8ed3..60818669 100644 --- a/backend/plugin/oauth2/plugin.toml +++ b/backend/plugin/oauth2/plugin.toml @@ -1,6 +1,6 @@ [plugin] summary = 'OAuth 2.0' -version = '0.0.4' +version = '0.0.5' description = '通过 OAuth 2.0 的方式登录系统' author = 'wu-clan' diff --git a/backend/plugin/oauth2/service/oauth2_service.py b/backend/plugin/oauth2/service/oauth2_service.py index aba6dcb9..f93af02b 100644 --- a/backend/plugin/oauth2/service/oauth2_service.py +++ b/backend/plugin/oauth2/service/oauth2_service.py @@ -54,6 +54,12 @@ class OAuth2Service: username = user.get('login') nickname = user.get('name') + if social == UserSocialType.google: + sid = user.get('id') + username = user.get('name') + nickname = user.get('given_name') + avatar = user.get('picture') + if social == UserSocialType.linux_do: sid = user.get('id') nickname = user.get('name') diff --git a/deploy/backend/docker-compose/.env.server b/deploy/backend/docker-compose/.env.server index 0cc83211..9ea1d26f 100644 --- a/deploy/backend/docker-compose/.env.server +++ b/deploy/backend/docker-compose/.env.server @@ -26,6 +26,8 @@ CELERY_RABBITMQ_PASSWORD='guest' # [ Plugin ] oauth2 OAUTH2_GITHUB_CLIENT_ID='test' OAUTH2_GITHUB_CLIENT_SECRET='test' +OAUTH2_GOOGLE_CLIENT_ID='test' +OAUTH2_GOOGLE_CLIENT_SECRET='test' OAUTH2_LINUX_DO_CLIENT_ID='test' OAUTH2_LINUX_DO_CLIENT_SECRET='test' # [ Plugin ] email