使用Djoser和Django进行React社交认证
Djoser是一个强大的认证库。它提供了注册、账户激活、登录、密码重置和注销等功能。
简介
Djoser提供了社交认证,这将是本文的主要焦点。我们将用Django后端和React前端创建一个处理谷歌社交认证的应用程序。
前提条件
读者应该具备这些先决条件才能跟上本文的进度。
- 在你的机器上安装了Python。
- 安装了
django,djangorestframework,djangorestframework-simplejwt,djoser,social-auth-app-django,django-cors-headers。你可以使用pip来安装它们。 - 为我们的前端部分安装好
npm。
开始使用
首先创建一个新的目录,然后在这个目录下运行django-admin startapp backend ,创建一个新的Django应用程序,名为backend 。
Djoser 在Django应用程序中使用自定义用户模型,所以让我们在项目中创建一个新的应用程序来包含自定义用户模型。浏览后台文件夹并运行 。python3 manage.py startapp users
我们需要扩展Django自带的默认用户模型。在users 文件夹中,编辑models.py ,如图所示。
users/models.py
from django.contrib.auth.models import BaseUserManager
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.db import models
# Create your models here.
class UserManager(BaseUserManager):
def create_user(self, email, password=None, **kwargs):
if not email:
raise ValueError("Users must have an email address")
email = self.normalize_email(email)
user = self.model(email=email, **kwargs)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, password=None, **kwargs):
kwargs.setdefault('is_active', True)
kwargs.setdefault('is_staff', True)
kwargs.setdefault('is_superuser', True)
if kwargs.get('is_active') is not True:
raise ValueError('Superuser must be active')
if kwargs.get('is_staff') is not True:
raise ValueError('Superuser must be staff')
if kwargs.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True')
return self.create_user(email, password, **kwargs)
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=255, unique=True)
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
is_active = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name']
def get_full_name(self):
return f"{self.first_name}{self.last_name}"
def get_short_name(self):
return self.first_name
def __str__(self):
return self.email
我们已经创建了一个新的用户模型,并指定用户名字段为电子邮件地址。我们还创建了一个UserManager 模型,处理用户和超级用户的创建。
接下来,我们必须编辑我们的settings.py ,以设置djoser 和谷歌认证。
在backend/settings.py 文件中添加以下内容。
from datetime import timedelta
AUTH_USER_MODEL = 'users.User'
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'users',
'rest_framework',
'djoser',
'corsheaders',
'social_django',
'rest_framework_simplejwt',
'rest_framework_simplejwt.token_blacklist'
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'social_django.middleware.SocialAuthExceptionMiddleware',
]
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000",
"http://127.0.0.1:3000",
]
CORS_ORIGIN_WHITELIST = [
"http://localhost:3000",
"http://127.0.0.1:3000",
]
CORS_ALLOW_CREDENTIALS = True
ROOT_URLCONF = 'auth_system.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'social_django.context_processors.backends',
'social_django.context_processors.login_redirect'
],
},
},
]
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
}
SIMPLE_JWT = {
'AUTH_HEADER_TYPES': ('JWT',),
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
}
DJOSER = {
'LOGIN_FIELD': 'email',
'SOCIAL_AUTH_TOKEN_STRATEGY': 'djoser.social.token.jwt.TokenStrategy',
'SOCIAL_AUTH_ALLOWED_REDIRECT_URIS': ['http://127.0.0.1:3000', 'http://127.0.0.1:3000/home','http://127.0.0.1:3000/login'],
'SERIALIZERS': {},
}
AUTHENTICATION_BACKENDS = (
'social_core.backends.google.GoogleOAuth2',
'django.contrib.auth.backends.ModelBackend'
)
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = 'your_client_id_key'
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = 'your_secret_key'
SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = [
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'openid'
]
SOCIAL_AUTH_GOOGLE_OAUTH2_EXTRA_DATA = ['first_name', 'last_name']
我们已经在INSTALLED_APPS 中列出了我们的应用程序所需的所有应用程序。接下来,我们指定了认证所需的User 模型,并为CORS 和django_social 设置了中间件。
然后,我们指定了允许访问我们应用程序的网站起源。这种情况下,我们将使用localhost:3000 ,因为我们以后将使用React。
然后,我们对Django REST框架和Django REST simple-jwt进行了设置。注意允许重定向的URLs部分。这些URL应该类似于我们将在谷歌云控制台中为应用程序设置的URL。
我们将实现谷歌的认证,所以我们也需要ModelBackend ,以登录到管理面板。然后,我们为SOCIAL_AUTH_GOOGLE_OAUTH2_KEY,SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET 进行设置。
现在我们需要配置URL,使后端应用程序工作。
编辑backend/urls.py 文件,如下所示。
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('auth/', include('djoser.urls')),
path('auth/', include('djoser.urls.jwt')),
path('auth/', include('djoser.social.urls')),#Needed for social authentication
]
运行python3 manage.py makemigrations 和python3 manage.py migrate 来完成后端设置。
创建谷歌OAuth凭证
访问Google Cloud Plaform并创建一个新项目,如下所示。

选择项目后,点击Credentials ,然后在导航后点击OAuth Client ID ,创建凭证。

你可能需要设置OAuth同意,所以选择网络应用,然后输入http://127.0.0.1:3000 ,用于授权的JavaScript起源。
对于授权重定向,URI有这些URL。http://127.0.0.1:3000,http://127.0.0.1:3000/home, 和http://127.0.0.1:3000/login

在你点击创建后,你将得到你的client id 和client secret ,你将在你的settings.py 中使用,如下所示。
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = 'your_client_id_key'
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = 'your_secret_key'
创建前端应用程序
在一个新的终端会话中,运行npx create-react-app frontend ,创建一个新的React应用程序。
导航到frontend并运行以下命令npm install axios redux react-redux redux-devtools-extension-redux-thunk styled-components 。
这些命令安装了我们在构建这个应用程序时需要的所有依赖项。
你的项目结构应该是这样的。
├── node_modules
├── package.json
├── package-lock.json
├── public
│ ├── favicon.ico
│ ├── images
│ │ ├── google.svg
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── README.md
└── src
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── logo.svg
│ ├── auth.js
│ └── index.js
├── reportWebVitals.js
└── setupTests.js
创建组件
在src 文件夹下,创建一个名为components 的新目录来存放所有需要的组件。
在该文件夹中,创建以下组件。
1.组件/Home.js
import React from 'react'
import {Link} from "react-router-dom";
const Home = () => {
return (
<div className="container">
<div className="card"style={{ width: 700}}>
<div className="card-body">
<h5 className="card-title">Welcome to The Auth with React & Djoser</h5>
<p className="card-text">Thank you for using this authentication system.</p>
</div>
</div>
</div>
)
}
export default Home
2.组件/Layout.js
import React, from 'react'
const Layout = (props) => {
return (
<div className="container">
<Navbar/>
{props.children}
</div>
)
}
export default Layout
3.组件/Login.js
import React from 'react'
import {Link, Navigate} from 'react-router-dom'
import styled from "styled-components";
const Login = () => {
return (
<div className="container mt-4">
<h1>Sign In</h1>
<p>Log into your account now.</p>
<Google className="btn btn-secondary" >
<img src="/images/google.svg" alt=""/>
Continue with Google.
</Google>
<p className="mt-3">Don't have an account? <Link to="/signup">Register</Link></p>
</div>
)
}
const Google = styled.button`
display: flex;
justify-content: center;
background-color: #fff;
align-items: center;
height: 46px;
width: 30%;
border-radius: 28px;
box-shadow: inset 0 0 0 1px rgb(0, 0, 0, 60%), inset 0 0 0 2px rgb(0, 0, 0, 0%), inset 0 0 0 1px rgb(0, 0, 0, 0);
vertical-align: middle;
z-index: 0;
transition-duration: 167ms;
font-size: 20px;
color: rgbe(0, 0, 0, 0.6);
&:hover {
background-color: rgba(207, 207, 207, 0.25);
color: rgba(0, 0, 0, 0.75);
}
`
export default Login
4.组件/Navbar.js
import React from 'react'
import {Link} from "react-router-dom";
const Navbar = () => {
return(
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<div className="container-fluid">
<a className="navbar-brand" href="#">The Auth</a>
<button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarNav">
<ul className="navbar-nav">
<li className="nav-item">
<Link className="nav-link active" aria-current="page" to="/">Home</Link>
</li>
<li className="nav-item">
<Link className="nav-link active" aria-current="page" to="/login">Home</Link>
</li>
<li className="nav-item">
<Link className="nav-link active" aria-current="page" to="/signup">Home</Link>
</li>
</ul>
</div>
</div>
</nav>
)
}
export Navbar
5.Components/Signup.js
import React from 'react'
import {Link, Navigate} from 'react-router-dom'
import styled from "styled-components";
const Signup = () => {
return (
<div className="container mt-4">
<h1>Sign Up</h1>
<p>Create into your account now.</p>
<Google className="btn btn-secondary">
<img src="/images/google.svg" alt=""/>
Continue with Google.
</Google>
<p className="mt-3">Have an account? <Link to="/login">Sign In</Link></p>
</div>
)
}
const Google = styled.button`
display: flex;
justify-content: center;
background-color: #fff;
align-items: center;
height: 46px;
width: 30%;
border-radius: 28px;
box-shadow: inset 0 0 0 1px rgb(0, 0, 0, 60%), inset 0 0 0 2px rgb(0, 0, 0, 0%), inset 0 0 0 1px rgb(0, 0, 0, 0);
vertical-align: middle;
z-index: 0;
transition-duration: 167ms;
font-size: 20px;
color: rgbe(0, 0, 0, 0.6);
&:hover {
background-color: rgba(207, 207, 207, 0.25);
color: rgba(0, 0, 0, 0.75);
}
`
export default Signup
6.组件/Welcome.js
import React from 'react'
import {Link} from "react-router-dom";
const Welcome = () => {
return (
<div className="container">
<div className="card"style={{ width: 700}}>
<div className="card-body">
<h5 className="card-title">Welcome to The Auth</h5>
<p className="card-text">Click to Login.</p>
<Link to="/login" className="btn btn-primary">Login</Link>
<p className="mt-3">Don't have an account? <Link to="/signup">Register</Link></p>
</div>
</div>
</div>
)
}
export default Welcome
现在我们需要为我们的组件配置路由。编辑App.js 文件,如下所示。
/* import logo from './logo.svg';*/
/* import './App.css'; */
import React from 'react'
import {BrowserRouter, Route, Routes} from "react-router-dom";
import Welcome from "./components/Welcome";
import Signup from "./components/Signup";
import Login from "./components/Login";
import Layout from "./components/Layout";
import store from "./store";
import Home from "./components/Home";
function App() {
return (
<div>
<BrowserRouter>
<Layout>
<Routes>
<Route exact path="/" element={<Welcome />} />
<Route exact path="/home" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/signup" element={<Signup />} />
</Routes>
</Layout>
</BrowserRouter>
</div>
);
}
export default App;
使用命令npm start ,可以看到这个欢迎页面。

你也应该能够导航到登录和注册页面。
设置认证
前端应用程序将使用redux ,用于我们的应用程序。Redux是一个用于JavaScript应用程序的状态容器。
我们将为我们的应用程序设置动作还原器和存储文件,但是,首先,在src 下创建以下文件和文件夹,文件夹结构如下。
├── node_modules
├── package.json
├── package-lock.json
├── public
│ ├── favicon.ico
│ ├── images
│ │ ├── google.svg
│ │ ├── haidong.jpg
│ │ ├── jaspergeys.jpg
│ │ └── robson.jpg
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── README.md
└── src
├── actions
│ ├── auth.js
│ └── types.js
├── App.css
├── App.js
├── App.test.js
├── components
│ ├── Home.js
│ ├── Layout.js
│ ├── Login.js
│ ├── Navbar.js
│ ├── Signup.js
│ └── Welcome.js
├── index.css
├── index.js
├── logo.svg
├── reducers
│ ├── auth.js
│ └── index.js
├── reportWebVitals.js
├── setupTests.js
└── store.js
- actions - 这个文件夹处理如何指定不同的动作。
- reducers - 处理在不同行动中如何操作状态的问题。
- store.js - 我们用来定义我们的商店的一个文件。
- .env文件 - 我们用这个文件来定义我们的默认API URL。
我们首先在.env 文件中定义我们的API URL。
REACT_APP_API_URL = 'http://127.0.0.1:8000'
为了定义我们的动作,我们需要不同的动作类型,所以编辑types.js 文件,如下所示。
export const GOOGLE_AUTH_SUCCESS = 'GOOGLE_AUTH_SUCCESS'
export const GOOGLE_AUTH_FAIL = 'GOOGLE_AUTH_FAIL'
export const LOGOUT = 'LOGOUT'
然后我们在auth.js 文件中定义我们的动作,如下所示。
import {
GOOGLE_AUTH_FAIL,
GOOGLE_AUTH_SUCCESS,
LOGOUT,
} from "./types";
import axios from "axios";
axios.defaults.withCredentials = true;
export const googleAuthenticate = (state, code) => async dispatch =>{
if( state && code && !localStorage.getItem('access')){
const config = {
headers: {
'Content-Type':'application/x-www-form-urlencoded'
}}
const details = {
'state': state,
'code':code
}
const formBody = Object.keys(details).map(key=> encodeURIComponent(key)+'='+encodeURIComponent(details[key])).join('&')
try{
const res = await axios.post(`${process.env.REACT_APP_API_URL}/auth/o/google-oauth2/?${formBody}`, config);
console.log(res.data)
dispatch({
type:GOOGLE_AUTH_SUCCESS,
payload: res.data
})
}catch(err){
dispatch({
type:GOOGLE_AUTH_FAIL
})
console.log(err)
}
}
}
export const logout = () => dispatch => {
dispatch({
type: LOGOUT
})
}
当用户使用谷歌进行认证时,应用程序将发送一个包含状态和代码的重定向URI。我们将使用这个代码和状态来获取用户信息,如访问和刷新令牌、电子邮件和姓名。
googleAuthenticate 函数用代码和状态作为数据处理post请求,然后将用户数据作为一个响应返回。
这些结果被派发到reducer,动作类型是GOOGLE_AUTH_SUCCESS 。然而,如果出现任何错误,那么派发的动作类型是GOOGLE_AUTH_FAIL 。
我们也有logout 动作,派发的动作类型是LOGOUT 。接下来,我们需要处理还原器。我们必须在src/reducers/ 中创建一个index.js 文件,其中有两个以上的还原器,如下所示。
import { combineReducers } from 'redux';
import auth from './auth';
const rootReducer = combineReducers({
auth
})
export default rootReducer
我们在reducers目录下有auth.js 文件,以根据类型触发一个动作。
import {
GOOGLE_AUTH_FAIL,
GOOGLE_AUTH_SUCCESS,
LOGOUT,
} from "../actions/types";
const initialState = {
access: localStorage.getItem('access'),
refresh: localStorage.getItem('refresh'),
isAuthenticated: false,
}
export default function(state=initialState,action){
switch (action.type){
case GOOGLE_AUTH_SUCCESS:
console.log(action.payload)
localStorage.setItem('access',action.payload.access)
return{
...state,
isAuthenticated: true,
access: action.payload.access,
refresh: action.payload.refresh
}
case GOOGLE_AUTH_FAIL:
case LOGOUT:
console.log(action.payload)
localStorage.removeItem('access')
localStorage.removeItem('refresh')
return{
...state,
isAuthenticated: false,
access: null,
refresh: null
}
default:
return state
}
}
我们需要设置一个初始状态,为访问和刷新令牌设置默认值。我们还为isAuthenticated 设置了一个默认的布尔值false。这个值以后将被用来处理认证用户的路由。
我们将本地存储中的访问和刷新令牌值设置为认证成功后派发的值。isAuthenticated 为真;否则,令牌值为空,isAuthenticated 仍为假。
如果没有其他动作,我们返回当前状态并创建我们的存储。编辑src/store.js ,使其类似于这个。
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
所有的组件将从这个文件中读取状态。我们需要使我们的商店对我们的组件可用。
让我们把它添加到src/App.js 。
// All imports remain the same
function App() {
return (
<div>
<Provider store={store}>
<BrowserRouter>
// All code remain the same
</BrowserRouter>
</Provider>
</div>
);
}
export default App;
<Provider store={store}> 这一行使我们的应用程序中的所有组件都可以使用这些状态。现在,我们必须把我们的组件连接到商店。所以让我们从src/components/Layout.js 文件开始。
import React, {useEffect} from 'react'
import Navbar from "./Navbar";
import {googleAuthenticate} from "../actions/auth";
import {connect} from "react-redux";
import {useLocation} from "react-router-dom";
import queryString from "query-string";
const Layout = (props) => {
const location = useLocation()
useEffect(() => {
const values = queryString.parse(location.search)
const state = values.state ? values.state : null
const code = values.code ? values.code : null
console.log('State: '+ state)
console.log('Code: '+code)
if (state && code){
props.googleAuthenticate(state, code)
}
}, [location])
return (
<div className="container">
<Navbar/>
{props.children}
</div>
)
}
export default connect(null, { googleAuthenticate})(Layout)
我们使用connect 函数来连接到我们的商店。然后,我们调度googleAuthenticate 动作,并将其作为道具传递给Layout 函数。
我们需要获得当前页面的URL和URL的键值对。如果代码和状态存在,我们调用googleAuthenticate 动作,该动作将代码和状态作为参数。
当我们被重定向时,URL有一个状态和代码,获得并传递给googleAuthenticate 函数。
接下来,我们把我们的Signup 连接到商店。编辑src/components/Signup.js ,如下所示。
import React, {useEffect, useState} from 'react'
import {Link, Navigate} from 'react-router-dom'
import {connect, useDispatch, useSelector} from 'react-redux'
import styled from "styled-components";
import axios from "axios";
const Signup = ({ isAuthenticated}) => {
const signupWithGoogle = async () => {
try {
const res = await axios.get(`${process.env.REACT_APP_API_URL}/auth/o/google-oauth2/?redirect_uri=${process.env.REACT_APP_API_URL}/login`)
window.location.replace(res.data.authorization_url)
} catch (err) {
console.log("Error logging in")
}
}
if (isAuthenticated) {
return <Navigate to='/home'/>
}
return (
<div className="container mt-4">
<h1>Sign Up</h1>
<p>Create into your account now.</p>
<Google className="btn btn-secondary" onClick={signupWithGoogle}>
<img src="/images/google.svg" alt=""/>
Continue with Google.
</Google>
<p className="mt-3">Have an account? <Link to="/login">Sign In</Link></p>
</div>
)
}
const Google = styled.button`
display: flex;
justify-content: center;
background-color: #fff;
align-items: center;
height: 46px;
width: 30%;
border-radius: 28px;
box-shadow: inset 0 0 0 1px rgb(0, 0, 0, 60%), inset 0 0 0 2px rgb(0, 0, 0, 0%), inset 0 0 0 1px rgb(0, 0, 0, 0);
vertical-align: middle;
z-index: 0;
transition-duration: 167ms;
font-size: 20px;
color: rgbe(0, 0, 0, 0.6);
&:hover {
background-color: rgba(207, 207, 207, 0.25);
color: rgba(0, 0, 0, 0.75);
}
`
const mapStateToProps = state => ({
isAuthenticated: state.auth.isAuthenticated,
})
export default connect(mapStateToProps, null)(Signup);
当用户在注册时点击继续使用谷歌,signupWithGoogle 函数被调用。这个函数向后端发送一个带有指定重定向URI的帖子请求。
这个URI必须是Djoser设置中所允许的重定向URI之一。此外,允许的URI必须与你的谷歌云控制台中的URI相同。
用户会被引导到授权页面,以及指定的重定向URI,URL中包含状态和代码。
我们还传递了isAuthenticated 状态,以将用户重定向到主页,如果他们已经通过了认证,则无需再进行认证。
现在我们对登录页面做同样的工作。
import React, {useEffect, useState} from 'react'
import {Link, Navigate} from 'react-router-dom'
import {connect, useDispatch} from 'react-redux'
import {login} from "../actions/auth";
import styled from "styled-components";
import axios from "axios";
const Login = ({ isAuthenticated}) => {
const loginWithGoogle = async () =>{
try{
const res = await axios.get(`${process.env.REACT_APP_API_URL}/auth/o/google-oauth2/?redirect_uri=${process.env.REACT_APP_API_URL}/home`)
window.location.replace(res.data.authorization_url)
}catch(err){
console.log("Error logging in")
}
}
if(isAuthenticated){
return <Navigate to="/home" />
}
return (
<div className="container mt-4">
<h1>Sign In</h1>
<p>Log into your account now.</p>
<Google className="btn btn-secondary" onClick={loginWithGoogle} >
<img src="/images/google.svg" alt=""/>
Continue with Google.
</Google>
<p className="mt-3">Don't have an account? <Link to="/signup">Register</Link></p>
</div>
)
}
const Google = styled.button`
display: flex;
justify-content: center;
background-color: #fff;
align-items: center;
height: 46px;
width: 30%;
border-radius: 28px;
box-shadow: inset 0 0 0 1px rgb(0, 0, 0, 60%), inset 0 0 0 2px rgb(0, 0, 0, 0%), inset 0 0 0 1px rgb(0, 0, 0, 0);
vertical-align: middle;
z-index: 0;
transition-duration: 167ms;
font-size: 20px;
color: rgbe(0, 0, 0, 0.6);
&:hover {
background-color: rgba(207, 207, 207, 0.25);
color: rgba(0, 0, 0, 0.75);
}
`
const mapStateToProps = state => ({
isAuthenticated: state.auth.isAuthenticated,
})
export default connect(mapStateToProps, null)(Login);
我们已经完成了与注册相同的程序,只是这一次,当用户登录时,重定向URI将是主页。
我们还传递了状态isAuthenticated ,以确保认证的用户被重定向到主页。
最后,我们处理注销过程。编辑src/components/Navbar.js ,如下所示。
import React from 'react'
import {Link} from "react-router-dom";
import {logout} from "../actions/auth";
import {connect} from "react-redux";
const Navbar = ({isAuthenticated, logout}) => {
return(
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<div className="container-fluid">
<a className="navbar-brand" href="#">The Auth</a>
<button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarNav">
<ul className="navbar-nav">
<li className="nav-item">
<Link className="nav-link active" aria-current="page" to="/">Home</Link>
</li>
{isAuthenticated ?
<li className="nav-item">
<a className="nav-link" href="/" onClick={logout}>Logout</a>
</li>
:
<div className="navbar-nav">
<li className="nav-item">
<Link className="nav-link" to="/login">Log In</Link>
</li>
<li className="nav-item">
<Link className="nav-link" to="/signup">Sign Up</Link>
</li>
</div>
}
</ul>
</div>
</div>
</nav>
)
}
const mapStateToProps = state => ({
isAuthenticated: state.auth.isAuthenticated
})
export default connect(mapStateToProps, { logout })(Navbar)
再一次,我们将isAuthenticated 状态和注销函数作为道具传递给Navbar函数。接下来,我们检查用户是否经过认证,如果是,则将用户注销;否则,用户可以选择注册或登录。
当用户点击注销链接时,就会调用注销函数。现在你应该能够使用谷歌账户进行认证。

你将被重定向到主页,可以选择注销。

请注意,只要用户登录了他们的谷歌账户,就会保持认证状态。这样,用户就不必在每次想登录时不断输入电子邮件和密码。
我建议运行
npm run build,将构建文件夹移到后台目录,设置必要的查看URL和静态文件的设置,使你的项目在localhost:8000上运行以避免认证错误。
总结
你现在已经用Djoser处理了Google的认证。你可以对其他社交账户,如Facebook、Twitter或其他账户做同样的处理。