当前位置:   article > 正文

ASP.NET Core3.1使用IdentityServer4实现授权登录(四)_identityserver4 密码模式invalid_grant错误

identityserver4 密码模式invalid_grant错误

创建使用[ResourceOwnerPassword-资源所有者密码凭证]授权模式的客户端
本篇将创建使用[ResourceOwnerPassword-资源所有者密码凭证]授权模式的客户端,来对受保护的API资源进行访问。

接上一篇项目,在IdentityServer项目Config.cs中添加一个客户端
在这里插入图片描述

/// 资源所有者密码凭证(ResourceOwnerPassword)
///     Resource Owner其实就是User,所以可以直译为用户名密码模式。
///     密码模式相较于客户端凭证模式,多了一个参与者,就是User。
///     通过User的用户名和密码向Identity Server申请访问令牌。
new Client
{ 
    ClientId = "client1",
    AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
    ClientSecrets = { new Secret("secret".Sha256()) },
    AllowedScopes = { "api1" }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

再添加一个用户的集合(测试数据来自IdentityServer官方)。
在这里插入图片描述
完整的Config.cs代码

using System.Collections.Generic;
using System.Security.Claims;

using IdentityModel;

using IdentityServer4.Models;
using IdentityServer4.Test;

namespace IdentityServer
{
    /// <summary>
    /// IdentityServer资源和客户端配置文件
    /// </summary>
    public static class Config
    {
        /// <summary>
        /// API资源集合
        ///     如果您将在生产环境中使用此功能,那么给您的API取一个逻辑名称就很重要。
        ///     开发人员将使用它通过身份服务器连接到您的api。
        ///     它应该以简单的方式向开发人员和用户描述您的api。
        /// </summary>
        public static IEnumerable<ApiResource> Apis => new List<ApiResource> { new ApiResource("api1", "My API") };

        /// <summary>
        /// 客户端集合
        /// </summary>
        public static IEnumerable<Client> Clients =>
            new List<Client>
            {
                /// 客户端模式(Client Credentials)
                ///     可以将ClientId和ClientSecret视为应用程序本身的登录名和密码。
                ///     它将您的应用程序标识到身份服务器,以便它知道哪个应用程序正在尝试与其连接。
                new Client
                { 
                    //客户端标识
                    ClientId = "client",
                    //没有交互用户,使用clientid/secret进行身份验证,适用于和用户无关,机器与机器之间直接交互访问资源的场景。
                    AllowedGrantTypes = GrantTypes.ClientCredentials,
                    //认证密钥
                    ClientSecrets = { new Secret("secret".Sha256()) },
                    //客户端有权访问的作用域
                    AllowedScopes = { "api1" }
                },
                /// 资源所有者密码凭证(ResourceOwnerPassword)
                ///     Resource Owner其实就是User,所以可以直译为用户名密码模式。
                ///     密码模式相较于客户端凭证模式,多了一个参与者,就是User。
                ///     通过User的用户名和密码向Identity Server申请访问令牌。
                new Client
                {
                    ClientId = "client1",
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
                    ClientSecrets = { new Secret("secret".Sha256()) },
                    AllowedScopes = { "api1" }
                }
            };

        /// <summary>
        /// 用户集合
        /// </summary>
        public static List<TestUser> Users =>
            new List<TestUser>
            {
                new TestUser{SubjectId = "818727", Username = "alice", Password = "alice",
                    Claims =
                    {
                        new Claim(JwtClaimTypes.Name, "Alice Smith"),
                        new Claim(JwtClaimTypes.GivenName, "Alice"),
                        new Claim(JwtClaimTypes.FamilyName, "Smith"),
                        new Claim(JwtClaimTypes.Email, "AliceSmith@email.com"),
                        new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
                        new Claim(JwtClaimTypes.WebSite, "http://alice.com"),
                        new Claim(JwtClaimTypes.Address, @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }", IdentityServer4.IdentityServerConstants.ClaimValueTypes.Json)
                    }
                },
                new TestUser{SubjectId = "88421113", Username = "bob", Password = "bob",
                    Claims =
                    {
                        new Claim(JwtClaimTypes.Name, "Bob Smith"),
                        new Claim(JwtClaimTypes.GivenName, "Bob"),
                        new Claim(JwtClaimTypes.FamilyName, "Smith"),
                        new Claim(JwtClaimTypes.Email, "BobSmith@email.com"),
                        new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
                        new Claim(JwtClaimTypes.WebSite, "http://bob.com"),
                        new Claim(JwtClaimTypes.Address, @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }", IdentityServer4.IdentityServerConstants.ClaimValueTypes.Json),
                        new Claim("location", "somewhere")
                    }
                }
            };
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90

我们使用Postman来获取ResourceOwnerPassword这种模式的AcceccToken
与上一种 Client Credentials 模式不同的是 client_id 使用 client1,grant_type 由原来的 client_credentials 改为 password,多了 username 和 password 两个参数,使用用户名密码 alice / alice 来登录
在这里插入图片描述
这是正常的效果,但是博主遇到一个报错如下
在这里插入图片描述
后来查资料要在startup里添加users,代码如下
在这里插入图片描述
效果:
在这里插入图片描述
2、创建一个名为 ResourceOwnerPasswordConsoleApp 的控制台客户端应用。
在这里插入图片描述
创建完成后的项目截图
在这里插入图片描述
3、添加nuget包:IdentityModel
在这里插入图片描述
在Program.cs编写代码

using System;
using System.Net.Http;
using System.Threading.Tasks;

using IdentityModel.Client;

using Newtonsoft.Json.Linq;

namespace ResourceOwnerPasswordConsoleApp
{
    class Program
    {
        static async Task Main(string[] args)
        {
            bool verifySuccess = false;
            TokenResponse tokenResponse = null;
            while (!verifySuccess)
            {
                Console.WriteLine("请输入用户名:");
                string userName = Console.ReadLine();
                Console.WriteLine("请输入密码:");
                string password = Console.ReadLine();

                //discovery endpoint - 发现终结点
                HttpClient client = new HttpClient();
                DiscoveryDocumentResponse disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");
                if (disco.IsError)
                {
                    Console.WriteLine($"[DiscoveryDocumentResponse Error]: {disco.Error}");
                    return;
                }

                //request assess token - 请求访问令牌
                tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
                {
                    Address = disco.TokenEndpoint,
                    ClientId = "client1",
                    ClientSecret = "secret",
                    Scope = "api1",
                    UserName = userName,
                    Password = password
                });
                if (tokenResponse.IsError)
                {
                    //ClientId 与 ClientSecret 错误,报错:invalid_client
                    //Scope 错误,报错:invalid_scope
                    //UserName 与 Password 错误,报错:invalid_grant
                    string errorDesc = tokenResponse.ErrorDescription;
                    if (string.IsNullOrEmpty(errorDesc)) errorDesc = "";
                    if (errorDesc.Equals("invalid_username_or_password"))
                    {
                        Console.WriteLine("用户名或密码错误,请重新输入!");
                    }
                    else
                    {
                        Console.WriteLine($"[TokenResponse Error]: {tokenResponse.Error}, [TokenResponse Error Description]: {errorDesc}");
                    }
                    Console.WriteLine("");
                    continue;
                }
                else
                {
                    Console.WriteLine("");
                    Console.WriteLine($"Access Token: {tokenResponse.AccessToken}");
                    verifySuccess = true;
                }
            }

            //call API Resource - 访问API资源
            HttpClient apiClient = new HttpClient();
            apiClient.SetBearerToken(tokenResponse?.AccessToken);
            HttpResponseMessage response = await apiClient.GetAsync("http://localhost:6000/weatherforecast");
            if (!response.IsSuccessStatusCode)
            {
                Console.WriteLine($"API Request Error, StatusCode is : {response.StatusCode}");
            }
            else
            {
                string content = await response.Content.ReadAsStringAsync();
                Console.WriteLine("");
                Console.WriteLine($"Result: {JArray.Parse(content)}");
            }

            Console.ReadKey();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87

用户名密码错误的话,会一直提示重新输入
在这里插入图片描述
我们使用用户名密码 alice / alice 进行登录
在这里插入图片描述
可以看到,成功获取到AccessToken,并使用AccessToken访问到受保护的API获取到结果。

以上内容来自于网络,如有侵权联系即删除

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/277214
推荐阅读
相关标签
  

闽ICP备14008679号