在React与Express应用中配置新版Google身份验证
本文作者:Onuorah Bonaventure Chukwudi
发布于:JavaScript·Web·2025年1月3日·更新:2025年1月3日
文章概述
本文将指导您如何在React.js和Express.js应用中配置新版Google“使用Google登录”按钮。随着Google身份服务SDK的更新,集成Google登录功能变得更加简单和强大。新方法引入了账户选择时的个人资料图片预览、一键登录(One Tap)以及增强的令牌管理功能。由于旧的“使用Google登录”JavaScript库已被弃用,现在转向这种更新方法对新项目和现有项目都至关重要。
主要内容
生成Google客户端ID和密钥
首先,需要在Google云控制台中生成Google客户端ID和密钥。这些凭证对于在应用中设置安全身份验证至关重要。
步骤:
- 导航到Google控制台。
- 创建新项目,输入项目名称(如
connect-google-auth-2024)。
- 配置OAuth同意屏幕,提供应用名称、支持邮箱和徽标等详细信息。
- 创建OAuth 2.0客户端ID,设置应用类型为“Web应用”,并添加授权重定向URI(如
http://localhost和http://localhost:3000)。
- 下载生成的客户端ID和密钥。
设置React应用
使用Create React App或类似现代工具初始化React.js应用。安装@react-oauth/google包以利用Google身份服务SDK。
1
2
|
npx create-react-app app
npm install @react-oauth/google
|
设置Express服务器
在根目录创建server文件夹,初始化package.json,并安装必要的包:
1
|
npm install express cors dotenv google-auth-library jsonwebtoken nodemon
|
配置服务器脚本和基本服务器代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
// package.json
{
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
}
}
// server.js
const express = require("express");
const app = express();
require("dotenv/config");
const cors = require("cors");
const { OAuth2Client } = require("google-auth-library");
const jwt = require("jsonwebtoken");
app.use(cors({ origin: ["http://localhost:3000"], methods: "GET,POST,PUT,DELETE,OPTIONS" }));
app.use(express.json());
let DB = [];
app.listen("5152", () => console.log("Server running on port 5152"));
|
准备React应用
现代应用不再需要手动添加Google脚本。改为从@react-oauth/google包导入GoogleLogin组件以实现更清晰的集成。
1
2
|
<!-- index.html -->
<script src="https://accounts.google.com/gsi/client" async defer></script>
|
配置客户端路由
使用react-router-dom进行客户端路由配置:
1
|
npm install react-router-dom
|
更新App.js以包含路由:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
// App.js
import React, { useEffect, useState } from "react";
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import { Landing, Login, Signup, Home } from "./screens";
const App = () => {
const [user, setUser] = useState({});
useEffect(() => {
const theUser = localStorage.getItem("user");
if (theUser && !theUser.includes("undefined")) {
setUser(JSON.parse(theUser));
}
}, []);
return (
<BrowserRouter>
<Routes>
<Route path="/" element={user?.email ? <Navigate to="/home" /> : <Landing />} />
<Route path="/signup" element={user?.email ? <Navigate to="/home" /> : <Signup />} />
<Route path="/login" element={user?.email ? <Navigate to="/home" /> : <Login />} />
<Route path="/home" element={user?.email ? <Home user={user} /> : <Navigate to="/" />} />
</Routes>
</BrowserRouter>
);
};
export default App;
|
创建登录和注册页面
**登录页面(Login.jsx)和注册页面(Signup.jsx)**使用Google身份服务SDK渲染登录按钮,并处理认证回调。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
// Signup.jsx
import React, { useEffect } from "react";
import { Link } from "react-router-dom";
import useFetch from "../hooks/useFetch";
const SignUp = () => {
const { handleGoogle, loading, error } = useFetch("http://localhost:5152/signup");
useEffect(() => {
if (window.google) {
google.accounts.id.initialize({
client_id: process.env.REACT_APP_GOOGLE_CLIENT_ID,
callback: handleGoogle,
});
google.accounts.id.renderButton(document.getElementById("signUpDiv"), {
theme: "filled_black",
text: "continue_with",
shape: "pill",
});
}
}, [handleGoogle]);
return (
<>
<nav><Link to="/">Go Back</Link></nav>
<header><h1>Register to continue</h1></header>
<main>
{error && <p style={{ color: "red" }}>{error}</p>}
{loading ? <div>Loading....</div> : <div id="signUpDiv" data-text="signup_with"></div>}
</main>
</>
);
};
export default SignUp;
|
创建useFetch钩子
useFetch钩子处理Google认证响应,并向服务器发送请求以验证凭证和创建用户会话。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
// useFetch.jsx
import { useState } from "react";
const useFetch = (url) => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const handleGoogle = async (response) => {
setLoading(true);
fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ credential: response.credential }),
})
.then((res) => res.json())
.then((data) => {
if (data?.user) {
localStorage.setItem("user", JSON.stringify(data?.user));
window.location.reload();
}
throw new Error(data?.message || data);
})
.catch((error) => setError(error?.message))
.finally(() => setLoading(false));
};
return { loading, error, handleGoogle };
};
export default useFetch;
|
创建服务器端路由
在服务器端,创建用于验证Google令牌和处册/登录请求的路由。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
// server.js
const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID;
const client = new OAuth2Client(GOOGLE_CLIENT_ID);
async function verifyGoogleToken(token) {
try {
const ticket = await client.verifyIdToken({
idToken: token,
audience: GOOGLE_CLIENT_ID,
});
return { payload: ticket.getPayload() };
} catch (error) {
return { error: "Invalid user detected. Please try again" };
}
}
app.post("/signup", async (req, res) => {
try {
if (req.body.credential) {
const verificationResponse = await verifyGoogleToken(req.body.credential);
if (verificationResponse.error) return res.status(400).json({ message: verificationResponse.error });
const profile = verificationResponse?.payload;
DB.push(profile);
res.status(201).json({
message: "Signup was successful",
user: {
firstName: profile?.given_name,
lastName: profile?.family_name,
picture: profile?.picture,
email: profile?.email,
token: jwt.sign({ email: profile?.email }, process.env.JWT_SECRET, { expiresIn: "1d" }),
},
});
}
} catch (error) {
res.status(500).json({ message: "An error occurred. Registration failed." });
}
});
app.post("/login", async (req, res) => {
try {
if (req.body.credential) {
const verificationResponse = await verifyGoogleToken(req.body.credential);
if (verificationResponse.error) return res.status(400).json({ message: verificationResponse.error });
const profile = verificationResponse?.payload;
const existsInDB = DB.find((person) => person?.email === profile?.email);
if (!existsInDB) return res.status(400).json({ message: "You are not registered. Please sign up" });
res.status(201).json({
message: "Login was successful",
user: {
firstName: profile?.given_name,
lastName: profile?.family_name,
picture: profile?.picture,
email: profile?.email,
token: jwt.sign({ email: profile?.email }, process.env.JWT_SECRET, { expiresIn: "1d" }),
},
});
}
} catch (error) {
res.status(500).json({ message: error?.message || error });
}
});
|
创建主页和登出功能
主页(Home.jsx)显示用户信息并提供登出功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
// Home.jsx
import React from "react";
const Home = ({ user }) => {
const logout = () => {
localStorage.removeItem("user");
window.location.reload();
};
return (
<div style={{ textAlign: "center", margin: "3rem" }}>
<h1>Dear {user?.email}</h1>
<p>You are viewing this page because you are logged in or you just signed up</p>
<button onClick={logout} style={{ color: "red", border: "1px solid gray", backgroundColor: "white", padding: "0.5rem 1rem", cursor: "pointer" }}>
Logout
</button>
</div>
);
};
export default Home;
|
与其他认证方法的比较
| 方法 |
优点 |
缺点 |
| Google Auth |
无缝登录、个人资料集成、支持一键登录 |
需要Google账户,依赖第三方 |
| Facebook Auth |
社交集成、用户基数大 |
隐私问题,采用率低于Google |
| GitHub Auth |
适合开发者工具,与GitHub API集成 |
仅限于开发者;不适合通用应用 |
| 邮箱/密码 |
不依赖第三方服务 |
需要额外的密码管理安全措施 |
结论
恭喜!您已成功在React.js和Express.js应用中实现了使用最新身份服务SDK的Google登录。此设置支持基于Web的登录和一键登录,以增强用户便利性。React Google Auth、Google OAuth React和react-oauth/google的组合提供了一个强大的身份验证框架,在简化集成的同时保持了可扩展性。不要忘记全面测试您的应用,以确保安全无缝的身份验证。
源代码可在此处获取。
常见问题解答(FAQs)
如何在带有Express后端的React应用中设置React Google登录?
首先在Google开发者控制台中创建凭证以获取Google客户端ID。使用此ID配置OAuth同意屏幕,确保设置了授权的JavaScript来源和重定向URI。在服务器上安装必要的npm包(如express、cors和google-auth-library)。在React应用中,使用react-google-login组件进行前端集成。
如何在React和Express应用中安全获取访问令牌?
在Google登录React实现中,使用react-oauth/google包,该包利用Google的OAuth 2.0协议,确保安全处理身份验证令牌而不暴露敏感数据。
如何在React应用中实现Google登录的登出功能?
提供一个登出按钮,点击时调用函数从本地存储中移除用户详细信息,并从服务器清除会话。这应使会话无效,并将用户重定向到登录屏幕,确保用户完全登出。
如何使用Google登录在React和Express应用中刷新令牌?
使用Google的OAuth 2.0服务器提供的刷新令牌在当前令牌过期时获取新的访问令牌。在Express后端设置中间件,检查令牌的有效性并在需要时刷新它。这使用户无需重复输入凭据即可保持登录状态。
在React Google身份验证中管理用户详细信息的最佳实践是什么?
成功登录后,使用Google返回的ID令牌检索用户详细信息。将这些详细信息存储在本地存储或状态管理解决方案中,以个性化用户体验而不影响安全性。确保敏感数据处理符合最佳实践以保护用户信息。
我可以创建自定义按钮登录到我的React应用吗?
是的,您可以自定义Google登录按钮以更好地适应Web应用的设计。Google通过react-google-login组件提供更改按钮外观的选项,包括大小、文本和颜色。利用这些自定义选项创建独特且无缝的登录流程。
如何配置Google云控制台中的新项目以进行Google身份验证?
转到Google云控制台,点击“创建项目”,输入项目名称,然后导航到API和服务仪表板。从那里启用Google+ API,并通过指定客户端ID、客户端密钥和重定向URI来配置OAuth 2.0凭证。
如何将Google返回的访问令牌用于React和Express应用中的进一步API请求?
使用访问令牌通过将其附加到HTTP授权标头来向Google服务发出授权API请求。这使您的应用能够代表用户访问Google API,从而实现检索个人资料信息或与Google服务交互等功能。