使用 Identity Server 和 .NET Core 构建简单的 OAuth2 授权服务器






4.83/5 (25投票s)
一篇指导性教程,介绍如何使用 Identity Server 和 .NET Core 构建一个简单的授权服务器,并启用客户端凭据工作流。
引言
本文是一篇简短易懂的教程,将解释如何使用 Identity Server 开源中间件构建一个 OAuth2 授权服务器,并将其托管在 .NET Core Web 服务器中。
Identity Server 项目的作者们已经做了出色的工作,提供了非常棒的文档和许多清晰有用的快速入门示例。然而,有时候很难理解示例的作者是如何做到这一点的。通常,从头开始重建同一个示例有助于更好地理解我们正在学习的技术。
本文来源于这些考量。我的想法是与您分享我学习这个主题的经验,希望能对其他开发者有所帮助。
背景
要理解本文内容,您可能需要了解更多关于
让我们创建授权服务器
在下一节中,我将(几乎)一步一步地解释代码。因为我知道我们都不喜欢阅读太多内容,所以我将每个部分都组织了清晰的段落标题,这样您就可以滚动浏览,找到可能更感兴趣的部分。开始吧!
在 Visual Studio 2017 中创建初始 WebAPI 项目
整个示例目前仅适用于 VS2017,使用 .NET Core 1.1 构建。方法如下:
- 打开 VS2017 并创建一个新项目,选择 VisualC# -> Web -> ASP.NET Core Web Application (.NET Core)。将其命名为
AuthorizationServer
。按“确定”。 - 现在选择 WebApi 项目类型。选择 ASP.NET Core 1.1。再次按“确定”。项目已创建。
向 WebAPI 服务器添加 NugetPackage IdentityServer4
- 浏览程序包管理器并安装
IdentityServer4
包。我安装了 1.5.0 版本,这是我撰写本文时最新的稳定版本。
向 WebAPI 服务器添加欢迎页面
为了让您的授权服务器能够通过浏览器访问,并让您轻松理解服务器是否已启动并正在运行,您可以添加一个基本控制器和一个欢迎页面。我将详细介绍我遵循的步骤,以便不熟悉 ASP.NET MVC 框架的人也能理解。
添加 MVC 控制器
- 在 Controllers 文件夹中,添加一个名为
HomeController
的新控制器。 - 添加新控制器时,VS 会询问要向项目添加哪些依赖项。目前您可以选择 **Minimal Dependencies**。
- 在我撰写本文时,添加依赖项后,我需要再次添加控制器。我认为这是 UI 的一个小 bug。
- 现在,添加控制器时,VS 会询问使用哪个脚手架。选择 **MVC Controller - Empty**。
- 最后,选择
HomeController
名称。现在已创建一个可以管理 Index.cshtml 页面的基本控制器。
添加 MVC 视图
- 再次,在 VS2017 中,右键单击项目并选择 **Add new folder**。
- 将其命名为 Views(**不要更改!这是 ASP.NET MVC 的默认选项!**)。
- 现在,在此 Views 文件夹中,添加另一个名为 Home 的文件夹。
- 现在右键单击 Home 文件夹,选择 Add -> New item -> MVC View Page (ASP.NET Core)。
- 默认情况下,视图的名称是 Index.cshtml,这正是我们想要的。按 **Add** 按钮添加新视图。
编写视图的 HTML 内容
将 Index.cshtml 中的代码更改为以下代码,以创建一个欢迎消息。
<html>
<head>
<title>Authorization server. Welcome page.</title>
</head>
<body>
<h1>Authorization server. Welcome page.</h1>
</body>
</html>
配置 Startup 类
在 Startup.cs 中设置正确的路由,以便能够浏览网站。
public void Configure(IApplicationBuilder app,
IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
现在浏览 Properties -> LaunchSettings.json 文件,并删除文件中所有可见部分的包含 launchUrl
属性的行。
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "api/values", //REMOVE THIS ONE
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"AuthorizationServer": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "api/values", //REMOVE THIS ONE
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://:50011"
}
没有此属性,Visual Studio 将会按照默认路径启动主页,并自动指向我们刚刚创建的网页。
启动项目以检查它是否正常工作...
...并享受第一个成就!**警告**:有些人报告了 VS2017 中与使用 IISExpress 启动 WebAPI 项目相关的一些问题。如果您是其中之一,可以更改“**Start project**”按钮旁边的配置文件,从 **IISExpress** 切换到 **AuthorizationServer**。
为客户端凭据工作流配置 **示例** 范围和客户端
现在我们可以配置授权服务器最重要的元素:clientIds
、clientSecrets
、scopes
。
这三个元素是客户端凭据工作流的一些基本要素。不要忘记参考 OAuth2 客户端注册文档 以获取更多信息!
这些属性将定义 WHO 可以向您的授权服务器请求访问令牌,以及 WHO 可以使用该令牌做什么。
为了激活我们最初的 **示例** 配置,只需在我们的项目中创建一个 Config.cs 类,内容如下:
using IdentityServer4.Models;
using System.Collections.Generic;
namespace AuthorizationServer
{
public class Config
{
// scopes define the API resources in your system
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("scope.readaccess", "Example API"),
new ApiResource("scope.fullaccess", "Example API"),
new ApiResource("YouCanActuallyDefineTheScopesAsYouLike", "Example API")
};
}
// client wants to access resources (aka scopes)
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "ClientIdThatCanOnlyRead",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets =
{
new Secret("secret1".Sha256())
},
AllowedScopes = { "scope.readaccess" }
},
new Client
{
ClientId = "ClientIdWithFullAccess",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets =
{
new Secret("secret2".Sha256())
},
AllowedScopes = { "scope.fullaccess" }
}
};
}
}
}
花点时间理解这个类...
花点时间理解这个类中的配置工作原理。这是我们示例的关键部分。
我们首先在这里定义了三个范围
return new List<ApiResource>
{
new ApiResource("scope.readaccess", "Example API"),
new ApiResource("scope.fullaccess", "Example API"),
new ApiResource("YouCanActuallyDefineTheScopesAsYouLike", "Example API")
};
正如您所见,我们可以使用任何我们想要的 string
作为范围。它只是一个标识符,仅此而已。
当被请求时,AuthorizationServer
将向客户端颁发一个 JWT Token,并且基于 clientId
,将正确的范围包含在 Token 中。由于范围被加密在 Token 中,因此接收 Token 的客户端无法更改范围并为自己启用比我们想要的更多的权限,这没有风险。
实际使用此范围的逻辑将在我们稍后创建的 Web API 服务器中(我计划很快在另一个示例/文章中这样做),该服务器将使用此授权服务器进行保护。Web API 服务器在执行实际操作之前,会检查从客户端传入的范围是否包含正确的授权。这就是我们利用授权服务器的地方。
哪个 ClientId
可以请求 Token,以及它获得什么范围?这就是配置类的第二部分所定义的。
// client want to access resources (aka scopes)
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "ClientIdThatCanOnlyRead",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets =
{
new Secret("secret1".Sha256())
},
AllowedScopes = { "scope.readaccess" }
},
new Client
{
ClientId = "ClientIdWithFullAccess",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets =
{
new Secret("secret2".Sha256())
},
AllowedScopes = { "scope.fullaccess" }
}
};
}
启用 IdentityServer 中间件功能
现在是时候启用 IdentityServer
功能,并将我们空网站的转换完成为一个真正的授权服务器,使其能够管理和认证我们在上面 Config 类中配置的客户端。
为此,我们只需在 Startup
类中调用几次 IdentityServer
对象。
将以下方法复制并粘贴到 Startup
类中,替换旧的方法。
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// configure identity server with in-memory stores, keys, clients and scopes
services.AddIdentityServer()
.AddTemporarySigningCredential()
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients());
//add framework services
services.AddMvc();
}
该方法“启用”了 IdentityServer
中间件,并为我们的范围和客户端添加了 InMemory
管理。
这是我们现在使用之前创建的 Config
类的关键点。
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients());
identityserver
上还有很多,**非常多** 的其他选项可以配置,但这超出了本文的范围。在这里,我们只是创建一个快速入门。再次,请查看文档,那里的人们确实开发了一个惊人的开源库。
现在,在 Startup.Configure
方法中,添加以下行:
app.UseIdentityServer();
最后,该方法应该如下所示:
// This method gets called by the runtime. Use this method to configure the
// HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseIdentityServer(); //HERE WE ENABLE IDENTITY SERVER
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
检查一切是否正常
我们几乎完成了。您的授权服务器已准备就绪!
因此,再次从 VS2017 启动项目。欢迎页面会显示出来,让您确信 Web 服务已启动并正在运行。
现在转到以下地址(**请将端口号替换为您服务器的端口号**):
- https://:50151/.well-known/openid-configuration
如果一切正常,JSON 文件将加载并在浏览器中显示(或者会提示您下载它,具体取决于您的浏览器)。这是来自 Identity Server 中间件生成的所有配置信息的 JSON 文件。
仔细查看此 JSON 文件。其中有一个重要部分,显示中间件已正确理解了您的配置。这是声明您的授权服务器支持的范围的部分,正是您在 Config.cs 中声明的那些范围。
"scopes_supported": [
"scope.readaccess",
"scope.fullaccess",
"YouCanActuallyDefineTheScopesAsYouLike",
"offline_access"
],
就这样!……嗯,暂时是这样……
您的第一个授权服务器已准备就绪,但如何使用呢?单独一个授权服务器本身作用不大,如果没有 API 来保护,或者没有客户端来授权。它需要一些朋友!
因此,在另一篇文章中,我们将学习如何保护 Web API 服务器,接受来自此授权服务器颁发的 Token。我们还将学习如何创建一个可以请求 Token 并使用它的客户端。
与此同时,我希望这个示例能帮助那些像我一样想开始玩转 OAuth2 工作流和强大的 IdentityServer
中间件的开发者。请分享您的反馈和评论!
历史
- 2017-04-22:初版
- 2017-04-24:添加了下载源代码的链接
- 2017-05-07:修复了一个提到错误按钮名称的步骤