使用 Google OAuth 登录
OAuth
是一种开放的委派授权协议,它允许网站或应用程序之间授予访问权限,而无需向被授予访问权限的各方暴露用户密码。许多公司和网站都使用这种非常常见的访问委派方式,通过提供 OAuth
授权的第三方(如 Google
和 Facebook
)来识别用户。让我们允许我们的用户使用 Google OAuth
登录我们的应用程序。此选项需要从 Google
开发者控制台获取客户端 ID
和客户端密钥。可以通过以下步骤获取它们:
-
在
Google
开发者控制台 (https://console.developers.google.com/) 中创建一个新项目。 -
在
OAuth
同意屏幕标签上选择 “外部”。 -
在 “凭据” 标签上的 “创建凭据” 下拉选项中选择 “OAuth 客户端 ID”,然后为 “应用程序类型” 选择 “Web 应用程序”。
-
在 “名称” 字段中提供你的
OAuth
客户端ID
的名称,并在 “已授权的重定向 URI” 字段中提供重定向URI
,以便Google
在用户在Google
同意页面上进行身份验证后将用户重定向到这些URI
。 -
启用
Google People API
,该API
通过 “库” 标签中的API
库提供对个人资料和联系人信息的访问。
按照前面的步骤设置开发者帐户并创建客户端 ID
和客户端密钥后,你就可以在下一节中将 Google OAuth
添加到后端身份验证了。让我们开始吧。
为后端认证添加 Google OAuth
为了让某人通过 Google
登录,我们需要将他们发送到 Google
登录页面。在那里,他们将登录自己的帐户,并被重定向回我们的应用程序,其中包含他们的 Google
登录详细信息,我们将从中提取 Google
代码并将其发送回 Google
以获取我们可以在应用程序中使用用户数据。此过程需要 googleapis Node.js
模块,这是一个用于使用 Google API
的客户端库。
让我们按照以下步骤安装并在我们的代码中采用它:
-
通过
npm
安装googleapis
Node.js 模块:$ npm i googleapis
-
创建一个包含你的凭据的文件,以便
Google
知道是谁发出的请求:// backend/src/config/google.js export default { clientId: '<客户端 ID>', clientSecret: '<客户端密钥>', redirect: 'http://localhost:3000/login' }
请注意,你必须将上面的 <客户端 ID> 和 <客户端密钥> 值替换为你从
Google
开发者控制台获得的ID
和密钥。另请注意,redirect
选项中的URL
必须与你的Google
应用API
设置中 “已授权的重定向 URI” 中的URI
匹配。 -
使用
Google OAuth
生成一个Google
身份验证URL
,以便将用户发送到Google
同意页面,从而获得用户检索访问令牌的权限,如下所示:// backend/src/modules/public/login/_routes/google/url.js import Router from 'koa-router' import { google } from 'googleapis' import googleConfig from 'config/google' const router = new Router() router.get('/google/url', async (ctx, next) => { const oauth = new google.auth.OAuth2( googleConfig.clientId, googleConfig.clientSecret, googleConfig.redirect ) const scopes = [ 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile', ] const url = oauth.generateAuthUrl({ access_type: 'offline', prompt: 'consent', scope: scopes }) ctx.body = url })
作用域决定了当用户登录并生成
URL
时,我们想要从用户那里获取哪些信息和权限。在我们的例子中,我们想要获取用户电子邮件和个人资料信息的权限:userinfo.email
和userinfo.profile
。用户在Google
同意页面上进行身份验证后,Google
会将用户重定向回我们的应用程序,其中包含一堆身份验证数据和一个用于访问用户数据的授权代码。 -
从上一步中
Google
附加在返回URL
中的身份验证数据中提取code
参数的值。我们将在下一节中回到Node.js
模块,该模块可以帮助我们稍后从URL
查询中提取code
参数。现在,假设我们已经提取了code
值并将其发送到服务器端,以便使用以下基本代码结构中的Google OAuth2
实例请求令牌:// backend/src/modules/public/login/_routes/google/me.js import Router from 'koa-router' import { google } from 'googleapis' import jwt from 'jsonwebtoken' import pool from 'core/database/mysql' import config from 'config' import googleConfig from 'config/google' const router = new Router() router.get('/google/me', async (ctx, next) => { // 从 URL 查询中获取代码。 const code = ctx.query.code // 创建一个新的 google oauth2 客户端实例。 const oauth2 = new google.auth.OAuth2( googleConfig.clientId, googleConfig.clientSecret, googleConfig.redirect ) //... })
-
使用我们刚刚提取的代码从
Google
获取令牌,并将它们传递给Google People (google.people)
,使用get
方法获取用户数据,并在personFields
查询参数中指定需要返回的与人相关的字段:// backend/src/modules/public/login/_routes/google/me.js ... const {tokens} = await oauth2.getToken(code) oauth.setCredentials(tokens) const people = google.people({ version: 'v1', auth: oauth2, }) const me = await people.people.get({ resourceName: 'people/me', personFields: 'names,emailAddresses' })
你可以看到,在上面的代码中,我们只想要从
Google
获取与人相关的两个字段,即names
和emailAddresses
。你可以在 https://developers.google.com/people/api/rest/v1/people/get 上找到你想从Google
获取的其他与人相关的字段。如果访问成功,我们应该从Google
获得JSON
格式的用户数据,然后我们可以从该数据中提取电子邮件,以确保它将在下一步中与我们数据库中的用户匹配。 -
仅从
Google
个人数据中检索第一个电子邮件,并查询我们的数据库以查看是否已存在具有该电子邮件的用户:// backend/src/modules/public/login/_routes/google/me.js ... let email = me.data.emailAddresses[0].value let users = [] try { users = await pool.query('SELECT * FROM users WHERE email = ?', [email]) } catch(err) { ctx.throw(400, err.sqlMessage) }
-
如果不存在具有该电子邮件的用户,则向客户端发送 “signup required” 消息以及来自
Google
的用户数据,并要求用户在我们的应用程序中注册一个帐户:// backend/src/modules/public/login/_routes/google/me.js ... if (users.length === 0) { ctx.body = { user: me.data, message: 'signup required' } return } let user = users[0]
-
如果存在匹配项,则使用载荷和
JWT
密钥签署一个JWT
,然后将令牌 (JWT
) 发送到客户端:// backend/src/modules/public/login/_routes/google/me.js ... let payload = { name: user.name, email: user.email } let token = jwt.sign(payload, config.JWT_SECRET, { expiresIn: 1 * 60 }) ctx.body = { user: payload, message: 'logged in ok', token: token }
就这样。在前面的几个步骤中,你已经成功地在服务器端添加了 Google OAuth
。接下来,我们将在下一节中了解如何在客户端使用 Nuxt
完成 Google OAuth
的身份验证。让我们开始吧。
有关 |
为 Google OAuth 创建前端认证
当 Google
将用户重定向回我们的应用程序时,我们将在重定向 URL
上获得一堆数据,例如:
http://localhost:3000/login?code=4%2F1QGpS37E21TcgQhhIvJZlK1cG4M1jpPJ0I_XPQ
grFjvKUFUJQ3aYuO1zYsqPmKgNb4Wfd8ito88yDjUTD6CKD3E&scope=email%20profile%20h
ttps%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email%20https%3A%2F%2Fwww
.googleapis.com%2Fauth%2Fuserinfo.profile%20openid&authuser=1&prompt=consen
t
乍一看,它很难阅读和理解,但它只是一个查询字符串,其参数附加到我们的重定向 URL
:
<redirect URL>?
code=4/1QFvWYDSrW...
&scope=email profile...
&authuser=1
&prompt=consent
我们可以使用 Node.js
模块 query-string
来解析 URL
中的查询字符串,例如:
const queryString = require('query-string')
const parsed = queryString.parse(location.search)
console.log(parsed)
然后你将在浏览器的控制台中得到以下 JavaScript
对象:
{
"authuser": "1",
"code": "4/1QFvWYDSrWLklhIgRfVR0LJy6Pk0gn5TkjTKWKlRr9pdZveGAHV_pMrxBhicy7Zd6d9nfz0IQrcLl-VGS-Gu9Xk",
"prompt": "consent",
"scope": "email profile https://www.googleapis.com/auth/user…//www.googleapis.com/auth/userinfo.profile openid"
}
正如你在上一节中学到的,code
参数是上述重定向 URL
中我们最感兴趣的,因为我们需要将其发送到服务器端,以便通过 googleapis
Node.js 模块获取 Google
用户数据。因此,让我们安装 query-string
并在我们的 Nuxt
应用程序中创建前端身份验证,步骤如下:
-
通过
npm
安装query-string
Node.js 模块:$ npm i query-string
-
在登录页面上创建一个按钮,并绑定一个名为
loginWithGoogle
的方法来分发store
中的getGoogleUrl
方法,如下所示:// frontend/pages/login.vue <button @click="loginWithGoogle">Google 登录</button>
export default { methods: { async loginWithGoogle() { try { await this.$store.dispatch('getGoogleUrl') } catch (error) { let errorData = error.response.data this.formError = errorData.message } } } }
-
在
getGoogleUrl
方法中调用API
中的/api/public/login/google/url
路由,如下所示:// frontend/store/actions.js export default { async getGoogleUrl(context) { const { data } = await this.$axios.$get('/api/public/login/google/url') window.location.replace(data) } }
/api/public/login/google/url
路由将返回一个Google URL
,然后我们可以使用它将用户重定向到Google
登录页面。在那里,如果用户有多个Google
帐户,他们将决定登录哪个帐户。 -
当
Google
将用户重定向回登录页面时,从返回的URL
中提取查询部分,并将其发送到store
中的loginWithGoogle
方法,如下所示:// frontend/pages/login.vue export default { async mounted () { let query = window.location.search if (query) { try { await this.$store.dispatch('loginWithGoogle', query) } catch (error) { // 处理错误 } } } }
-
使用
query-string
从上述查询部分的code
参数中提取代码,并使用$axios
将其发送到我们的 API/api/public/login/google/me
,如下所示:// frontend/store/actions.js import queryString from 'query-string' export default { async loginWithGoogle (context, query) { const parsed = queryString.parse(query) const { data } = await this.$axios.$get('/api/public/login/google/me', { params: { code: parsed.code } }) if (data.message === 'signup required') { localStorage.setItem('user', JSON.stringify(data.user)) this.$router.push({ name: 'signup'}) } else { cookies.set('auth', data) context.commit('setAuth', data) } } }
当从服务器收到 “signup required” 消息时,我们将用户重定向到注册页面。但是,如果收到带有
JWT
的消息,那么我们可以设置一个cookie
并将身份验证数据提交到store
的state
中。我们将注册页面留给你的想象力和努力,因为它是一个收集用户数据以存储在数据库中的表单。 -
最后,使用
npm run dev
运行Nuxt
应用程序。你应该在浏览器上的localhost:3000
看到应用程序正在运行。你可以使用Google
登录,然后访问受JWT
保护的受限页面——就像本地身份验证一样。
就这样——这些是你使用 Google OAuth API
登录用户的基本步骤。一点也不难,对吧?我们还可以使用 Nuxt Auth
模块来实现几乎与我们在这里完成的相同的功能。使用此模块,你可以使用 Auth0
、Facebook
、GitHub
、Laravel Passport
和 Google
登录用户。如果你正在为 Nuxt
寻找快速、简单且零样板的身份验证支持,它可能是你项目的一个不错的选择。有关此 Nuxt
模块的更多信息,请访问 https://auth.nuxtjs.org/ 。现在,让我们在下一节中总结一下你在本章中学到的内容。
你可以在我们的 有关 |