IdentityServer4示例之使用ResourceOwnerPassword流程来保护API

Posted wall-ee

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IdentityServer4示例之使用ResourceOwnerPassword流程来保护API相关的知识,希望对你有一定的参考价值。

使用ResourceOwnerPassword流程来保护API

OAuth2.0中的ResourceOwnerPassword授权流程允许一个客户端发送username和password到token服务上面,以便获取一个代表用户的access token。

”规范“中建议只对受信任的客户端使用这种授权类型。通常情况来讲,在有用户交互的场景下,你应该优先使用OpenID Connect协议中的其中一个流程(有authorization code 、implicit、hybrid)来对用户进行认证,并获取access token。

话虽如此,这种授权类型引入了IdentityServer中的用户的概念,这也是我们要展现它的唯一原因。

添加用户

就像内存中的资源(或者叫做scopes)和客户端,也可以创建内存中的用户。

TestUser类代表了一个测试用户和它的一些声明(claim)。我们现在在Config类中创建一些用户:

using IdentityServer4.Test;

public static List<TestUser> GetUsers()
{
    return new List<TestUser>
    {
        new TestUser
        {
            SubjectId = "1",
            Username = "alice",
            Password = "password"
        },
        new TestUser
        {
            SubjectId = "2",
            Username = "bob",
            Password = "password"
        }
    };
}

然后在ConfigureService方法中注入:

public void ConfigureServices(IServiceCollection services)
{
    // configure identity server with in-memory stores, keys, clients and scopes
    services.AddIdentityServer()
        .AddDeveloperSigningCredential()
        .AddInMemoryApiResources(Config.GetApiResources())
        .AddInMemoryClients(Config.GetClients())
        .AddTestUsers(Config.GetUsers());
}

AddTestUsers扩展方法在后台做了这么几件事:

  • 添加了对resource owner password这种授权类型的支持
  • 添加了通常在login ui中使用的用户相关的服务。
  • 在testuser上添加了profile service。

为resource owner password这种授权类型创建一个相应的客户端

如果你想要客户端对于这两种授权类型都支持,你可以在现有的客户端上面通过修改AllowedGrantTypes属性的值来添加对这种授权类型的支持。

通常情况下你只是想要创建一个单独的客户端作为resource owner password这种授权类型的场景下使用,在Config类的GetClients方法中添加以下的代码:

public static IEnumerable<Client> GetClients()
{
    return new List<Client>
    {
        // other clients omitted...

        // resource owner password grant client
        new Client
        {
            ClientId = "ro.client",
            AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,

            ClientSecrets =
            {
                new Secret("secret".Sha256())
            },
            AllowedScopes = { "api1" }
        }
    };
}

使用password的授权类型来请求token

上面定义的那个客户端看起来和我们先前定义的client credentials客户端看起来很像。最主要的不同在于现在客户端会收集用户的密码,并在请求token的过程中将他连同其他东西一起发送到token service上面。

再次使用IdentityModel的TokenCLient来帮助我们实现这个请求:

// request token
var tokenClient = new TokenClient(disco.TokenEndpoint, "ro.client", "secret");
var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("alice", "password", "api1");

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

Console.WriteLine(tokenResponse.Json);
Console.WriteLine("

");

当你向API发送token的时候,你会发现一个非常小而又非常重要的改变(相对于clientcredential这种授权类型来说):access token现在包含了一个”sub“的claim,这个claim就是用的唯一标识(在Config类中定义的TestUser的subjectid属性就是这里的东西),这个不同会通过api的方法返回的json里面发现。我在这里展示1下,我通过postman:

首先是通过client credential这种授权获取的token来访问api的,获取的结果如下:

[
    {
        "claimType": "nbf",
        "claimValue": "1532508154"
    },
    {
        "claimType": "exp",
        "claimValue": "1532511754"
    },
    {
        "claimType": "iss",
        "claimValue": "http://localhost:5000"
    },
    {
        "claimType": "aud",
        "claimValue": "http://localhost:5000/resources"
    },
    {
        "claimType": "aud",
        "claimValue": "api1"
    },
    {
        "claimType": "client_id",
        "claimValue": "firstClient"
    },
    {
        "claimType": "scope",
        "claimValue": "api1"
    }
]

看一看出没有sub这个声明。

然后使用resource owner password这个授权类型来搞到token,再用这个token访问一下api:

[
    {
        "claimType": "nbf",
        "claimValue": "1532511508"
    },
    {
        "claimType": "exp",
        "claimValue": "1532515108"
    },
    {
        "claimType": "iss",
        "claimValue": "http://localhost:5000"
    },
    {
        "claimType": "aud",
        "claimValue": "http://localhost:5000/resources"
    },
    {
        "claimType": "aud",
        "claimValue": "api1"
    },
    {
        "claimType": "client_id",
        "claimValue": "secondClient"
    },
    {
        "claimType": "sub",
        "claimValue": "2"
    },
    {
        "claimType": "auth_time",
        "claimValue": "1532511508"
    },
    {
        "claimType": "idp",
        "claimValue": "local"
    },
    {
        "claimType": "scope",
        "claimValue": "api1"
    },
    {
        "claimType": "amr",
        "claimValue": "pwd"
    }
]

 

以上是关于IdentityServer4示例之使用ResourceOwnerPassword流程来保护API的主要内容,如果未能解决你的问题,请参考以下文章

IdentityServer4主题之登出

IdentityServer4主题之定义资源

我可以使用 IdentityServer4 spa JS UI 而不是 asp .net core mvc 吗?

Asp.Net Core 中IdentityServer4 实战之角色授权详解

identityserver4 踩坑之不同API的ClaimsPrincipal获取用户信息的Type不一致

IdentityServer4专题之二:OpenID介绍