VB.NET/C#实现Gmail OAuth 2.0邮件认证的方案咨询
C#/VB.NET 桌面应用对接Gmail OAuth2.0 单账号实现方案
适用于仅使用固定单个Gmail账号发信、不需要对接其他Google API的Windows桌面场景,无需依赖重型Google SDK
前置配置
- 进入Google Cloud控制台新建项目,搜索启用Gmail API
- 打开OAuth同意屏幕配置页,用户类型选「外部」,填写基础应用信息后,在测试用户列表添加你要使用的目标Gmail账号
个人自用单账号场景不需要提交Google应用审核,添加测试账号即可正常使用,无额外调用限制 - 进入凭证创建页,新建OAuth2.0客户端ID,应用类型选择「桌面应用」,创建完成后保存生成的客户端ID、客户端密钥
- 授权范围按需选择:仅发信填
https://www.googleapis.com/auth/gmail.send,需要收信+发信填https://mail.google.com/
核心实现逻辑
单固定账号场景不需要做复杂的多用户交互、每次弹浏览器授权:
- 首次运行一次临时授权流程,拿到长期有效的刷新令牌,永久存储到本地即可
- 后续程序每次需要连接Gmail服务时,用刷新令牌向Google令牌接口请求1小时有效期的访问令牌,直接传给邮件组件走XOAUTH2认证即可,和原有账号密码登录的流程体验基本一致
可直接复用的代码
所有代码基于原生HttpClient实现,无额外强制依赖,JSON解析可以用你项目里已有的JSON库,不需要额外装包。
日常运行用:刷新令牌换访问令牌(无交互)
VB.NET 版本
Imports System.Net.Http Imports System.Collections.Generic Imports System.Text ' 如果你项目里没有JSON解析库,可以手动拆分响应字符串拿access_token,不需要强制装Newtonsoft.Json Imports Newtonsoft.Json Public Class GmailOAuthHelper ' 以下三个值替换成你自己配置拿到的内容 Private Const ClientId As String = "你的OAuth客户端ID" Private Const ClientSecret As String = "你的OAuth客户端密钥" Private Const SavedRefreshToken As String = "首次授权拿到的永久刷新令牌" Private Shared ReadOnly _httpClient As New HttpClient() Public Shared Async Function GetGmailAccessTokenAsync() As Task(Of String) Dim requestParams = New Dictionary(Of String, String) From { {"client_id", ClientId}, {"client_secret", ClientSecret}, {"refresh_token", SavedRefreshToken}, {"grant_type", "refresh_token"} } Dim response = Await _httpClient.PostAsync( "https://oauth2.googleapis.com/token", New FormUrlEncodedContent(requestParams) ) response.EnsureSuccessStatusCode() Dim responseContent = Await response.Content.ReadAsStringAsync() Dim tokenResult = JsonConvert.DeserializeObject(Of TokenResponse)(responseContent) Return tokenResult.access_token End Function Private Class TokenResponse Public Property access_token As String Public Property expires_in As Integer Public Property token_type As String Public Property refresh_token As String End Class End Class
C# 版本
using System.Net.Http; using System.Collections.Generic; using System.Threading.Tasks; using Newtonsoft.Json; public class GmailOAuthHelper { // 以下三个值替换成你自己配置拿到的内容 private const string ClientId = "你的OAuth客户端ID"; private const string ClientSecret = "你的OAuth客户端密钥"; private const string SavedRefreshToken = "首次授权拿到的永久刷新令牌"; private static readonly HttpClient _httpClient = new HttpClient(); public static async Task<string> GetGmailAccessTokenAsync() { var requestParams = new Dictionary<string, string> { {"client_id", ClientId}, {"client_secret", ClientSecret}, {"refresh_token", SavedRefreshToken}, {"grant_type", "refresh_token"} }; var response = await _httpClient.PostAsync( "https://oauth2.googleapis.com/token", new FormUrlEncodedContent(requestParams) ); response.EnsureSuccessStatusCode(); var responseContent = await response.Content.ReadAsStringAsync(); var tokenResult = JsonConvert.DeserializeObject<TokenResponse>(responseContent); return tokenResult.access_token; } private class TokenResponse { public string access_token { get; set; } public int expires_in { get; set; } public string token_type { get; set; } public string refresh_token { get; set; } } }
一次性用:首次授权拿刷新令牌
这段逻辑不需要写到正式发布的程序里,单独建个临时控制台项目跑一次,拿到refresh_token存下来就可以删掉,避免给正式项目加多余逻辑:
Imports System.Net Imports System.Diagnostics ' 放在GmailOAuthHelper类里即可 Public Shared Async Function GetRefreshTokenOneTimeAsync() As Task(Of String) Dim redirectUri = "http://localhost:9000" ' 构造授权URL,必须加access_type=offline&prompt=consent才会返回刷新令牌 Dim authUrl = $"https://accounts.google.com/o/oauth2/v2/auth?client_id={Uri.EscapeDataString(ClientId)}&redirect_uri={Uri.EscapeDataString(redirectUri)}&response_type=code&scope={Uri.EscapeDataString("https://mail.google.com/")}&access_type=offline&prompt=consent" ' 打开系统默认浏览器跳授权页 Process.Start(New ProcessStartInfo(authUrl) With {.UseShellExecute = True}) ' 临时监听本地端口拿Google回调的授权码 Dim listener = New HttpListener() listener.Prefixes.Add($"{redirectUri}/") listener.Start() Dim context = Await listener.GetContextAsync() Dim authCode = context.Request.QueryString("code") ' 给浏览器返回成功提示 Dim successPage = Encoding.UTF8.GetBytes("<html><body style='padding:40px;font-size:18px'>授权完成,可以关闭当前页面</body></html>") context.Response.ContentType = "text/html; charset=utf-8" context.Response.ContentLength64 = successPage.Length Await context.Response.OutputStream.WriteAsync(successPage, 0, successPage.Length) context.Response.Close() listener.Stop() ' 用授权码换令牌 Dim tokenRequestParams = New Dictionary(Of String, String) From { {"code", authCode}, {"client_id", ClientId}, {"client_secret", ClientSecret}, {"redirect_uri", redirectUri}, {"grant_type", "authorization_code"} } Dim tokenResponse = Await _httpClient.PostAsync( "https://oauth2.googleapis.com/token", New FormUrlEncodedContent(tokenRequestParams) ) tokenResponse.EnsureSuccessStatusCode() Dim tokenContent = Await tokenResponse.Content.ReadAsStringAsync() Dim tokenResult = JsonConvert.DeserializeObject(Of TokenResponse)(tokenContent) ' 这里拿到的refresh_token就是永久有效的,存到代码/配置里即可 Return tokenResult.refresh_token End Function
对接现有IMapX组件注意事项
- 认证时用户名依然填完整的Gmail邮箱地址,和之前的配置一致
- 原来填写Gmail应用密码的位置,替换成
GetGmailAccessTokenAsync()返回的访问令牌 - 组件认证模式选择
XOAUTH2,如果IMapX没有提供显式的XOAUTH2选项,可以手动拼接标准XOAUTH2认证串转Base64传入,格式为:user={你的完整Gmail地址}+ Chr(1) +auth=Bearer {访问令牌}+ Chr(1) + Chr(1) - 访问令牌有效期为1小时,不需要频繁刷新,程序启动时刷一次、或者每次发信前判断过期时间刷新即可
- 只要不手动在Google账号安全设置里撤销应用授权,刷新令牌永久有效,不需要重复走授权流程
内容的提问来源于stack exchange,提问作者Derpopotumus
相关产品推荐
相关产品推荐

