我在使用C#中的Gmail SMTP服务器发送电子邮件时遇到问题。在StackOverflow中有很多关于这个问题的主题,但是那里的解决方案都没有使用我的特性。
我的具体情况是:
有人可以提出解决方案吗? 解决方案“禁用两步验证”不是一个有效的解决方案,我们正在寻找一个尊重主要帐户完整性的解决方案。
也许有使用Google的API或任何其他配置参数的解决方案?我怀疑是否有面向C#的解决方案,因为2步验证旨在提供更安全的帐户,但是不是“应用程序专用密码”可以绕过某些应用程序的2步验证吗?
我已生成特定的应用密码:https://security.google.com/settings/security/apppasswords
Less Secure Apps参数不可用:https://www.google.com/settings/security/lesssecureapps
这是我的代码:
public string sendEmailUsingGmail(string email)
{
using (MailMessage mail = new MailMessage("from_account@gmail.com", email)
{
Subject = "Aquaponey today",
Body = "Hello, you are a Unicorn and you will be late for aquaponey today. - God",
IsBodyHtml = true,
Priority = System.Net.Mail.MailPriority.High
})
{
using (SmtpClient smtpClient = new SmtpClient("smtp.gmail.com", 587))
{
smtpClient.Credentials = new System.Net.NetworkCredential()
{
UserName = "my_account@gmail.com",
Password = @"my_specific_app_password"
};
smtpClient.EnableSsl = true;
System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate (object s,
System.Security.Cryptography.X509Certificates.X509Certificate certificate,
System.Security.Cryptography.X509Certificates.X509Chain chain,
System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
return true;
};
smtpClient.Send(mail);
return "OK";
}
}
}
我的客户返回的错误是:
System.Net.Mail.SmtpException:5.5.1需要身份验证。
了解更多信息
如果不可能,可能与How does google recognize a "trusted device" with 2-step verification
有关答案 0 :(得分:1)
我之前尝试使用Google API进行邮件发送。我的理解是你必须建立一个登录(这将触发两步),它为你提供一个开放的会话。然后通过该开放会话,您可以发送邮件。否则,很可能代码在等待登录的后半部分时超时。
更新:您可以使用服务器端身份验证过程,这将需要从最终用户进行一次性登录(必须通过双因素身份验证登录),这将为您提供刷新令牌可以用来重新连接到服务器。信息位于at the Google Developers Docs
摘录
实施服务器端授权
必须使用OAuth 2.0授权对Gmail API的请求 证书。您应该在应用程序时使用服务器端流程 需要代表用户访问Google API,例如 用户离线。这种方法需要通过一次性 从客户端到服务器的授权码;使用此代码 获取访问令牌并刷新服务器的令牌。
以下是该网站的示例python代码(我知道您正在使用C#)
将CLIENTSECRETS_LOCATION值替换为client_secrets.json文件的位置。
import logging
from oauth2client.client import flow_from_clientsecrets
from oauth2client.client import FlowExchangeError
from apiclient.discovery import build
# ...
# Path to client_secrets.json which should contain a JSON document such as:
# {
# "web": {
# "client_id": "[[YOUR_CLIENT_ID]]",
# "client_secret": "[[YOUR_CLIENT_SECRET]]",
# "redirect_uris": [],
# "auth_uri": "https://accounts.google.com/o/oauth2/auth",
# "token_uri": "https://accounts.google.com/o/oauth2/token"
# }
# }
CLIENTSECRETS_LOCATION = '<PATH/TO/CLIENT_SECRETS.JSON>'
REDIRECT_URI = '<YOUR_REGISTERED_REDIRECT_URI>'
SCOPES = [
'https://www.googleapis.com/auth/gmail.readonly',
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
# Add other requested scopes.
]
class GetCredentialsException(Exception):
"""Error raised when an error occurred while retrieving credentials.
Attributes:
authorization_url: Authorization URL to redirect the user to in order to
request offline access.
"""
def __init__(self, authorization_url):
"""Construct a GetCredentialsException."""
self.authorization_url = authorization_url
class CodeExchangeException(GetCredentialsException):
"""Error raised when a code exchange has failed."""
class NoRefreshTokenException(GetCredentialsException):
"""Error raised when no refresh token has been found."""
class NoUserIdException(Exception):
"""Error raised when no user ID could be retrieved."""
def get_stored_credentials(user_id):
"""Retrieved stored credentials for the provided user ID.
Args:
user_id: User's ID.
Returns:
Stored oauth2client.client.OAuth2Credentials if found, None otherwise.
Raises:
NotImplemented: This function has not been implemented.
"""
# TODO: Implement this function to work with your database.
# To instantiate an OAuth2Credentials instance from a Json
# representation, use the oauth2client.client.Credentials.new_from_json
# class method.
raise NotImplementedError()
def store_credentials(user_id, credentials):
"""Store OAuth 2.0 credentials in the application's database.
This function stores the provided OAuth 2.0 credentials using the user ID as
key.
Args:
user_id: User's ID.
credentials: OAuth 2.0 credentials to store.
Raises:
NotImplemented: This function has not been implemented.
"""
# TODO: Implement this function to work with your database.
# To retrieve a Json representation of the credentials instance, call the
# credentials.to_json() method.
raise NotImplementedError()
def exchange_code(authorization_code):
"""Exchange an authorization code for OAuth 2.0 credentials.
Args:
authorization_code: Authorization code to exchange for OAuth 2.0
credentials.
Returns:
oauth2client.client.OAuth2Credentials instance.
Raises:
CodeExchangeException: an error occurred.
"""
flow = flow_from_clientsecrets(CLIENTSECRETS_LOCATION, ' '.join(SCOPES))
flow.redirect_uri = REDIRECT_URI
try:
credentials = flow.step2_exchange(authorization_code)
return credentials
except FlowExchangeError, error:
logging.error('An error occurred: %s', error)
raise CodeExchangeException(None)
def get_user_info(credentials):
"""Send a request to the UserInfo API to retrieve the user's information.
Args:
credentials: oauth2client.client.OAuth2Credentials instance to authorize the
request.
Returns:
User information as a dict.
"""
user_info_service = build(
serviceName='oauth2', version='v2',
http=credentials.authorize(httplib2.Http()))
user_info = None
try:
user_info = user_info_service.userinfo().get().execute()
except errors.HttpError, e:
logging.error('An error occurred: %s', e)
if user_info and user_info.get('id'):
return user_info
else:
raise NoUserIdException()
def get_authorization_url(email_address, state):
"""Retrieve the authorization URL.
Args:
email_address: User's e-mail address.
state: State for the authorization URL.
Returns:
Authorization URL to redirect the user to.
"""
flow = flow_from_clientsecrets(CLIENTSECRETS_LOCATION, ' '.join(SCOPES))
flow.params['access_type'] = 'offline'
flow.params['approval_prompt'] = 'force'
flow.params['user_id'] = email_address
flow.params['state'] = state
return flow.step1_get_authorize_url(REDIRECT_URI)
def get_credentials(authorization_code, state):
"""Retrieve credentials using the provided authorization code.
This function exchanges the authorization code for an access token and queries
the UserInfo API to retrieve the user's e-mail address.
If a refresh token has been retrieved along with an access token, it is stored
in the application database using the user's e-mail address as key.
If no refresh token has been retrieved, the function checks in the application
database for one and returns it if found or raises a NoRefreshTokenException
with the authorization URL to redirect the user to.
Args:
authorization_code: Authorization code to use to retrieve an access token.
state: State to set to the authorization URL in case of error.
Returns:
oauth2client.client.OAuth2Credentials instance containing an access and
refresh token.
Raises:
CodeExchangeError: Could not exchange the authorization code.
NoRefreshTokenException: No refresh token could be retrieved from the
available sources.
"""
email_address = ''
try:
credentials = exchange_code(authorization_code)
user_info = get_user_info(credentials)
email_address = user_info.get('email')
user_id = user_info.get('id')
if credentials.refresh_token is not None:
store_credentials(user_id, credentials)
return credentials
else:
credentials = get_stored_credentials(user_id)
if credentials and credentials.refresh_token is not None:
return credentials
except CodeExchangeException, error:
logging.error('An error occurred during code exchange.')
# Drive apps should try to retrieve the user and credentials for the current
# session.
# If none is available, redirect the user to the authorization URL.
error.authorization_url = get_authorization_url(email_address, state)
raise error
except NoUserIdException:
logging.error('No user ID could be retrieved.')
# No refresh token has been retrieved.
authorization_url = get_authorization_url(email_address, state)
raise NoRefreshTokenException(authorization_url)