在C#中使用Google OAuth 2.0作为ASP.NET的用户登录——基本概述

目录

介绍

首先,在Google API控制台上为您的网站注册并启用Google API访问权限

建立网站

获取授权码

在后端获取授权码

获取OAuth 2.0访问令牌

下一步:使用AccessToken访问另一个Google API端点以获取用户的电子邮件


介绍

您可以使用Google OAuth登录做很多事情。

然而,在本文中,我想重点介绍一个基本功能:使用Google OAuth处理您网站的用户登录。

让我们开始吧。

有关更多详细信息,请参阅官方Google文档:

首先,在Google API控制台上为您的网站注册并启用Google API访问权限

Google API控制台:https://console.developers.google.com/apis/library

步骤 1:创建新项目。

步骤 2:设置OAuth同意屏幕。

按照屏幕上的说明填写应用的详细信息。

步骤 3:范围

单击添加或删除范围。在这里,我们将只选择一个范围:

../auth/userinfo.email

步骤 4:测试用户

添加一些用户用于测试目的。

步骤 5:创建凭据

导航到 API和服务>凭据OAuth客户端ID >创建凭据

选择应用程序类型作为“Web应用程序

填写授权的JavaScript授权的重定向URI

这两个URL至关重要。请确保稍后在代码中使用这些确切的URL

授权重定向URI 应是Google API在成功登录后将值返回到您的网站的确切目标网址。

步骤 6:获取客户端ID和客户端密钥

创建OAuth客户端后,复制客户端ID 客户端密码,因为稍后在代码中将需要它们。

建立网站

要开始Google OAuth登录,请首先准备以下参数:

  • access_type=”online” (此选项可以更改为离线,本文后面将对此进行讨论)
  • client_id= <your Google client id>——Google API注册期间获得
  • redirect_uri= Google登录后的目标重定向页面——与您在Google API上注册的内容完全相同
  • response_type=”code”
  • scope=”email”—— Google API注册期间定义的允许范围
  • prompt=”consent”——通知用户应用程序请求的权限
  • login_hint——(可选)如果您的应用程序知道哪个用户正在尝试进行身份验证,则可以使用此参数向Google身份验证服务器提供提示。服务器使用此提示来简化登录流程,方法是在登录表单中预先填写电子邮件字段,或者选择适当的多点登录会话。

需要上述参数才能传递到Google OAuth登录页面或API端点,即:

https://accounts.google.com/o/oauth2/v2/auth

有多种方法可以启动Google OAuth登录。最简单的方法之一是在您的登录页面上包含一个链接,将用户重定向到Google OAuth登录页面。将所有参数附加为查询字符串。

使用HTML <a>标记(用于文档目的的换行符):

<a href="https://accounts.google.com/o/oauth2/v2/auth?
access_type=online
&client_id=xxxxx
&redirect_uri=https%3A//mywebsite.com/oauth
&response_type=code
&scope=email
&prompt=consent">Sign In With Google</a>

或者,使用JavaScript

<button type="button" onclick="signInWithGoogle();">Sign In With Google</button>

<script>
    function signInWithGoogle() {
        
        const clientId = 'xxxxxxxxxxxxxxxxxxxxxxxx';
        const redirectUri = encodeURIComponent('https://mywebsite.com/oauth');

        const url = `https://accounts.google.com/o/oauth2/v2/auth?
        access_type=online
        &client_id=${clientId}
        &redirect_uri=${redirectUri}
        &response_type=code
        &scope=email
        &prompt=consent`;

        window.location.href = url;
    }
</script>

或者,从后端使用C#重定向(用于文档目的的换行符):

public static void SignInWithGoogle()
{
    string clientId = "xxxxxxxxxxxxxxxxxxxxxxxx";
    string redirectUri = HttpUtility.UrlEncode("https://mywebsite.com/oauth");

    string url = $@"https://accounts.google.com/o/oauth2/v2/auth?
    access_type=online
    &client_id={clientId}
    &redirect_uri={redirectUri}
    &response_type=code
    &scope=email
    &prompt=consent";

    HttpContext.Current.Response.Redirect(url, true);
}

获取授权码

用户在Google登录页面上成功登录后,Google会将他们连同一些参数(查询string)重定向回您的网站。

目标重定向URL的示例为:

https://mywebsite.com/oauth

or

https://mywebsite.com/login

ASP.NET WebForms中,物理页以文件扩展名.aspx结尾,物理路径可能如下所示:

https://mywebsite.com/oauth.aspx

or

https://mywebsite.com/pages/user/login/google-login.aspx

可以执行路由。创建或打开Global.asax文件并添加URL路由:

void Application_Start(object sender, EventArgs e)
{
    RouteTable.Routes.MapPageRoute("oauth", "oauth", "~/oauth.aspx");

    // or

    RouteTable.Routes.MapPageRoute("google-login", 
        "pages/user/login/google-login", "~/oauth.aspx");
}

我写了一篇关于在一行中一次路由所有页面的文章,这是链接:

https://adriancs.com/aspnet-webforms/419/automatic-route-all-pages-in-asp-net-webforms/

**注意:Google API无需使用路由即可正常工作;如果您愿意,您仍然可以使用绝对文件路径。例如:

https://mywebsite.com/login.aspx
https://mywebsite.com/oauth.aspx
https://mywebsite.com/google-login.aspx

以下参数将作为查询字符串一起返回到您的网站:

  • code=<授权码>
  • scope=<允许您从Google用户那里访问的数据>
  • authuser=<在当前浏览器上登录的用户的索引号>
  • prompt = “consent”<用户将被告知您的应用正在请求的权限>

完整URL示例(用于文档目的的换行符):

https://mywebsite.com/oauth?
code=xxxxxxx
&scope=email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+openid
&authuser=0
&prompt=consent

在后端获取授权码

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        if (Request.QueryString["code"] != null)
        {
            string authorizationCode = Request.QueryString["code"] + "";
        }
    }
}

获取OAuth 2.0访问令牌

准备以下参数:

  • client_id=您的Google客户端ID
  • client_secret=您的Google客户端密钥
  • code=在上一步中获取的授权码
  • grant_type = “authorization_code”(固定字符串)
  • redirect_uri= Google登录期间使用的重定向网址
  • access_type = “online”(固定字符串)

**注意:access_type还有另一个选项值,即“offline”。这将在本文的最后一部分进行讨论。

这些参数将发送到GoogleOAuth 2.0端点以获取访问令牌

https://oauth2.googleapis.com/token

可以使用POSTGET请求发送参数。

发送POST请求的示例:

using System.Net.Http;

public async Task GetAccessTokenAsync()
{
    string url = "https://oauth2.googleapis.com/token";

    var dicData = new Dictionary<string, string>();

    dicData["client_id"] = google_api_client_id;
    dicData["client_secret"] = google_api_client_secret;
    dicData["code"] = authorization_code;
    dicData["grant_type"] = "authorization_code";
    dicData["redirect_uri"] = google_api_redirect_url;
    dicData["access_type"] = "online";

    try
    {
        using (var client = new HttpClient())
        using (var content = new FormUrlEncodedContent(dicData))
        {
            HttpResponseMessage response = await client.PostAsync(url, content);
            string json = await response.Content.ReadAsStringAsync();
        }
    }
    catch (Exception ex)
    {
        // error
    }
}

发送GET请求的示例(使用string插值构造的 url):

public async Task GetAccessTokenAsync()
{
    string baseUrl = "https://oauth2.googleapis.com/token";
    string encodedRedirectUri = HttpUtility.UrlEncode(google_api_redirect_url);
    
    string urlWithParameters = $"{baseUrl}?client_id={google_api_client_id}&
    client_secret={google_api_client_secret}&code={authorizationCode}&
    grant_type=authorization_code&redirect_uri={encodedRedirectUri}&access_type=online";
    
    string json = "";
    
    using (var client = new HttpClient())
    {
        HttpResponseMessage response = await client.GetAsync(urlWithParameters);
        json = await response.Content.ReadAsStringAsync();
    }
}

发送GET请求的示例(使用Dictionary/ NameValueCollection构造的url):

var dicData = new Dictionary<string, string>()
{
    { "client_id", google_api_client_id },
    { "client_secret", google_api_client_secret },
    { "code", authorizationCode },
    { "grant_type", "authorization_code" },
    { "redirect_uri", google_api_redirect_url },
    { "access_type", "online" }
};

string baseUrl = "https://oauth2.googleapis.com/token";

var query = HttpUtility.ParseQueryString(string.Empty);
foreach (var pair in dicData)
{
    query[pair.Key] = pair.Value;
}

string urlWithParameters = $"{baseUrl}?{query.ToString()}";

string json = "";

using (var client = new HttpClient())
{
    HttpResponseMessage response = await client.GetAsync(urlWithParameters);
    json = await response.Content.ReadAsStringAsync();
}

Google将以JSON格式返回结果。

成功请求示例:

{
  "access_token": "xxxxxxxxxxx",
  "expires_in": 3599,
  "scope": "https://www.googleapis.com/auth/userinfo.email openid",
  "token_type": "Bearer",
  "id_token": "xxxxxxxxxxxxxxxx"
}

错误请求示例:

// example 1:
{
  "error": "invalid_grant",
  "error_description": "Bad Request"
}

// example 2:
{
  "error": "redirect_uri_mismatch",
  "error_description": "Bad Request"
}

生成C#类对象以存储响应信息:

public class OAuthTokenResponse
{
    public string access_token { get; set; }
    public int expires_in { get; set; }
    public string refresh_token { get; set; }
    public string scope { get; set; }
    public string token_type { get; set; }
    public string id_token { get; set; }
    public string error { get; set; }
    public string error_description { get; set; }

    public bool IsSuccess => string.IsNullOrEmpty(error);
}

然后,使用System.Text.JsonJSON转换为类对象:

using System.Text.Json;

OAuthTokenResponse tokenResponse = JsonSerializer.Deserialize<OAuthTokenResponse>(json);

string AccessToken = "";

if (tokenResponse.IsSuccess) 
{
    // success
    AccessToken = tokenResponse.access_token;
}
else
{
    // error
}

已获取访问令牌。您可以使用此令牌访问或保存用户Google帐户中的数据,例如阅读或发送电子邮件、访问或保存日历事件、获取Google云端硬盘中的文件列表等。

在本文中,我们只对获取用户的电子邮件地址感兴趣。访问令牌的生存期为一小时,足以从Google API检索用户的电子邮件地址。在我们的例子中,访问令牌将只使用一次。

下一步:使用AccessToken访问另一个Google API端点以获取用户的电子邮件

此操作是使用GET请求完成的。

使用带有授权请求标头的GET请求的示例(推荐,更安全):

using System.Net.Http;
using System.Net.Http.Headers;

public async Task GetEmail()
{
    string json = "";
    string url = $"https://www.googleapis.com/oauth2/v2/userinfo?fields=email";
    
    using (var client = new HttpClient())
    {
        // set the access token at the request header
        client.DefaultRequestHeaders.Authorization = 
            new AuthenticationHeaderValue("Bearer", AccessToken);
    
        HttpResponseMessage response = await client.GetAsync(url);
        json = await response.Content.ReadAsStringAsync();
    }
}

GET请求与在查询字符串中发送的访问令牌一起使用的示例(安全性较低):

public async Task GetEmail()
{
    string json = "";
    string url = $"https://www.googleapis.com/oauth2/v2/userinfo?fields=email&oauth_token={AccessToken}";
    
    using (var client = new HttpClient())
    {
        HttpResponseMessage response = await client.GetAsync(url);
        json = await response.Content.ReadAsStringAsync();
    }
}

JSON返回内容:

成功请求示例:

{
  "email": "somebody@gmail.com"
}

失败请求示例:

{
  "error": {
    "code": 401,
    "message": "Request is missing required authentication credential. 
     Expected OAuth 2 access token, login cookie or other valid authentication credential.
     See https://developers.google.com/identity/sign-in/web/devconsole-project.",
    "status": "UNAUTHENTICATED"
  }

结合JSON的两个结果,我们可以创建一个C#Object来处理这两种情况:

public class ApiEmailResponse
{
    public string email { get; set; }
    public ApiError error { get; set; }

    public bool IsSuccess => error == null;

    public class ApiError
    {
        public int code { get; set; }
        public string message { get; set; }
        public string status { get; set; }
    }
}

JSON转换为类对象:

ApiResponse emailResponse = JsonSerializer.Deserialize<ApiEmailResponse>(json);

string UserEmail = "";

if (emailResponse.IsSuccess) 
{
    // success
    UserEmail = emailResponse.email;
}
else
{
    // failed
}

此时已成功获取用户的电子邮件。

在结束本文之前,让我们更深入地研究一下访问令牌

访问令牌是一种数字密钥,它使应用能够访问用户的帐号或Google服务上的数据,而无需用户的密码。如前所述,访问令牌的生命周期为一小时。过期后,您的应用程序必须重复Google登录过程。如果我们唯一关心的是获取用户的电子邮件,那么这个持续时间就足够了。但是,对于开发第三方应用程序(例如访问用户的Google日历或Google云端硬盘的应用程序),每小时提示用户登录一次是不切实际的。

请记住,在启动Google登录时,有一个参数access_type=online

如果将access_type设置为offline,您将获得另一个附加参数refresh_token

{
  "access_token": "xxxxxxxxxxx",
  "expires_in": 3599,
  "refresh_token": "xxxxxxxxxx",
  "scope": "https://www.googleapis.com/auth/userinfo.email openid",
  "token_type": "Bearer",
  "id_token": "xxxxxxxxxxxxxxxx"
}

expires_in表示access_token的剩余有效时间。缓存此值,一旦它即将过期,或者当access_token无法从Google访问数据时,使用refresh_token获取新的access_token

string AccessToken = "";

async void RenewAccessToken()
{
    string url = "https://oauth2.googleapis.com/token";

    var dicData = new Dictionary<string, string>()
    {
        { "client_id", google_api_client_id },
        { "client_secret", google_api_client_secret },
        { "refresh_token", RefreshToken },
        { "grant_type", "refresh_token" }
    };

    string url = "https://oauth2.googleapis.com/token";
    
    var content = new FormUrlEncodedContent(dicData);
    
    string json = "";
    
    using (var client = new HttpClient())
    {
        HttpResponseMessage response = await client.PostAsync(url, content);
        json = await response.Content.ReadAsStringAsync();
        
        var tokenResponse = 
            JsonSerializer.Deserialize<OAuthTokenResponse>(json, jsonOptions);
        
        AccessToken = tokenResponse.access_token;
    }
}

这是返回结果(JSON)的示例:

{
  "access_token": "xxxxxxx",
  "expires_in": 3599,
  "scope": "https://www.googleapis.com/auth/userinfo.email openid",
  "token_type": "Bearer",
  "id_token": "xxxxxxxxxxxxxx"
}

你有它,新的access_token

现在,在您的网站上,通过使用获得的电子邮件地址,您可以继续创建一个新的用户帐户或登录用户(如果已经创建了用户帐户)。

在您的Web应用程序中,可能有一些方法可以创建会话令牌并使用现有Cookie实现自动登录。

当用户首次登录您的网站时,将创建一个新的登录会话令牌。此令牌被保存,然后发送到用户的浏览器以存储为cookie

每次用户重新打开浏览器时,cookie都会发送回服务器。然后,服务器从数据库中检索用户信息。如果Cookie中的会话令牌与数据库中的会话令牌匹配,则执行自动登录,并延长Cookie的生存期(到期日期)。

会话Cookie通常具有到期日期。如果会话令牌Cookie过期,系统会将用户重定向到Google登录页面以重新启动身份验证过程。

有多种方法可以管理用户登录会话,这可能是另一篇文章的主题,也许是第2部分。

Google OAuth 2.0登录提供了广泛的自定义选项。如果您有任何其他想法,请随时在评论区分享。

https://www.codeproject.com/Articles/5376012/Using-Google-OAuth-2-0-as-User-Sign-In-for-ASP-NET

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Vue项目使用微信OAuth2.0授权登录,可以通过以下步骤实现: 1. 在微信公众平台或开放平台申请应用,并获取到appID和appSecret。 2. 在Vue项目安装wechat-oauth模块,该模块提供了微信OAuth2.0的相关接口。 ``` npm install wechat-oauth ``` 3. 在Vue项目的后端服务器,编写一个处理微信OAuth2.0授权登录的回调接口,并在该接口调用wechat-oauth模块提供的接口,实现用户授权登录。 ``` const OAuth = require('wechat-oauth'); const client = new OAuth(appId, appSecret); // 获取授权地址并重定向到该地址 router.get('/wechat/login', async (ctx, next) => { const redirectUrl = client.getAuthorizeURL( 'http://your-redirect-url', '', 'snsapi_userinfo' ); ctx.redirect(redirectUrl); }); // 处理授权回调 router.get('/wechat/callback', async (ctx, next) => { const code = ctx.query.code; const token = await client.getAccessToken(code); const openid = token.data.openid; const userInfo = await client.getUser(openid); // TODO: 处理用户信息 }); ``` 4. 在Vue项目,提供一个“使用微信登录”的按钮,点击该按钮时,重定向到后端服务器的授权接口,实现用户授权登录。 ``` <template> <div> <button @click="loginWithWechat">使用微信登录</button> </div> </template> <script> export default { methods: { loginWithWechat() { window.location.href = 'http://your-server/wechat/login'; } } } </script> ``` 需要注意的是,使用微信OAuth2.0授权登录,需要用户在微信客户端进行操作,因此需要在移动端或微信公众号使用。同时,需要在微信公众平台或开放平台配置授权回调地址,并保证该地址可以被访问到。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值