ASP.NET Core的身份认证框架IdentityServer4(7)- 使用客户端认证控制API访问

前言

今天(2017-9-8,写于9.8,今天才发布)一口气连续把最后几篇IdentityServer4相关理论全部翻译完了,终于可以进入写代码的过程了,比较累。目前官方的文档和Demo以及一些相关组件全部是.net core 1.1的,应该是因为目前IdentityServer4目前最新版本只是2.0.0 rc1的原因,官方文档和Demo还没来更新。我准备使用的是.net core 2.0 所支持的IdentityServer4 2.0.0,官方文档及Demo只能参考,因为在asp.net core 2.0及IdentityServer4 2.0版本中一些接口做了调整,有些写法和原来不一样。本文是根据官方QuickStart1(官方QuickStart演示项目的目前只支持.net core 1.1,QuickStart1是第一个QuickStart)写出的demo。

第一次接触IdentityServer4是在ABP的asp.net zero项目中,感觉IdentityServer4挺方便的,便有了系统性的学一下IdentityServer4的想法,这是我写IdentityServer4系列文章的原因。

本文包括后续的Demo都会放在github:https://github.com/stulzq/IdentityServer4.Samples 希望大家能给我来一个star 鼓励一下。

使用客户端认证保护API

此示例介绍了使用IdentityServer保护API的最基本场景。

在这种情况下,我们将定义一个API和要访问它的客户端。 客户端将在IdentityServer上请求访问令牌,并使用它来访问API。

准备

创建一个名为QuickstartIdentityServer的ASP.NET Core Web 空项目(asp.net core 2.0),端口5000 创建一个名为Api的ASP.NET Core Web Api 项目(asp.net core 2.0),端口5001 创建一个名为Client的控制台项目(.net core 2.0)

定义API

QuickstartIdentityServer项目中添加一个Config.cs文件:

// scopes define the API resources in your system
public static IEnumerable<ApiResource> GetApiResources()
{
    return new List<ApiResource>
    {
        new ApiResource("api1", "My API")
    };
}

定义客户端

对于这种情况,客户端将不具有交互式用户,并将使用IdentityServer使用所谓的客户机密码进行身份验证。 将以下代码添加到Config.cs文件中:

// client want to access resources (aka scopes)
public static IEnumerable<Client> GetClients()
{
    return new List<Client>
    {
        new Client
        {
            ClientId = "client",
            // 没有交互性用户,使用 clientid/secret 实现认证。
            AllowedGrantTypes = GrantTypes.ClientCredentials,
            // 用于认证的密码
            ClientSecrets = 
            {
                new Secret("secret".Sha256())
            },
            // 客户端有权访问的范围(Scopes)
            AllowedScopes = { "api1" }
        }
    };
}

配置 IdentityServer

要配置IdentityServer以使用范围和客户端定义,您需要向ConfigureServices方法添加代码。

Startup.cs

// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
    // 使用内存存储,密钥,客户端和资源来配置身份服务器。
    services.AddIdentityServer()
        .AddDeveloperSigningCredential()
        .AddInMemoryApiResources(Config.GetApiResources())
        .AddInMemoryClients(Config.GetClients());
}

// 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(LogLevel.Debug);
    app.UseDeveloperExceptionPage();

    app.UseIdentityServer();

}

运行此项目,打开浏览器访问http://localhost:5000/.well-known/openid-configuration你将会看到所谓的发现文档。

添加API

在项目Api中添加一个Controller

[Route("[controller]")]
[Authorize]
public class IdentityController : ControllerBase
{
    [HttpGet]
    public IActionResult Get()
    {
        return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
    }
}

稍后将使用此控制器来测试授权要求,以及通过API来查看声明身份。

配置

添加身份验证中间件

  • 验证传入令牌以确保它来自可信发行者。
  • 令牌验证是有效的,用于在这个API

在项目中添加nuget包Microsoft.AspNetCore.Authentication.JwtBearer

这里使用了Microsoft.AspNetCore.Authentication.JwtBearer包来替换AccessTokenValidation,因为后者还没有更新到.net core 2.0,使用的话,是有问题的

您还需要将中间件添加到管道中。它必须在MVC之前添加:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvcCore().AddJsonFormatters();
    services.AddAuthentication((options) =>
        {
            options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters();
            options.RequireHttpsMetadata = false;
            options.Audience = "api1";//api范围
            options.Authority = "http://localhost:5000";//IdentityServer地址
        });
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseAuthentication();
    app.UseMvc();
}

创建客户端

最后一个步骤是编写一个客户端来请求访问令牌,然后使用这个令牌来访问 API。为此你需要为你的解决方案添加一个控制台应用程序。

IdentityServer 上的令牌端点实现了 OAuth 2.0 协议,你应该使用合法的 HTTP请求来访问它。然而,我们有一个叫做 IdentityModel 的客户端库,它将协议交互封装到了一个易于使用的 API 里面。

添加 IdentityModel NuGet 程序包到你的客户端项目中。

IdentityModel 包含了一个用于 发现端点 的客户端库。这样一来你只需要知道 IdentityServer 的基础地址,实际的端点地址可以从元数据中读取:

// 从元数据中发现端口
var disco = await DiscoveryClient.GetAsync("http://localhost:5000");

接着你可以使用 TokenClient 来请求令牌。为了创建一个该类型的实例,你需要传入令牌端点地址、客户端id和密码。

然后你可以使用 RequestClientCredentialsAsync 方法来为你的目标 API 请求一个令牌:

// 请求令牌
var tokenClient = new TokenClient(disco.TokenEndpoint, "client", "secret");
var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1");

if (tokenResponse.IsError)
{
    Console.WriteLine(tokenResponse.Error);
    return;
}

Console.WriteLine(tokenResponse.Json);

注意:从控制台中复制和粘贴访问令牌到 jwt.io 以检查令牌的合法性。

最后是调用 API。

为了发送访问令牌到 API,你一般要使用 HTTP 授权 header。这可以通过 SetBearerToken 扩展方法来实现:

// 调用api
var client = new HttpClient();
client.SetBearerToken(tokenResponse.AccessToken);

var response = await client.GetAsync("http://localhost:5001/identity");
if (!response.IsSuccessStatusCode)
{
    Console.WriteLine(response.StatusCode);
}
else
{
    var content = await response.Content.ReadAsStringAsync();
    Console.WriteLine(JArray.Parse(content));
}

依次启动QuickstartIdentityServer>API>Client 最后查看输出结果:

注意:默认情况下访问令牌将包含 scope 身份信息,生命周期(nbf 和 exp),客户端 ID(client_id) 和 发行者名称(iss)。

进一步实践 当前演练目前主要关注的是成功的步骤:

  • 客户端可以请求令牌
  • 客户端可以使用令牌来访问 API

你现在可以尝试引发一些错误来学习系统的相关行为,比如:

  • 尝试在 IdentityServer 未运行时(unavailable)连接它
  • 尝试使用一个非法的客户端id或密码来请求令牌
  • 尝试在请求令牌的过程中请求一个非法的 scope
  • 尝试在 API 未运行时(unavailable)调用它
  • 不向 API 发送令牌
  • 配置 API 为需要不同于令牌中的 scope

本文demo github地址 https://github.com/stulzq/IdentityServer4.Samples/tree/master/Quickstarts/1_ClientCredentials

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏JarvanMo的IT专栏

Flutter: Failed to notify project evalution listener

又是一个常见的flutter的环境问题,一般发生在引用了plugin之后。因为默认的gradle,kotlin之类的比较旧,而所引用的plugin里面的grad...

2752
来自专栏Linux 杂货铺

使用Apache Guacamole连接虚拟云桌面

Apache Guacamole是一款HTML5应用程序,可通过RDP,VNC和其他协议访问远程桌面。您可以创建一个虚拟云桌面,用户通过Web浏览器即可访问。本...

3.9K4
来自专栏依乐祝

Asp.NetCoreWebApi图片上传接口(二)集成IdentityServer4授权访问(附源码)

上一篇文章中,给大家讲解了如何通过 Asp.Net Core Web Api实现图片上传的接口,具体的可以[点这里查看][https://www.cnblogs...

1391
来自专栏开心的学习之路

Fedora27搭建GPU版TensorFlow

主机:华硕(ASUS)G11飞行堡垒 显卡:GeForce 1070 原操作系统为win10,准备安装Fedora 27,从零开始搭建GPU版TensorF...

3637
来自专栏互联网杂技

SpringBoot ( 十二 ) :SpringBoot 如何测试打包部署

有很多网友会时不时的问我,spring boot项目如何测试,如何部署,在生产中有什么好的部署方案吗?这篇文章就来介绍一下spring boot 如何开发、调试...

992
来自专栏程序员的SOD蜜

使用“消息服务框架”(MSF)实现分布式事务的三阶段提交协议(电商创建订单的示例)

1,示例解决方案介绍 在上一篇 《消息服务框架(MSF)应用实例之分布式事务三阶段提交协议的实现》中,我们分析了分布式事务的三阶段提交协议的原理,现在我们来看看...

3379
来自专栏哎_小羊

国内使用 kubeadm 在 Centos 7 搭建 Kubernetes 集群

目录 Kubeadm 介绍 环境、软件准备 软件安装配置 镜像准备 安装 Docker 安装并配置 Kubernetes 组件 初始化 Master 安装 F...

1.8K9
来自专栏orientlu

连接器 -- Scatter File & Linker Script File

源文件编译后生成 elf 格式的目标文件(各种.o), 与运行时库经过连接器处理后,生成可以被写入嵌入式设备 ROM 中的 elf 格式文件。

1792
来自专栏惨绿少年

awk巩固练习题

第1章 awk数组练习题 1.1 文件内容(仅第一行) [root@znix test]# head -1 secure-20161219 access.log...

2420
来自专栏数据之美

使用 django-blog-zinnia 搭建个人博客

django-blog-zinnia 虽然小巧,但是具备了个人博客应用的全部基础功能,且具有很高的拓展性,并且开箱即用。以下是官方列出的一些特性: 评论 站...

3329

扫码关注云+社区

领取腾讯云代金券