本教程介绍如何实现基于 Django REST 的身份验证系统(通过Django REST Framework)并将其与前端的Auth.js(以前称为 NextAuth.js)集成。它涵盖了基于凭据的身份验证以及 Google 的社交身份验证。
介绍
Django 的默认身份验证系统对于全栈 Django 项目来说是一个出色的开箱即用解决方案。但在使用 Django REST Framework 用于客户端-服务端架构时,利用 Django 的内置身份验证可能相当困难。最重要的是,Django 本身并不支持社交身份验证。
幸运的是,您可以将 Django 的身份验证系统公开为 API,并使用社区开发的django-allauth和dj-rest-auth软件包添加社交身份验证支持。一旦您的身份验证系统公开,从前端连接到它就相对容易了。
虽然基于凭据的身份验证很容易在前端实现,但添加社交身份验证时会出现问题。每个社交提供商都是不同的,单独为每个社交提供商添加支持可能非常耗时。
因此,出现了Auth.js,它是 Next.js、SvelteKit 和 SolidStart 的标准化开源身份验证解决方案。该库提供基于凭据的身份验证、无密码身份验证、社交身份验证等。它还支持 60 多个社交提供商。
在本教程中,我们将了解如何实现基于 Django REST 的身份验证系统并从Next.js前端连接到它。在本教程结束时,您将拥有一个支持社交身份验证的完整可用的身份验证系统。我们将首先处理后端,然后再处理前端。
实现的解决方案具有以下身份验证流程:
目标
学完本教程后,您将能够:
- 使用 Django REST Framework、django-allauth 和 dj-rest-auth 实现基于 Django REST 的身份验证系统。
- 通过 djangorestframework-simplejwt 启用并配置 JSON Web Token 身份验证。
- 设置 django-cors-headers 以避免从前端连接时出现 CORS 问题。
- 设置 Auth.js 以支持基于凭据的身份验证以及社交身份验证。
- 将基于 Django REST 的身份验证系统与 Auth.js 连接。
- 通过 Google 添加社交身份验证。
后端
在本节中,我们将创建一个新的 Django 项目,启动一个专用的身份验证应用程序,安装和配置所有依赖项,并通过cURL测试身份验证系统。
Django 设置
首先为您的项目和虚拟环境创建一个新目录:
$ mkdir django-rest-authjs && cd django-rest-authjs $ python3.11 -m venv env $ source env/bin/activate (env)$
接下来,安装 Django 并创建一个新项目:
(env)$ pip install Django==4.2.3 (env)$ django-admin startproject core .
迁移数据库并运行服务器:
(env)$ python manage.py migrate (env)$ python manage.py runserver
最后,打开您最喜欢的网络浏览器并导航到http://localhost:8000。您应该能够看到默认的 Django 登录页面。
认证应用程序
为了使我们的项目更有组织性,我们创建一个专门的应用程序进行身份验证。该应用程序将包括身份验证视图和 URL。
创建一个新应用程序:
(env)$ python manage.py startapp authentication
将其添加到core/settings.py的INSTALLED_APPS
中:
# core/settings.py INSTALLED_APPS = [ # ... "authentication.apps.AuthenticationConfig", ]
在“authentication”文件夹中创建urls.py文件:
# authentication/urls.py from django.urls import path from . import views urlpatterns = [ # URLs will come here ]
注册:
# core/urls.py from django.contrib import admin from django.urls import path, include urlpatterns = [ path('api/auth/', include('authentication.urls')), path("admin/", admin.site.urls), ]
这就是初始设置。
基于凭证的身份验证
在本节中,我们将设置Django REST Framework、django-allauth和dj-rest-auth以启用基于凭据的身份验证。此外,我们将设置django-cors-headers以避免从前端连接时出现 CORS 问题。
Django REST 框架
首先安装 Django REST Framework:
(env)$ pip install djangorestframework==3.14.0
接下来,导航到core/settings.py并将其和authtoken
一起添加到INSTALLED_APPS
:
# core/settings.py INSTALLED_APPS = [ # ... "rest_framework", "rest_framework.authtoken", ]
该authtoken
应用程序是必需的,因为我们将使用基于令牌的身份验证而不是 Django 的默认SessionAuthentication。令牌身份验证更适合客户端-服务端架构。
我们可以选择两种基于令牌的身份验证系统:
建议您通过djangorestframework-simplejwt插件使用 JWT 身份验证。JWT 方法更好,因为它更安全、更容易处理数据库,并且具有标准化的 HTTP 响应。
要安装 Simple JWT 插件,请运行:
$(env) pip install djangorestframework-simplejwt==5.2.2
接下来,将其添加到INSTALLED_APPS
中的rest_framework.authtoken
下面:
# core/settings.py INSTALLED_APPS = [ # ... "rest_framework.authtoken", "rest_framework_simplejwt", ]
然后,将以下设置添加到core/settings.py的底部:
# core/settings.py SIMPLE_JWT = { "ACCESS_TOKEN_LIFETIME": timedelta(minutes=60), "REFRESH_TOKEN_LIFETIME": timedelta(days=7), "ROTATE_REFRESH_TOKENS": False, "BLACKLIST_AFTER_ROTATION": False, "UPDATE_LAST_LOGIN": True, "SIGNING_KEY": "complexsigningkey", # generate a key and replace me "ALGORITHM": "HS512", }
请根据您的需要随意修改这些设置。要了解每个变量的作用,请查看官方文档。
不要忘记导入:
from datetime import timedelta
最后,更改默认的 DRF 身份验证类别:
# core/settings.py REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": [ "rest_framework_simplejwt.authentication.JWTAuthentication", ] }
Django-allauth
首先,安装 django-allauth:
(env)$ pip install django-allauth==0.52.0
接下来,导航到settings.py,将其与account
和socialaccount
一起添加到INSTALLED_APPS
:
# core/settings.py INSTALLED_APPS = [ "django.contrib.sites", # make sure 'django.contrib.sites' is installed # ... "allauth", "allauth.account", "allauth.socialaccount", # add if you want social authentication ]
django-allauth 依赖于Django 的“sites”框架,因此请确保已安装它。最重要的是,请确保您设置了SITE_ID
:
# core/settings.py SITE_ID = 1 # make sure SITE_ID is set
接下来,您应该关闭电子邮件验证功能:
# core/settings.py ACCOUNT_EMAIL_REQUIRED = False ACCOUNT_EMAIL_VERIFICATION = "none"
在此步骤中应关闭电子邮件验证,因为我们的 Django 项目未配置为发送电子邮件。最重要的是,我们没有允许用户验证其电子邮件地址的端点。本教程末尾有更多相关内容。
最后,再次迁移数据库:
(env)$ python manage.py migrate
dj-rest-auth
首先安装 dj-rest-auth 包:
(env)$ pip install "dj-rest-auth[with_social]==4.0.0"
我们需要使用with_social
说明符,因为我们想要启用标准注册过程。此外,稍后当我们启用社交身份验证时,我们将使用此包。
接下来,导航到core/settings.py将其添加到INSTALLED_APPS
:
# core/settings.py INSTALLED_APPS = [ # ... "dj_rest_auth", "dj_rest_auth.registration", ]
然后将以下配置设置添加到settings.py:
# core/settings.py REST_AUTH = { "USE_JWT": True, "JWT_AUTH_HTTPONLY": False, }
USE_JWT
是必需的,因为我们使用的是djangorestframework-simplejwt。JWT_AUTH_HTTPONLY
应该关闭,否则 dj-rest-auth 将不会发送刷新令牌。
对于所有REST_AUTH
设置,请查看官方文档。
在撰写本文时,官方安装指南告诉您注册dj_rest_auth.urls
. 我不建议您这样做,因为如果配置不正确(例如密码重置、电子邮件验证),默认 URL 会包含损坏的 URL。
相反,请查看dj-rest-auth源代码并精心挑选您需要的 URL:
像这样更新authentication/urls.py:
# authentication/urls.py from dj_rest_auth.jwt_auth import get_refresh_view from dj_rest_auth.registration.views import RegisterView from dj_rest_auth.views import LoginView, LogoutView, UserDetailsView from django.urls import path from rest_framework_simplejwt.views import TokenVerifyView urlpatterns = [ path("register/", RegisterView.as_view(), name="rest_register"), path("login/", LoginView.as_view(), name="rest_login"), path("logout/", LogoutView.as_view(), name="rest_logout"), path("user/", UserDetailsView.as_view(), name="rest_user_details"), path("token/verify/", TokenVerifyView.as_view(), name="token_verify"), path("token/refresh/", get_refresh_view().as_view(), name="token_refresh"), ]
django-cors-headers
我们需要安装和配置的最后一个包是django-cors-headers。当我们的前端与后端通信时,该包将设置正确的 CORS 标头。
首先,通过运行以下命令来安装它:
$(env) pip install django-cors-headers==4.1.0
将其添加到core/settings.py的INSTALLED_APPS
中:
# core/settings.py INSTALLED_APPS = [ # ... "corsheaders", ]
将CorsMiddleware
添加到MIDDLEWARE
列表中:
# core/settings.py MIDDLEWARE = [ # ... "corsheaders.middleware.CorsMiddleware", "django.middleware.common.CommonMiddleware", # ... ]
您应该把CorsMiddleware
放到尽可能前面的位置。特别是在任何可以生成响应的中间件(例如 Django 的CommonMiddleware
或 Whitenoise的WhiteNoiseMiddleware
)之前。
最后,将以下设置添加到core/settings.py的底部:
# core/settings.py if DEBUG: CORS_ALLOW_ALL_ORIGINS = True else: CORS_ALLOWED_ORIGINS = [ "http://localhost:3000/", "http://127.0.0.1:3000/", ]
太棒了!就是这样。
您现在拥有了一个完全可运行的基于凭据的身份验证系统,该系统使用 JSON Web 令牌。
测试
为了测试 API,我们将使用cURL。我们还将把响应通过管道传输到jq中,以自动格式化它们并用颜色突出显示它们。
首先运行 Django 开发服务:
(env)$ python manage.py runserver
注册
首先,创建一个用户:
$ curl -XPOST -H "Content-type: application/json" -d '{ "username": "user1", "password1": "complexpassword123", "password2": "complexpassword123" }' 'http://localhost:8000/api/auth/register/' | jq
您应该得到类似的响应:
{ "access": "eyJhb...", "refresh": "eyJhb...", "user": { "pk": 1, "username": "user1", "email": "" } }
您现在可以使用access
令牌来验证自己的身份。access
令牌在core/settings.py中指定的时间内有效,然后必须使用refresh
令牌刷新它。
登录
以新创建的用户身份登录:
$ curl -XPOST -H "Content-type: application/json" -d '{ "username": "user1", "password": "complexpassword123" }' 'http://localhost:8000/api/auth/login/' | jq
响应将与此类似:
{ "access": "eyJhb...", "refresh": "eyJhb...", "user": { "pk": 1, "username": "user1", "email": "" } }
用户详细信息
通过获取到的access
令牌获取用户详细信息:
$ curl -XGET -H 'Authorization: Bearer <your_access_token>' \ -H "Content-type: application/json" 'http://localhost:8000/api/auth/user/' | jq
响应:
{ "pk": 1, "username": "user1", "email": "" }
令牌验证
要验证令牌是否仍然有效,您可以将其传递给/api/auth/token/verify/
如下:
$ curl -XPOST -H "Content-type: application/json" -d '{ "token": "<your_access_token>" }' 'http://localhost:8000/api/auth/token/verify/' | jq
响应:
{}
状态码:
200
表示令牌仍然有效。4xx
意味着令牌不再有效。
令牌刷新
要获取刷新的access
令牌,您必须像下面这样使用refresh
令牌:
$ curl -XPOST -H "Content-type: application/json" -d '{ "refresh": "<your_refresh_token>" }' 'http://localhost:8000/api/auth/token/refresh/' | jq
响应:
{ "access": "eyJhb...", "access_expiration": "2023-07-11T17:42:22.480739Z" }
响应将包含新access
令牌和到期时间。
注销
要注销用户,请尝试发送以下请求:
$ curl -XPOST -H 'Authorization: Bearer <your_access-token>' \ -H "Content-type: application/json" 'http://localhost:8000/api/auth/logout/' | jq
响应:
{ "detail": "Neither cookies or blacklist are enabled. Delete the token on client." }
由于我们禁用了 cookie 并且没有配置黑名单,因此该端点不会执行任何操作。注销用户时无需调用它。您唯一要做的就是从客户端删除令牌。
如果您想要更安全的方法,请通过JWT Blacklist 应用程序设置黑名单。
通过 Google 进行社交身份验证
在本节中,我们将了解如何设置社交身份验证。将指导您通过 Google 设置社交身份验证,但您也可以对任何其他社交提供商使用类似的步骤。
设置
要启用 Google 注册,您首先需要创建 OAuth 客户端密钥。导航到您的Google Cloud Console,选择您要使用的项目,然后搜索“API Credentials”:
接下来,单击“创建凭据”按钮并在下拉列表中选择“OAuth 客户端 ID”:
选择“Web 应用程序”,选择自定义名称,然后添加您的前端 URL 作为授权重定向 URI。为了更容易测试,我建议您还添加:
- http://127.0.0.1:3000
- http://127.0.0.1:3000/api/auth/callback/google
- https://developers.google.com/oauthplayground
最后,单击“创建”以生成凭据:
您将看到您的“客户端 ID”和“客户端密码”。记下它们,因为我们在下一步中需要它们。
接下来,返回您的 Django 项目并将以下应用程序添加到INSTALLED_APPS
:
# core/settings.py INSTALLED_APPS = [ # ... "allauth.socialaccount.providers.google", ]
接下来,将SOCIALACCOUNT_PROVIDERS
设置添加到core/settings.py中,如下所示:
# core/settings.py SOCIALACCOUNT_PROVIDERS = { "google": { "APP": { "client_id": "<your google client id>", # replace me "secret": "<your google secret>", # replace me "key": "", # leave empty }, "SCOPE": [ "profile", "email", ], "AUTH_PARAMS": { "access_type": "online", }, "VERIFIED_EMAIL": True, }, }
确保将<your google client id>
和<your google secret>
替换为您的实际钥匙。将key
属性留空,因为 Google 社交身份验证不需要该属性。
在authentication/views.py中添加一个继承自SocialLoginView
的新视图:
# authentication/views.py from dj_rest_auth.registration.views import SocialLoginView from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter from allauth.socialaccount.providers.oauth2.client import OAuth2Client class GoogleLogin(SocialLoginView): adapter_class = GoogleOAuth2Adapter callback_url = "http://127.0.0.1:3000/" client_class = OAuth2Client
最后,将以下 URL 添加到authentication/urls.py:
# authentication/urls.py from allauth.socialaccount.views import signup from authentication.views import GoogleLogin urlpatterns = [ # ... path("google/", GoogleLogin.as_view(), name="google_login"), ]
不要忘记导入:
from authentication.views import GoogleLogin
测试
要测试 API 端点是否工作,您首先需要获取id_token
并将其 POST 到后端的端点。最简单的获取方法id_token
是通过Google 的 OAuth 2.0 Playground。
要获得令牌,您可以关注这篇精彩文章。
接下来,将id_token
作为access_token
在 cURL 请求正文中传递:
$ curl -XPOST -H "Content-type: application/json" -d '{ "access_token": "<your_id_token>" }' 'http://localhost:8000/api/auth/google/'
您应该得到类似的响应:
{ "access": "eyJhb...", "refresh": "eyJhb...", "user": { "pk": 2, "username": "user2", "email": "" } }
您现在可以使用access
令牌来验证自己的身份。
前端
在本节中,我们将创建 Next.js 项目、设置Chakra UI、安装和配置 Auth.js,并启用基于凭据的社交身份验证。最后,我们还将测试身份验证系统。
Web 应用程序将具有以下端点:
/
提示用户登录。/profile
显示用户的帐户信息。/api/auth/...
由 Auth.js 动态 API 路由器处理。
Next.js 设置
首先使用create-next-app
生成 Next.js 项目:
$ npx create-next-app@13.4.9 django-rest-authjs-frontend √ Would you like to use TypeScript? ... Yes √ Would you like to use ESLint? ... Yes √ Would you like to use Tailwind CSS? ... No √ Would you like to use `src/` directory? ... No √ Would you like to use App Router? (recommended) ... No √ Would you like to customize the default import alias? ... No Creating a new Next.js app in ~\django-rest-authjs-frontend.
为了简单起见,我建议您只对 TypeScript 和 ESLint 说“是”。如果您更喜欢 TailwindCSS 而不是 Chakra UI,您也可以选择使用 TailwindCSS。
Chakra 用户界面设置
为了简化 UI 构建过程,我们将引入 Chakra UI——一个简约、模块化且可访问的组件库,用于快速构建 React UI。该库具有内置组件、专用挂钩、样式系统等等。
通过运行以下命令来安装它:
$ npm i @chakra-ui/react@2.5.5 @chakra-ui/next-js@2.1.4 @emotion/react@11.10.6 \ @emotion/styled@11.10.6 framer-motion@10.12.4
接下来,通过extendTheme()
创建一个新主题,并在pages/_app.tsx中用ChakraProvider
将Component
包装起来,如下所示:
// pages/_app.tsx import type {AppProps} from "next/app"; import {ChakraProvider, extendTheme} from "@chakra-ui/react"; export const theme = extendTheme({}); export default function App({Component, pageProps: {session, ...pageProps}}: AppProps) { return ( <ChakraProvider theme={theme}> <Component {...pageProps} /> </ChakraProvider> ); }
不要忘记导入:
import {ChakraProvider, extendTheme} from "@chakra-ui/react";
我们创建了一个新主题,因为我们需要在下一步中将其传递给颜色模式脚本。此外,定义这样的主题可以让您将来轻松自定义 Chakra。
接下来,让我们处理颜色模式脚本。
颜色模式脚本确保本地存储同步正常工作。它必须添加在任何内容之前(在我们的例子中,在pages/_document.tsx文件中)。
像下面这样将ColorModeScript
包含在您的pages/_document.tsx中:
// pages/_document.tsx import {Head, Html, Main, NextScript} from "next/document"; import {ColorModeScript} from "@chakra-ui/react"; import {theme} from "./_app"; export default function Document() { return ( <Html lang="en"> <Head/> <body> <ColorModeScript initialColorMode={theme.config.initialColorMode}/> <Main/> <NextScript/> </body> </Html> ); }
再次强调,不要忘记导入:
import {ColorModeScript} from "@chakra-ui/react"; import {theme} from "./_app";
视图
让我们用登录提示替换默认的 Next.js 首页。
导航到pages/index.tsx并将其内容替换为以下内容:
// pages/index.tsx import {Box, Button, Spinner, Text, VStack} from "@chakra-ui/react"; export default function Home() { return ( <Box m={8}> <VStack> <Text>You are not authenticated.</Text> <Button colorScheme="blue" onClick={() => console.log("Beep boop")}> Sign in </Button> </VStack> </Box> ); }
启动开发服务:
$ npx next dev
打开您最喜欢的网络浏览器并导航至http://127.0.0.1:3000/。您应该会看到一个带有“您未经身份验证”消息和登录按钮的页面。单击该按钮应将调试消息打印到控制台。
很好,这就是初始设置。
基于凭证的身份验证
在本节中,我们将安装 Auth.js 并处理基于凭据的身份验证。
Axios
在使用 Auth.js 之前,让我们安装 Axios——一个适用于浏览器和 Node.js 的简单的基于 Promise 的 HTTP 客户端。这个库将允许我们使用优雅而简洁的语法向后端发送 HTTP 请求。
通过 NPM 安装:
$ npm install axios@1.3.6
Auth.js
接下来,安装 Auth.js:
$ npm install next-auth@4.22.1
为了让 Auth.js 正常工作,我们首先必须设置一些环境变量。在 Next.js 中,环境变量是从项目根目录的.env.local自动加载的。
创建包含以下内容的.env.local:
# .env.local NEXTAUTH_URL=http://127.0.0.1:3000/ NEXTAUTH_SECRET=verycomplexsecretkey NEXTAUTH_BACKEND_URL=http://127.0.0.1:8000/api/ NEXT_PUBLIC_BACKEND_URL=http://127.0.0.1:8000/api/
接下来,我们必须创建一个动态路由处理程序和 Auth.js 配置文件夹。在“pages/api”文件夹中创建一个“auth”文件夹,并在新创建的文件夹中创建一个[...nextauth].js文件。
pages/ ├── api/ │ └── auth/ │ └── [...nextauth].js ├── _app.tsx ├── _document.tsx └── index.tsx
在编写代码之前,我们先澄清一下 Auth.js 会话的工作原理。Auth.js 带有两种处理会话的内置方法:
- 使用数据库(会话存储在数据库中)
- 使用 JWT 令牌(会话存储在令牌中)
由于我们使用 Django 作为后端,因此我们必须采用第二种方法。这种方法的缺点是在 JWT 令牌中传递和存储accessToken
、refreshToken
和其他敏感变量的繁琐参数。
将以下内容粘贴到[...nextauth].js中:
// pages/api/auth/[...nextauth].js import NextAuth from "next-auth"; import CredentialsProvider from "next-auth/providers/credentials"; import axios from "axios"; // These two values should be a bit less than actual token lifetimes const BACKEND_ACCESS_TOKEN_LIFETIME = 45 * 60; // 45 minutes const BACKEND_REFRESH_TOKEN_LIFETIME = 6 * 24 * 60 * 60; // 6 days const getCurrentEpochTime = () => { return Math.floor(new Date().getTime() / 1000); }; const SIGN_IN_HANDLERS = { "credentials": async (user, account, profile, email, credentials) => { return true; }, }; const SIGN_IN_PROVIDERS = Object.keys(SIGN_IN_HANDLERS); export const authOptions = { secret: process.env.AUTH_SECRET, session: { strategy: "jwt", maxAge: BACKEND_REFRESH_TOKEN_LIFETIME, }, providers: [ CredentialsProvider({ name: "Credentials", credentials: { username: {label: "Username", type: "text"}, password: {label: "Password", type: "password"} }, // The data returned from this function is passed forward as the // `user` variable to the signIn() and jwt() callback async authorize(credentials, req) { try { const response = await axios({ url: process.env.NEXTAUTH_BACKEND_URL + "auth/login/", method: "post", data: credentials, }); const data = response.data; if (data) return data; } catch (error) { console.error(error); } return null; }, }), ], callbacks: { async signIn({user, account, profile, email, credentials}) { if (!SIGN_IN_PROVIDERS.includes(account.provider)) return false; return SIGN_IN_HANDLERS[account.provider]( user, account, profile, email, credentials ); }, async jwt({user, token, account}) { // If `user` and `account` are set that means it is a login event if (user && account) { let backendResponse = account.provider === "credentials" ? user : account.meta; token["user"] = backendResponse.user; token["access_token"] = backendResponse.access; token["refresh_token"] = backendResponse.refresh; token["ref"] = getCurrentEpochTime() + BACKEND_ACCESS_TOKEN_LIFETIME; return token; } // Refresh the backend token if necessary if (getCurrentEpochTime() > token["ref"]) { const response = await axios({ method: "post", url: process.env.NEXTAUTH_BACKEND_URL + "auth/token/refresh/", data: { refresh: token["refresh_token"], }, }); token["access_token"] = response.data.access; token["refresh_token"] = response.data.refresh; token["ref"] = getCurrentEpochTime() + BACKEND_ACCESS_TOKEN_LIFETIME; } return token; }, // Since we're using Django as the backend we have to pass the JWT // token to the client instead of the `session`. async session({token}) { return token; }, } }; export default NextAuth(authOptions);
- 我们首先定义了后端令牌生命周期和获取 UNIX 时间的方法。
- 登录处理程序将仅与社交身份验证一起使用。当使用基于凭据的身份验证时,验证已在
authorize()
方法中执行。 - 然后我们定义了
authOptions
, 并添加CredentialsProvider
到它的providers
中。 - 在
CredentialsProvider
中,我们定义了登录表单应包含哪些字段,并定义了将凭据转发到后端以验证它们的authorize()
方法。 - 然后,我们使用
signIn()
、jwt()
和session()
回调将数据附加到 JWT 令牌并将其传递给客户端。jwt()
回调中实现了后端token刷新。
要了解有关 Auth.js 回调的更多信息,请查看Auth.js 回调。
最后,将您的应用程序在pages/_app.tsx中用SessionProvider
包装起来,如下所示:
// pages/_app.tsx import type {AppProps} from "next/app"; import {SessionProvider} from "next-auth/react"; import {ChakraProvider, extendTheme} from "@chakra-ui/react"; export const theme = extendTheme({}); export default function App({Component, pageProps: {session, ...pageProps}}: AppProps) { return ( <SessionProvider session={session}> <ChakraProvider theme={theme}> <Component {...pageProps} /> </ChakraProvider> </SessionProvider> ); }
不要忘记导入:
import {SessionProvider} from "next-auth/react";
SessionProvider
将确保您可以通过useSession()
挂钩访问会话。
要注入Session
类型,请在项目根目录中创建一个名为“types”的新文件夹。然后将名为next-auth.d.ts的文件添加到其中。
视图
继续,让我们研究视图。
首先,像这样修改pages/index.tsx:
// pages/index.tsx import {useRouter} from "next/router"; import {signIn, useSession} from "next-auth/react"; import {Box, Button, Spinner, Text, VStack} from "@chakra-ui/react"; export default function Home() { const router = useRouter(); const {data: session, status} = useSession(); if (status == "loading") { return <Spinner size="lg"/>; } // If the user is authenticated redirect to `/profile` if (session) { router.push("profile"); return; } return ( <Box m={8}> <VStack> <Text>You are not authenticated.</Text> <Button colorScheme="blue" onClick={() => signIn(undefined, {callbackUrl: "/profile"})} > Sign in </Button> </VStack> </Box> ); }
- 我们使用
useSession()
钩子从SessionProvider
获取会话。 - 加载会话时,会向用户呈现该
Spinner
组件。 - 如果用户已经登录,我们会通过
useRouter()
重定向到/profile
。 - 登录按钮现在调用
signIn()
方法。
接下来,在“pages”文件夹中创建一个名为profile.tsx的新文件,其中包含以下内容:
// pages/profile.tsx import {useState} from "react"; import {signOut, useSession} from "next-auth/react"; import {Box, Button, Code, HStack, Spinner, Text, VStack} from "@chakra-ui/react"; import axios from "axios"; export default function Home() { const {data: session, status} = useSession({required: true}); const [response, setResponse] = useState("{}"); const getUserDetails = async (useToken: boolean) => { try { const response = await axios({ method: "get", url: process.env.NEXT_PUBLIC_BACKEND_URL + "auth/user/", headers: useToken ? {Authorization: "Bearer " + session?.access_token} : {}, }); setResponse(JSON.stringify(response.data)); } catch (error) { setResponse(error.message); } }; if (status == "loading") { return <Spinner size="lg"/>; } if (session) { return ( <Box m={8}> <VStack> <Text>PK: {session.user.pk}</Text> <Text>Username: {session.user.username}</Text> <Text>Email: {session.user.email || "Not provided"}</Text> <Code> {response} </Code> </VStack> <HStack justifyContent="center" mt={4}> <Button colorScheme="blue" onClick={() => getUserDetails(true)}> User details (with token) </Button> <Button colorScheme="orange" onClick={() => getUserDetails(false)}> User details (without token) </Button> <Button colorScheme="red" onClick={() => signOut({callbackUrl: "/"})}> Sign out </Button> </HStack> </Box> ); } return <></>; }
- 我们使用
useRouter()
anduseSession()
的方式与前面的代码片段相同。 - 我们添加了
getUserDetails()
方法获取用户信息并显示它。 - 单击注销按钮,调用
signOut()
方法。
测试
重新启动 Next 开发服务:
$ npx next dev
然后打开您最喜欢的网络浏览器并导航到http://127.0.0.1:3000。单击“登录”按钮查看您是否被重定向到登录表单。填写表格并单击“使用凭据登录”按钮。
如果登录成功,您将被重定向到个人资料页面。尝试使用您的accessToken
获取用户信息。
通过 Google 进行社交身份验证
在本节中,我们将使用 Google 设置社交身份验证。将其他社交提供商与 Auth.js 集成实际上是类似的。只需确保您的 Django 后端可以正常工作,然后再转到前端部分。
设置
首先,将以下两个环境变量添加到.env.local:
# .env.local # ... GOOGLE_CLIENT_ID=<your_google_client_id> GOOGLE_CLIENT_SECRET=<your_google_secret>
确保将<your_google_client_id>
和<your_google_secret>
替换为您的实际钥匙。请记住,密钥必须与core/settings.py中提供的密钥匹配。
接下来,在[...nextauth].js中注册 Google 登录处理程序:
// pages/api/auth/[...nextauth].js const SIGN_IN_HANDLERS = { // ... "google": async (user, account, profile, email, credentials) => { try { const response = await axios({ method: "post", url: process.env.NEXTAUTH_BACKEND_URL + "auth/google/", data: { access_token: account["id_token"] }, }); account["meta"] = response.data; return true; } catch (error) { console.error(error); return false; } } };
该处理程序的唯一目的是将用户的id_token
转发到 Django 社交端点。然后 Django 会处理其他所有事情——例如,创建帐户、让用户登录等。
然后将GoogleProvider
添加到[...nextauth].js中的providers
数组底部:
// pages/api/auth/[...nextauth].js export const authOptions = { // ... providers: [ // ... GoogleProvider({ clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, authorization: { params: { prompt: "consent", access_type: "offline", response_type: "code" } } }), ], // ... };
不要忘记在文件顶部导入GoogleProvider
:
import GoogleProvider from "next-auth/providers/google";
这就是我们所要做的。
测试
在测试之前,请确保您的 Django 后端正在运行并且可以通过.env.local中定义的 URL 进行访问。之后,终止 Next 开发服务器进程并再次启动它:
$ npx next dev
在您喜欢的网络浏览器中导航至http://127.0.0.1:3000/,然后单击“登录”按钮。选择“使用 Google 登录”并测试社交身份验证是否有效。
结论
在本教程中,我们成功实现了 Django REST 身份验证系统并将其与 Auth.js 链接。实施的系统支持基于凭据的身份验证以及 Google 的社交身份验证。它的构建方式使得可以轻松添加其他社交提供商,例如 Twitter、GitHub 等。
源代码可在django-rest-authjs GitHub 存储库中获取。
未来的步骤
- 确保为社交提供商实施刷新令牌轮换。
- 默认情况下,Auth.js 不负责帐户创建。如果您希望启用用户注册,请在
authorize()
方法中处理它或创建专用的注册页面。 - 要启用电子邮件验证和密码重置功能,请查看Django REST Framework 身份验证文章的“电子邮件验证和密码重置”部分。
- 要启用社交帐户链接功能,请查看“社交连接视图”。