fastapi+react实现第三方登录功能示例

本文主要是介绍fastapi+react实现第三方登录功能示例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

fastapi+react实现第三方登录功能示例

  • 介绍
      • 后端(FastAPI)
      • 前端(React)
      • 总结

介绍

推荐:一个实现各个平台OAuth2的GitHub开源项目
实现使用第三方登录功能(例如 Google、GitHub、WeChat 等)通常涉及前后端的协同工作。
以下是一个基本的实现方案,使用 FastAPI 作为后端,React 作为前端。

后端(FastAPI)

用 PostgreSQL 作为数据库,并且登录 URL 将从后端动态获取。以下是详细的实现步骤:

  1. 安装依赖

    pip install fastapi uvicorn httpx python-jose passlib bcrypt sqlalchemy psycopg2
    
  2. 配置 PostgreSQL 数据库

    from sqlalchemy import create_engine
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy.orm import sessionmakerSQLALCHEMY_DATABASE_URL = "postgresql://user:password@localhost/dbname"
    engine = create_engine(SQLALCHEMY_DATABASE_URL)
    SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
    Base = declarative_base()
    
  3. 创建 FastAPI 应用

    from fastapi import FastAPI, HTTPException, Depends, Request
    from fastapi.security import OAuth2PasswordBearer
    from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy.orm import sessionmaker, relationship
    from passlib.context import CryptContext
    from jose import JWTError, jwt
    import httpxapp = FastAPI()# 数据库配置
    SQLALCHEMY_DATABASE_URL = "postgresql://user:password@localhost/dbname"
    engine = create_engine(SQLALCHEMY_DATABASE_URL)
    SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
    Base = declarative_base()# 密码加密
    pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")# JWT 配置
    SECRET_KEY = "your-secret-key"
    ALGORITHM = "HS256"# OAuth2 配置
    oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")class User(Base):__tablename__ = "users"id = Column(Integer, primary_key=True, index=True)email = Column(String, unique=True, index=True)hashed_password = Column(String)oauth_accounts = relationship("OAuthAccount", back_populates="user")class OAuthAccount(Base):__tablename__ = "oauth_accounts"id = Column(Integer, primary_key=True, index=True)user_id = Column(Integer, ForeignKey("users.id"))provider = Column(String, index=True)provider_id = Column(String, index=True)user = relationship("User", back_populates="oauth_accounts")Base.metadata.create_all(bind=engine)def get_db():db = SessionLocal()try:yield dbfinally:db.close()async def get_current_user(token: str = Depends(oauth2_scheme)):credentials_exception = HTTPException(status_code=401, detail="Could not validate credentials")try:payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])email: str = payload.get("sub")if email is None:raise credentials_exceptionexcept JWTError:raise credentials_exceptiondb = next(get_db())user = db.query(User).filter(User.email == email).first()if user is None:raise credentials_exceptionreturn user@app.post("/token")
    async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):db = next(get_db())user = authenticate_user(db, form_data.username, form_data.password)if not user:raise HTTPException(status_code=400, detail="Incorrect username or password")access_token = create_access_token(data={"sub": user.email})return {"access_token": access_token, "token_type": "bearer"}def authenticate_user(db, email: str, password: str):user = db.query(User).filter(User.email == email).first()if not user:return Falseif not verify_password(password, user.hashed_password):return Falsereturn userdef verify_password(plain_password, hashed_password):return pwd_context.verify(plain_password, hashed_password)def create_access_token(data: dict):to_encode = data.copy()encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)return encoded_jwt@app.get("/users/me")
    async def read_users_me(current_user: User = Depends(get_current_user)):return current_user@app.get("/oauth/{provider}/login-url")
    async def get_oauth_login_url(provider: str):redirect_uri = "http://localhost:3000/oauth/{provider}/callback"if provider == "google":return {"url": f"https://accounts.google.com/o/oauth2/v2/auth?client_id=your-google-client-id&redirect_uri={redirect_uri}&response_type=code&scope=email profile"}elif provider == "github":return {"url": f"https://github.com/login/oauth/authorize?client_id=your-github-client-id&redirect_uri={redirect_uri}&scope=user:email"}elif provider == "wechat":return {"url": f"https://open.weixin.qq.com/connect/qrconnect?appid=your-wechat-app-id&redirect_uri={redirect_uri}&response_type=code&scope=snsapi_login"}else:raise HTTPException(status_code=404, detail="Provider not found")@app.get("/oauth/{provider}/callback")
    async def oauth_callback(provider: str, request: Request):code = request.query_params.get("code")if not code:raise HTTPException(status_code=400, detail="Missing code")# 获取 access_tokenasync with httpx.AsyncClient() as client:response = await client.post(f"https://{provider}.com/oauth/token", data={"client_id": "your-client-id","client_secret": "your-client-secret","code": code,"redirect_uri": "your-redirect-uri"})token_data = response.json()access_token = token_data.get("access_token")# 获取用户信息async with httpx.AsyncClient() as client:response = await client.get(f"https://{provider}.com/user", headers={"Authorization": f"Bearer {access_token}"})user_data = response.json()db = next(get_db())oauth_account = db.query(OAuthAccount).filter(OAuthAccount.provider == provider, OAuthAccount.provider_id == user_data["id"]).first()if oauth_account:user = oauth_account.userelse:user = db.query(User).filter(User.email == user_data["email"]).first()if not user:user = User(email=user_data["email"], hashed_password=pwd_context.hash("default_password"))db.add(user)db.commit()db.refresh(user)oauth_account = OAuthAccount(provider=provider, provider_id=user_data["id"], user=user)db.add(oauth_account)db.commit()db.refresh(oauth_account)access_token = create_access_token(data={"sub": user.email})return {"access_token": access_token, "token_type": "bearer"}
    

前端(React)

  1. 安装依赖

    npm install axios react-router-dom
    
  2. 创建 React 组件

    import React from 'react';
    import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom';
    import axios from 'axios';const Login = () => {const [loginUrls, setLoginUrls] = React.useState({});React.useEffect(() => {const fetchLoginUrls = async () => {const providers = ['google', 'github', 'wechat'];const urls = {};for (const provider of providers) {const response = await axios.get(`/oauth/${provider}/login-url`);urls[provider] = response.data.url;}setLoginUrls(urls);};fetchLoginUrls();}, []);const handleLogin = (provider) => {window.location.href = loginUrls[provider];};return (<div><button onClick={() => handleLogin('google')}>Login with Google</button><button onClick={() => handleLogin('github')}>Login with GitHub</button><button onClick={() => handleLogin('wechat')}>Login with WeChat</button></div>);
    };const Callback = ({ match }) => {const { provider } = match.params;React.useEffect(() => {const params = new URLSearchParams(window.location.search);const code = params.get('code');axios.get(`/oauth/${provider}/callback?code=${code}`).then(response => {const { access_token } = response.data;localStorage.setItem('access_token', access_token);window.location.href = '/profile';}).catch(error => {console.error(error);});}, [provider]);return <div>Loading...</div>;
    };const Profile = () => {const [user, setUser] = React.useState(null);React.useEffect(() => {const access_token = localStorage.getItem('access_token');axios.get('/users/me', { headers: { Authorization: `Bearer ${access_token}` } }).then(response => {setUser(response.data);}).catch(error => {console.error(error);});}, []);if (!user) return <div>Loading...</div>;return (<div><h1>Profile</h1><p>Email: {user.email}</p></div>);
    };const App = () => {return (<Router><nav><Link to="/">Home</Link><Link to="/profile">Profile</Link></nav><Switch><Route path="/" exact component={Login} /><Route path="/oauth/:provider/callback" component={Callback} /><Route path="/profile" component={Profile} /></Switch></Router>);
    };export default App;
    

总结

  1. 后端:使用 FastAPI 处理 OAuth2 回调,获取用户信息,并将其与现有用户关联或创建新用户。同时,提供动态获取登录 URL 的接口。
  2. 前端:使用 React 处理登录按钮和回调逻辑,从后端动态获取登录 URL,并将获取的 access_token 存储在 localStorage 中,并在用户访问个人资料页面时使用该 access_token 获取用户信息。

这篇关于fastapi+react实现第三方登录功能示例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1096237

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

部署Vue项目到服务器后404错误的原因及解决方案

《部署Vue项目到服务器后404错误的原因及解决方案》文章介绍了Vue项目部署步骤以及404错误的解决方案,部署步骤包括构建项目、上传文件、配置Web服务器、重启Nginx和访问域名,404错误通常是... 目录一、vue项目部署步骤二、404错误原因及解决方案错误场景原因分析解决方案一、Vue项目部署步骤

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

在 Spring Boot 中使用 @Autowired和 @Bean注解的示例详解

《在SpringBoot中使用@Autowired和@Bean注解的示例详解》本文通过一个示例演示了如何在SpringBoot中使用@Autowired和@Bean注解进行依赖注入和Bean... 目录在 Spring Boot 中使用 @Autowired 和 @Bean 注解示例背景1. 定义 Stud

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque