簡體   English   中英

如何從 c# asp.net 中的私有提供程序實現 OpenID Connect

[英]how to implement OpenID Connect from a private provider in the c# asp.net

我有一個 ASP.NET MVC 應用程序,需要從Private OpenID Connect (OIDC) Provider集成 OpenID Connect 身份驗證,流程包含以下步驟:

  1. 用戶點擊登錄

  2. 它將使用以下HTTP GET請求將用戶重定向到私有 OIDC 站點進行身份驗證: 在此處輸入圖片說明

  3. 成功登錄私有 OIDC 站點后,它將重定向回我的站點並獲取 uri, code結果如下所示: 在此處輸入圖片說明

  4. 然后我將需要使用上面的code並對私有 ODIC 令牌端點進行HTTP POST調用以獲取該用戶的訪問令牌。

所以,我的questions #1是:如何在 c# asp.net 應用程序中實現它?

另外,我在郵遞員“獲取新訪問令牌”中嘗試了這個,我得到了令牌。

正如您在我提供所有參數並單擊“請求令牌”后所看到的那樣,它會彈出登錄窗口, 在此處輸入圖片說明 登錄成功后顯示token 在此處輸入圖片說明

我的questions #2是:類似於問題 #1,無論如何在 c# asp.net 應用程序中實現它? 就像在一個asp.net的MVC應用程序,添加由第1圖像中的網址,當用戶點擊它會重定向回用的myapp鏈接按鈕code ,然后使用這個代碼,以使在stpe3 HTTP POST調用。

您可以在 GitHub 上找到一個開源示例 該許可證非常寬松,並且有據可查。 我已經在各種研討會和培訓中使用過它,所以大部分錯誤都已經解決了。 我建議你深入研究。 不過,為了完整起見,我將在這里描述一般過程,並將其用作解釋的基礎。

任何實現 OpenID Connect 代碼流的 Web 應用程序都將包括兩個部分:

  1. 流量的開始和
  2. 回調的處理

執行這兩件事的應用程序稱為“客戶端”或“依賴方”。 此客戶端使用 OpenID Connect 協議與之通信的對象稱為 OpenID Connect 提供程序 (OP),通常也稱為身份提供程序 (IdP)。

客戶端實現的第一部分將顯示一個包含按鈕的視圖。 此按鈕將是典型的“登錄”或“登錄”按鈕。 請注意,這是可選的,如果應用程序檢測到用戶沒有會話,它可能會立即將用戶重定向到 OP。 但是,鑒於您上面的問題,您的情況並非如此,客戶端將首先呈現顯示此類按鈕的視圖。 視圖可能如下所示:

<div>
    @if(Session.Count == 0) {
        <p>
            This is a demo application to demonstrate the use for OAuth2 
            and OpenID Connect. 
        </p>

        <p>
            Pressing Sign In will redirect you to @ViewData["server_name"] 
            and authorize the application to access your profile info. The 
            data will only be used to demonstrate the possibilities of the 
            OpenID Connect protocol and will not be stored. Be sure to 
            revoke access when you are satisfied.
        </p>
        <div>
            <a href="/login">Sign In</a>
        </div>
    } else {
      // ...
    }
</div>

該視圖將由一個非常基本的控制器呈現,該控制器連接在Global.asax.cs建立的路由配置中。 單擊登錄按鈕后,OpenID Connect 部分將啟動。 處理此請求的控制器將簡單地重定向到 OP 的授權端點。 在最基本的情況下,這可能如下所示:

public class LoginController : Controller
{
    private static string start_oauth_endpoint = Helpers.Client.Instance.GetAuthnReqUrl();

    public ActionResult Index()
    {
        return Redirect(start_oauth_endpoint);
    }
}

有趣的部分是如何獲得授權端點。 這可以是硬編碼的,在Web.config定義,或者從 OP 的元數據中獲得。 在我上面引用的示例中,它在應用程序啟動時獲取 OP 的元數據。 這是在位於 Web 應用程序的App_Start目錄中的AppConfig中完成的。 這使用/.well-known/openid-configuration對發行者 ID(位於Web.config )執行 HTTP GET 請求。 在應用程序啟動時獲取此元數據而不是將其全部放入配置中的原因是為了減少 OP 和客戶端的耦合。

上面截圖中執行的重定向將有一些重要的查詢字符串參數。 其中一些將在設計時已知,並將被硬編碼。 其他將在Web.config配置。 有些將在運行時動態計算。 下面列出了這些:

client_id
此 MVC Web 應用程序的客戶端 ID。
response_type
OP 應該使用的響應類型。 在您的情況下,這將始終是code
scope
客戶端請求的訪問范圍。 這將至少包括openid
redirect_uri
在用戶對客戶端進行身份驗證和授權后,OP 應將用戶發送到的重定向 URI。

也可以發送其他請求參數。 為了幫助您確定要發送的內容以及它們對流程的影響,請查看 oauth.tools 這就像“OAuth 和 OpenID Connect 的郵遞員”。 這是夢幻般的; 你會喜歡的。 在那里,您可以使用各種參數形成各種 OAuth 和 OpenID Connect 流。

一旦重定向到 OP,用戶將進行身份驗證。 用戶可能還必須同意客戶端訪問其受保護資源。 在任何情況下,OP 都會在此之后將用戶重定向到回調。 這是實現的第二部分。

在這里,我們將有一個CallbackController (或類似的東西)。 它看起來像這樣(以最簡單的形式):

public class CallbackController : Controller
{
    public ActionResult Index()
    {
        try
        {
            string responseString = Helpers.Client.Instance
                .GetToken(Request.QueryString["code"]);

            SaveDataToSession(responseString);
        }
        catch (Exception e)
        {
            Session["error"] = e.Message;
        }

        return Redirect("/");
    }
}

此代碼段的重要部分是它從查詢字符串中獲取code ,並向 OP 的令牌端點(也通過解析 OP 的元數據定位)發出 HTTP POST 請求。 如果成功,它將在會話中保存響應以供以后使用。 GetToken方法看起來像這樣:

public String GetToken(String code)
{
    var values = new Dictionary<string, string>
    {
        { "grant_type", "authorization_code" },
        { "client_id", client_id},
        { "client_secret", client_secret },
        { "code" , code },
        { "redirect_uri", redirect_uri}
    };


    HttpClient tokenClient = new HttpClient();
    var content = new FormUrlEncodedContent(values);
    var response = tokenClient.PostAsync(token_endpoint, content).Result;

    if (response.IsSuccessStatusCode)
    {
        var responseContent = response.Content;

        return responseContent.ReadAsStringAsync().Result;
    }

    throw new OAuthClientException("Token request failed with status code: " + response.StatusCode);
}

這會將代碼發送到 OP 並獲取訪問令牌、ID 令牌和刷新令牌作為交換。 這段代碼的重要部分是:

  • 內容是形式 URL 編碼的,而不是JSON。 這是一個常見的錯誤。
  • 再次包含之前發送的相同重定向 URI。 這是為了匹配 OP 的兩個請求。
  • grant_Typeauthorization_code
  • 客戶端以某種方式進行身份驗證。 在這種情況下,通過在請求中包含與先前一起發送的相同client_id以及client_secret表單元素中的秘密。
  • 使用的 HTTP 方法(如我上面所說)是 POST,而不是 GET。 這也是一個常見的錯誤。

在上面的示例中,我重定向回默認值HomeController 現在,執行 if 語句的 else 條件。 在這里,它可以找到令牌:

<div>
    @if(Session.Count == 0) {
        // ...
    } else {
        @if(Session["id_token"] != null) {
            <div>
                ID Token:<br>
                <pre>@Session["id_token"]</pre>
            </div>
        }

        @if(Session["access_token"] != null) {            
            <div>
                Access Token:<br>            
                <pre>@Session["access_token"]</pre>                
            </div>
        }

        @if(Session["refresh_token"] != null) {
            <div>
                Refresh Token:<br>                
                <pre>@Session["refresh_token"]</pre>
            </div>
        }
    }
</div>

這個例子比這更復雜,但它希望能給你一個想法。 仔細閱讀,檢查自述文件,並樂於了解有關 OpenID Connect 的更多信息!

您需要在不同的地方添加一些配置。 我會盡量展示你需要的所有拼圖。
在我的示例中,我將使用 IdentityServer4 的公共演示版用於 OIDC,因此您可以與工作版本進行比較。

應用程序接口
在任何控制器(或方法)中,添加[Authorize]屬性,因此這將需要有效的身份驗證。
如果您想更具體地了解用戶可以執行的操作,您還可以添加策略。 就像是:

[Authorize(Policy = "Read")]
[ApiController]
[Route("[controller]")]
public class HelloWorldsController : ControllerBase
{
    [HttpGet]
    public string Get()
    {
        return "Hello, World!";
    }
}

Startup.csConfigureServices方法中,您需要添加類似的配置,如下所示:

services
    .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
    {
        options.Authority = "https://demo.identityserver.io";
        options.Audience = "api";
    });

    // In case you want to work with policies
    services.AddAuthorization(options =>
    {
        options.AddPolicy("Read", policy => policy.RequireClaim("scope", "api"));
    });

要編譯上述配置,您應該添加 NuGet 包Microsoft.AspNetCore.Authentication.JwtBearer

Startup.csConfigure方法中,您需要添加app.UseAuthentication(); 就在app.UseAuthorization();之前app.UseAuthorization(); .

MVC
在任何控制器(或方法)中,添加[Authorize]屬性。 每當您的 MVC 應用程序的用戶點擊具有此屬性的方法時,登錄過程將自動觸發。
為了演示這一點,我將這個屬性添加到一個方法中:

[Authorize]
public async Task<IActionResult> Privacy()
{
    var httpClient = _httpClientFactory.CreateClient("ApiClient");
    var apiResult = await httpClient.SendAsync(
        new HttpRequestMessage(HttpMethod.Get, "/helloworlds"),
        HttpCompletionOption.ResponseHeadersRead);
    if (apiResult.IsSuccessStatusCode)
    {
        var content = await apiResult.Content.ReadAsStringAsync();
        ViewData.Add("apiResult", content); // Just to demonstrate
    }

    return View();
}

Startup.csConfigureServices方法中,您需要添加類似的配置,如下所示:

services.AddHttpContextAccessor();

services.AddTransient<BearerTokenHandler>();

services
    .AddHttpClient("ApiClient", client =>
    {
        client.BaseAddress = new Uri("https://localhost:5001");
    })
    .AddHttpMessageHandler<BearerTokenHandler>();

services.AddHttpClient("IDPClient", client =>
{
    client.BaseAddress = new Uri("https://demo.identityserver.io");
});

services
    .AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
    {
        options.Authority = "https://demo.identityserver.io";
        options.ClientId = "interactive.confidential";
        options.ClientSecret = "secret";
        options.ResponseType = "code";
        options.SaveTokens = true;
        options.Scope.Add("api");
    });

要編譯上述配置,您應該添加 NuGet 包Microsoft.AspNetCore.Authentication.CookiesMicrosoft.AspNetCore.Authentication.OpenIdConnect

Startup.csConfigure方法中,您需要添加app.UseAuthentication(); 就在app.UseAuthorization();之前app.UseAuthorization(); .

由於BearerTokenHandler相當大,您可以從GitHub 存儲庫復制它。 您將需要IdentityModel的 NuGet 包參考。
此存儲庫還包含您要求的設置的完整工作示例。


最后,您可能希望為用戶提供注銷的可能性。
您可以通過在視圖中添加鏈接來完成此操作:

@if (User.Identity.IsAuthenticated)
{
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Logout">Logout</a>
    </li>
}

匹配的控制器方法:

public IActionResult Logout()
{
    return SignOut(
        CookieAuthenticationDefaults.AuthenticationScheme,
        OpenIdConnectDefaults.AuthenticationScheme);
}

應該是這樣。 希望你能夠跟隨所有的拼圖。
如果有不清楚的地方,請告訴我。

設置 IdentityServer4:IdentityServer4 是用於 ASP.NET 的 OpenID Connect 和 OAuth 2.0 框架

您可以在此處找到有關如何使用 IdentificationServer4 的文檔: https : //identityserver4.readthedocs.io/en/latest/ https://identityserver4.readthedocs.io/en/latest/quickstarts/3_aspnetcore_and_apis.html

IdentityServer4 提供的一些功能是:

身份驗證即服務

所有應用程序(Web、本機、移動、服務)的集中登錄邏輯和工作流。 IdentityServer 是 OpenID Connect 的官方認證實現。

單點登錄/注銷

多種應用程序類型的單點登錄(和退出)。

API 的訪問控制 為各種類型的客戶端發布API 的訪問令牌,例如服務器到服務器、Web 應用程序、SPA 和本機/移動應用程序。

聯合網關

支持外部身份提供商,如 Azure Active Directory、Google、Facebook 等。這使您的應用程序不受如何連接到這些外部提供商的詳細信息的影響。

專注於定制

最重要的部分 - IdentityServer 的許多方面都可以自定義以滿足您的需求。 由於 IdentityServer 是一個框架而不是盒裝產品或 SaaS,因此您可以編寫代碼來調整系統,使其適合您的場景。

成熟的開源

IdentityServer 使用寬松的 Apache 2 許可證,允許在其上構建商業產品。 它也是提供治理和法律支持的 .NET 基金會的一部分。 免費和商業支持

沒有足夠的聲譽來為 IdentityServer4 答案添加評論,所以我只在這里提到它。

IS4 將不再免費用於商業用途: IdentityServer 的未來

當前版本 (IdentityServer4 v4.x) 將是我們作為免費開源工作的最后一個版本。 我們將繼續支持 IdentityServer4,直到 .NET Core 3.1 於 2022 年 11 月結束。

為了繼續我們的工作,我們成立了一家新公司 Duende Software,IdentityServer4 將更名為 Duende IdentityServer。 Duende IdentityServer 將包含所有新功能工作,並將面向 .NET Core 3.1 和 .NET 5(以及以后的所有版本)。

這個新產品將保持開源,但將提供雙重許可(RPL 和商業)。 如果您也在做免費的開源工作,RPL(互惠公共許可證)可以讓 Duende IdentityServer 免費。 如果您在商業場景中使用 Duende IdentityServer,則需要商業許可證。 我們提供多種許可 Duende IdentityServer 的方式,以適應不同的公司規模和使用模式。 包含 RPL 許可證對我們很重要,因為它使我們能夠認識並表達我們對開源社區和我們的貢獻者的感謝。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM