• IdentityServer4【QuickStart】之使用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"
        }
    ]
  • 相关阅读:
    用户管理
    开机、重启、用户登录注销
    网络请求的封装
    Vuex
    Promise
    Vue Router(二)
    Vue Router(一)
    Vue CLI
    前端模块化-导入导出
    插槽
  • 原文地址:https://www.cnblogs.com/pangjianxin/p/9367434.html
Copyright © 2020-2023  润新知