Google 来自 MS Access 2016 应用程序的 oauth2
Google oauth2 from an MS Access 2016 application
我需要从 MS Access 2016 应用程序创建 Google 日历项。
为了能够做到这一点,我需要执行 OAuth2 身份验证,我现在有点绝望。
我可以找到很多提示,但它 none 适用于 MS Access(好吧,有些适用于 MS Access,但它们使用过时的身份验证方法,因此不是很有用)
我真的是第一个尝试这个的人吗?还是有人比我先走这条路并且愿意分享他或她的经验和代码?
非常感谢!
首先,有关 Google 的 oAuth2 的信息以及获取您的 client_id 和 client_secret,请看这里:https://developers.google.com/identity/protocols/OAuth2
好的,开始吧,这就是我构建的。
首先让我们讨论先决条件。有 2 个功能我不会在这里描述,因为它们往往非常个人化。这些函数是:
Public Function GetDBSetting(field as string) as string
Public Sub SetSetting(field as string, value as string)
这些函数获取并设置此功能正常工作所需的设置。您可能希望将它们存储在数据库中 table,但是如何执行完全取决于您,并且不在本答案的范围内。
本方案涉及的设置有:
oauth2_client_id (you get this from google)
oauth2_client_secret (this is given to you by google as well)
oauth2_scope (just pick the scope you need)
oauth2_access_token (this we collect)
oauth2_refresh_token (this we collect)
oauth2_token_type (this we collect)
对于此解决方案,我们需要:
- 带有 WebBrowser 控件(名为 WebBrowser1)的表单(名为 Browser),
- a class 放置 HTTP 请求
- 带有一些工具的模块
class 看起来像这样(将 class 保存为 HTTP_Response):
Option Compare Database
Option Explicit
' A very simple class to send HTTP requests and receive the resulting body
' These variables hold the results
Public Status As Long
Public StatusText As String
Public responseText As String
' This sub simply sends the request and collects the results
' Headers should be an array of strings with the following format: headername:value
Public Sub sendHTTP(URL As String, Optional METHOD As String = "POST", Optional CONTENT As String = "text/plain", Optional BODY As String = "", Optional addAuth As Boolean = False, Optional Headers As Variant)
Dim Http As MSXML2.XMLHTTP60
Dim hdrLine As Variant
Dim hdrarr As Variant
Set Http = New MSXML2.XMLHTTP60
With Http
Call .Open(METHOD, URL)
If CONTENT <> "" Then Call .setRequestHeader("Content-Type", CONTENT)
If addAuth Then Call .setRequestHeader("Authorization", GetDBSetting("oauth2_token_type") & " " & GetDBSetting("oauth2_access_token"))
If IsArray(Headers) Then
For Each hdrLine In Headers
hdrarr = Split(CStr(hdrLine), ":")
Call .setRequestHeader(hdrarr(0), hdrarr(1))
Next
End If
Call .send(BODY)
Status = .Status
StatusText = .StatusText
responseText = .responseText
End With
End Sub
此class仅用于使发送HTTP请求和接收结果更容易。没什么好看的。
模块如下所示:
Option Compare Database
Option Explicit
' A function that checks if the known token is still valid and tries to request a refresh token if it is not
Public Function checkToken() As Boolean
Dim resTxt As New HTTP_Response
Call resTxt.sendHTTP("https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=" & GetDBSetting("oauth2_access_token"))
If resTxt.Status = 200 Then
checkToken = True
Else
checkToken = refreshToken
End If
End Function
' A function that requests a refresh token
Public Function refreshToken() As Boolean
Dim authres() As String
Dim resTxt As New HTTP_Response
Dim svarbody As String
Dim aCnt As Integer
svarbody = "client_secret=" & GetDBSetting("oauth2_client_secret") & "&" & _
"grant_type=refresh_token&" & _
"refresh_token=" & GetDBSetting("oauth2_refresh_token") & "&" & _
"client_id=" & GetDBSetting("oauth2_client_id")
Call resTxt.sendHTTP("https://www.googleapis.com/oauth2/v4/token", , "application/x-www-form-urlencoded", svarbody, False)
If resTxt.Status = 200 Then
authres = Split(resTxt.responseText, """")
aCnt = 0
While aCnt < UBound(authres)
aCnt = aCnt + 1
If authres(aCnt) = "access_token" Then Call SetSetting("oauth2_access_token", authres(aCnt + 2))
If authres(aCnt) = "token_type" Then Call SetSetting("oauth2_token_type", authres(aCnt + 2))
If authres(aCnt) = "refresh_token_" Then Call SetSetting("oauth2_refresh_token", authres(aCnt + 2))
Wend
refreshToken = True
Else
refreshToken = False
End If
End Function
' A sub to revoke a known token
Public Sub revokeToken()
Dim resTxt As New HTTP_Response
if checkToken() Then Call resTxt.sendHTTP("https://accounts.google.com/o/oauth2/revoke?token=" & GetDBSetting("oauth2_access_token"))
End Sub
您可以使用这些功能来确保您拥有有效的 access_token。
如果您没有有效的 access_token,您可以通过请求特定访问权限的 oAuth2 流程(通过您在 oauth2_scope 中设置的值)打开使用命令形成:
Call DoCmd.OpenForm("Browser", acDialog)
表单 VBA 代码如下所示:
Option Compare Database
Private Enum BrowserNavigationFlags
navOpenInNewWindow = 1 ' Open the resource or file in a new window.
navNoHistory = 2 ' Do not add the resource or file to the history list. The new page replaces the current page in the list.
navNoReadFromCache = 4 ' Do not read from the disk cache for this navigation.
navNoWriteToCache = 8 ' Do not write the results of this navigation to the disk cache.
End Enum
Private Sub Form_Load()
Call Me.WebBrowser1.Object.Navigate2("about:blank", navNoReadFromCache)
Call startOauth2
End Sub
Private Sub WebBrowser1_NavigateError(ByVal pDisp As Object, URL As Variant, TargetFrameName As Variant, StatusCode As Variant, Cancel As Boolean)
' Due to the redirect URL pointing to Localhost and we don't have a webserver running at localhost (Just make sure we don't!)
' The browser triggers the NavigateError event when it receives the URL for localhost
' We can now read the URL and extract the received code to request a token
Dim retCode, getAccessToken As String
Dim authres() As String
Dim aCnt As Long
Dim resTxt As New HTTP_Response
' Extract the code from the URL
retCode = Right(URL, Len(URL) - (InStr(1, URL, "&code=") + 5))
' Construct the Body to request a access token and a refresh token
getAccessToken = "code=" & retCode & "&" & _
"client_id=" & GetDBSetting("oauth2_client_id") & "&" & _
"client_secret=" & GetDBSetting("oauth2_client_secret") & "&" & _
"redirect_uri=http%3A%2F%2Flocalhost&" & _
"grant_type=authorization_code"
' Send the request
Call resTxt.sendHTTP("https://www.googleapis.com/oauth2/v4/token", "POST", "application/x-www-form-urlencoded", getAccessToken)
' And receive the tokens
authres = Split(resTxt.responseText, """")
' Now extract the tokens from the received body
' I know this can be done differently with a nice JSON class
aCnt = 0
While aCnt < UBound(authres)
aCnt = aCnt + 1
If authres(aCnt) = "access_token" Then Call SetSetting("oauth2_access_token", authres(aCnt + 2))
If authres(aCnt) = "token_type" Then Call SetSetting("oauth2_token_type", authres(aCnt + 2))
If authres(aCnt) = "refresh_token_" Then Call SetSetting("oauth2_refresh_token", authres(aCnt + 2))
Wend
' And we are done
Set resTxt = Nothing
Call DoCmd.Close(acForm, "Browser")
End Sub
Private Sub startOauth2()
' Here we start stage 1 of the oAuth2 process
Dim svarbody As String
Dim resTxt As New HTTP_Response
' First we create a body to request access
svarbody = "client_id=" & GetDBSetting("oauth2_client_id") & "&" & _
"state=Anything_can_come_here_we_dont_use_it&" & _
"redirect_uri=http%3A%2F%2Flocalhost&" & _
"scope=" & GetDBSetting("oauth2_scope") & "&" & _
"response_type=code&" & _
"access_type=offline"
' Send the request
Call resTxt.sendHTTP("https://accounts.google.com/o/oauth2/v2/auth", "POST", "application/x-www-form-urlencoded", svarbody)
' And write the result to the WebBrowser control on the form
Call Me.WebBrowser1.Object.Document.Write(resTxt.responseText)
Set resTxt = Nothing
End Sub
现在,我已尽我所能将这一点说清楚,但总会有问题。请不要犹豫,我会很乐意回答他们。
我需要从 MS Access 2016 应用程序创建 Google 日历项。
为了能够做到这一点,我需要执行 OAuth2 身份验证,我现在有点绝望。
我可以找到很多提示,但它 none 适用于 MS Access(好吧,有些适用于 MS Access,但它们使用过时的身份验证方法,因此不是很有用)
我真的是第一个尝试这个的人吗?还是有人比我先走这条路并且愿意分享他或她的经验和代码?
非常感谢!
首先,有关 Google 的 oAuth2 的信息以及获取您的 client_id 和 client_secret,请看这里:https://developers.google.com/identity/protocols/OAuth2
好的,开始吧,这就是我构建的。
首先让我们讨论先决条件。有 2 个功能我不会在这里描述,因为它们往往非常个人化。这些函数是:
Public Function GetDBSetting(field as string) as string
Public Sub SetSetting(field as string, value as string)
这些函数获取并设置此功能正常工作所需的设置。您可能希望将它们存储在数据库中 table,但是如何执行完全取决于您,并且不在本答案的范围内。
本方案涉及的设置有:
oauth2_client_id (you get this from google)
oauth2_client_secret (this is given to you by google as well)
oauth2_scope (just pick the scope you need)
oauth2_access_token (this we collect)
oauth2_refresh_token (this we collect)
oauth2_token_type (this we collect)
对于此解决方案,我们需要:
- 带有 WebBrowser 控件(名为 WebBrowser1)的表单(名为 Browser),
- a class 放置 HTTP 请求
- 带有一些工具的模块
class 看起来像这样(将 class 保存为 HTTP_Response):
Option Compare Database
Option Explicit
' A very simple class to send HTTP requests and receive the resulting body
' These variables hold the results
Public Status As Long
Public StatusText As String
Public responseText As String
' This sub simply sends the request and collects the results
' Headers should be an array of strings with the following format: headername:value
Public Sub sendHTTP(URL As String, Optional METHOD As String = "POST", Optional CONTENT As String = "text/plain", Optional BODY As String = "", Optional addAuth As Boolean = False, Optional Headers As Variant)
Dim Http As MSXML2.XMLHTTP60
Dim hdrLine As Variant
Dim hdrarr As Variant
Set Http = New MSXML2.XMLHTTP60
With Http
Call .Open(METHOD, URL)
If CONTENT <> "" Then Call .setRequestHeader("Content-Type", CONTENT)
If addAuth Then Call .setRequestHeader("Authorization", GetDBSetting("oauth2_token_type") & " " & GetDBSetting("oauth2_access_token"))
If IsArray(Headers) Then
For Each hdrLine In Headers
hdrarr = Split(CStr(hdrLine), ":")
Call .setRequestHeader(hdrarr(0), hdrarr(1))
Next
End If
Call .send(BODY)
Status = .Status
StatusText = .StatusText
responseText = .responseText
End With
End Sub
此class仅用于使发送HTTP请求和接收结果更容易。没什么好看的。
模块如下所示:
Option Compare Database
Option Explicit
' A function that checks if the known token is still valid and tries to request a refresh token if it is not
Public Function checkToken() As Boolean
Dim resTxt As New HTTP_Response
Call resTxt.sendHTTP("https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=" & GetDBSetting("oauth2_access_token"))
If resTxt.Status = 200 Then
checkToken = True
Else
checkToken = refreshToken
End If
End Function
' A function that requests a refresh token
Public Function refreshToken() As Boolean
Dim authres() As String
Dim resTxt As New HTTP_Response
Dim svarbody As String
Dim aCnt As Integer
svarbody = "client_secret=" & GetDBSetting("oauth2_client_secret") & "&" & _
"grant_type=refresh_token&" & _
"refresh_token=" & GetDBSetting("oauth2_refresh_token") & "&" & _
"client_id=" & GetDBSetting("oauth2_client_id")
Call resTxt.sendHTTP("https://www.googleapis.com/oauth2/v4/token", , "application/x-www-form-urlencoded", svarbody, False)
If resTxt.Status = 200 Then
authres = Split(resTxt.responseText, """")
aCnt = 0
While aCnt < UBound(authres)
aCnt = aCnt + 1
If authres(aCnt) = "access_token" Then Call SetSetting("oauth2_access_token", authres(aCnt + 2))
If authres(aCnt) = "token_type" Then Call SetSetting("oauth2_token_type", authres(aCnt + 2))
If authres(aCnt) = "refresh_token_" Then Call SetSetting("oauth2_refresh_token", authres(aCnt + 2))
Wend
refreshToken = True
Else
refreshToken = False
End If
End Function
' A sub to revoke a known token
Public Sub revokeToken()
Dim resTxt As New HTTP_Response
if checkToken() Then Call resTxt.sendHTTP("https://accounts.google.com/o/oauth2/revoke?token=" & GetDBSetting("oauth2_access_token"))
End Sub
您可以使用这些功能来确保您拥有有效的 access_token。
如果您没有有效的 access_token,您可以通过请求特定访问权限的 oAuth2 流程(通过您在 oauth2_scope 中设置的值)打开使用命令形成:
Call DoCmd.OpenForm("Browser", acDialog)
表单 VBA 代码如下所示:
Option Compare Database
Private Enum BrowserNavigationFlags
navOpenInNewWindow = 1 ' Open the resource or file in a new window.
navNoHistory = 2 ' Do not add the resource or file to the history list. The new page replaces the current page in the list.
navNoReadFromCache = 4 ' Do not read from the disk cache for this navigation.
navNoWriteToCache = 8 ' Do not write the results of this navigation to the disk cache.
End Enum
Private Sub Form_Load()
Call Me.WebBrowser1.Object.Navigate2("about:blank", navNoReadFromCache)
Call startOauth2
End Sub
Private Sub WebBrowser1_NavigateError(ByVal pDisp As Object, URL As Variant, TargetFrameName As Variant, StatusCode As Variant, Cancel As Boolean)
' Due to the redirect URL pointing to Localhost and we don't have a webserver running at localhost (Just make sure we don't!)
' The browser triggers the NavigateError event when it receives the URL for localhost
' We can now read the URL and extract the received code to request a token
Dim retCode, getAccessToken As String
Dim authres() As String
Dim aCnt As Long
Dim resTxt As New HTTP_Response
' Extract the code from the URL
retCode = Right(URL, Len(URL) - (InStr(1, URL, "&code=") + 5))
' Construct the Body to request a access token and a refresh token
getAccessToken = "code=" & retCode & "&" & _
"client_id=" & GetDBSetting("oauth2_client_id") & "&" & _
"client_secret=" & GetDBSetting("oauth2_client_secret") & "&" & _
"redirect_uri=http%3A%2F%2Flocalhost&" & _
"grant_type=authorization_code"
' Send the request
Call resTxt.sendHTTP("https://www.googleapis.com/oauth2/v4/token", "POST", "application/x-www-form-urlencoded", getAccessToken)
' And receive the tokens
authres = Split(resTxt.responseText, """")
' Now extract the tokens from the received body
' I know this can be done differently with a nice JSON class
aCnt = 0
While aCnt < UBound(authres)
aCnt = aCnt + 1
If authres(aCnt) = "access_token" Then Call SetSetting("oauth2_access_token", authres(aCnt + 2))
If authres(aCnt) = "token_type" Then Call SetSetting("oauth2_token_type", authres(aCnt + 2))
If authres(aCnt) = "refresh_token_" Then Call SetSetting("oauth2_refresh_token", authres(aCnt + 2))
Wend
' And we are done
Set resTxt = Nothing
Call DoCmd.Close(acForm, "Browser")
End Sub
Private Sub startOauth2()
' Here we start stage 1 of the oAuth2 process
Dim svarbody As String
Dim resTxt As New HTTP_Response
' First we create a body to request access
svarbody = "client_id=" & GetDBSetting("oauth2_client_id") & "&" & _
"state=Anything_can_come_here_we_dont_use_it&" & _
"redirect_uri=http%3A%2F%2Flocalhost&" & _
"scope=" & GetDBSetting("oauth2_scope") & "&" & _
"response_type=code&" & _
"access_type=offline"
' Send the request
Call resTxt.sendHTTP("https://accounts.google.com/o/oauth2/v2/auth", "POST", "application/x-www-form-urlencoded", svarbody)
' And write the result to the WebBrowser control on the form
Call Me.WebBrowser1.Object.Document.Write(resTxt.responseText)
Set resTxt = Nothing
End Sub
现在,我已尽我所能将这一点说清楚,但总会有问题。请不要犹豫,我会很乐意回答他们。