• 05 Resource Owner Password Credentials 授权


    原文:https://www.yuque.com/yuejiangliu/dotnet/qq7lgs


    05 Resource Owner Password Credentials 授权.mp4 (93.5 MB)

    一、回顾 Client Credentials

    image.png

    • 客户端应用不代表用户,客户端应用本身就相当于资源所有者
    • 通常用于机器对机器的通信
    • 客户端也需要身份认证

    Token 请求:

    POST http://xxx/connect/token HTTP/1.1
    Accept: application/json
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 116
    Host: localhost:5000
    
    grant_type=client_credentials
    &scope=api1
    &client_id=console+client
    &client_secret=xxx

    Token 响应:

    HTTP/1.1 200 OK
    Date: Thu, 02 May 2019 03:52:13 GMT
    Content-Type: application/json; charset=UTF-8
    Server: Kestrel
    Cache-Control: no-store, no-cache, max-age=0
    Pragma: no-cache
    Transfer-Encoding: chunked
    
    {"access_token":"xxxxxx","expires_in":3600,"token_type":"Bearer"}

    可以复制 access_token 后在 jwt.io 解码查看:

    image.png

    二、Resource Owner Password Credentials

    • 资源所有者的密码凭证(例如用户名和密码)直接被用来请求 Access Token
    • 通常用于遗留的应用
    • 资源所有者和客户端应用间必须高度信任
    • 其它授权方式不可用的时候才使用,尽量不用

    1、在 IdentityServer 中配置客户端

    配置 OpenID 相关资源,并添加 WPF Client:

    public static IEnumerable<IdentityResource> GetIdentityResources()
    {
        return new IdentityResource[]
        {
            // 要请求下面几个 OpenID 相关的资源,必须先添加它
            new IdentityResources.OpenId(),
    
            new IdentityResources.Profile(),
            new IdentityResources.Address(),
            new IdentityResources.Phone(),
            new IdentityResources.Email()
        };
    }
    ...
    
    public static IEnumerable<Client> GetClients()
    {
        return new[]
        {
            // client credentials flow client
            ...
            // WPF client, password grant
            new Client
            {
                ClientId = "wpf client",
                AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
                ClientSecrets = {new Secret("wpf secret".Sha256())},
                AllowedScopes = {
                    "api1",
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    IdentityServerConstants.StandardScopes.Email,
                    IdentityServerConstants.StandardScopes.Address,
                    IdentityServerConstants.StandardScopes.Phone}
            }
        };
    }

    测试用的 User 已定义在 TestUsers 里:

    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),
            ...
        }
    },

    2、配置 WPF 客户端

    整体界面:

    image.png

    记得安装 IdentityModel。

    MainWindow 代码

    public partial class MainWindow : Window
    {
        private string _accessToken;
        private DiscoveryResponse _disco;
    
        public MainWindow()
        {
            InitializeComponent();
        }
    
        private async void RequestAccessToken_ButtonClick(object sender, RoutedEventArgs e)
        {
            var userName = UserNameInput.Text;
            var password = PasswordInput.Password;
    
            var client = new HttpClient();
            var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000/");
            _disco = disco;
            if (disco.IsError)
            {
                Console.WriteLine(disco.Error);
                return;
            }
    
            // request access token
            var tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
            {
                Address = disco.TokenEndpoint,
                ClientId = "wpf client",
                ClientSecret = "wpf secret",
                Scope = "api1 openid profile address phone email",
    
                UserName = userName,
                Password = password
            });
    
            if (tokenResponse.IsError)
            {
                MessageBox.Show(tokenResponse.Error);
                return;
            }
    
            _accessToken = tokenResponse.AccessToken;
            AccessTokenTextBlock.Text = tokenResponse.Json.ToString();
        }
    
        private async void RequestApi1Resource_ButtonClick(object sender, RoutedEventArgs e)
        {
            // call API1 Resource
            var apiClient = new HttpClient();
            apiClient.SetBearerToken(_accessToken);
    
            var response = await apiClient.GetAsync("http://localhost:5001/identity");
            if (!response.IsSuccessStatusCode)
            {
                MessageBox.Show(response.StatusCode.ToString());
            }
            else
            {
                var content = await response.Content.ReadAsStringAsync();
                Api1ResponseTextBlock.Text = content;
            }
        }
    
        private async void RequestIdentityResource_ButtonClick(object sender, RoutedEventArgs e)
        {
            // call Identity Resource from Identity Server
            var apiClient = new HttpClient();
            apiClient.SetBearerToken(_accessToken);
    
            var response = await apiClient.GetAsync(_disco.UserInfoEndpoint);
            if (!response.IsSuccessStatusCode)
            {
                MessageBox.Show(response.StatusCode.ToString());
            }
            else
            {
                var content = await response.Content.ReadAsStringAsync();
                IdentityResponseTextBlock.Text = content;
            }
        }
    }

    3、OpenID 预设的 4 个 Scope

    5.4.  Requesting Claims using Scope Values

    • profile
    • OPTIONAL. This scope value requests access to the End-User's default profile Claims, which are: name, family_name, given_name, middle_name, nickname, preferred_username, profile, picture, website, gender, birthdate, zoneinfo, locale, and updated_at.
    • email
    • OPTIONAL. This scope value requests access to the email and email_verified Claims.
    • address
    • OPTIONAL. This scope value requests access to the address Claim.
    • phone
    • OPTIONAL. This scope value requests access to the phone_number and phone_number_verified Claims.

    4、Token 请求与响应

    Token 请求:

    POST /oauth/token HTTP/1.1
    Host:authorization-server.com
    
    grant_type=password
    &username=user@example.com
    &password=xxx
    &client_id=xxx
    &client_secret=xxx

    响应:

    {
        "access_token'; "MTQONjOkZmQ5OTM5NDE9ZTZjNGZmZjI3",
        "token_type":"bearer",
        "expires_in":3600,
    }

    5、示意图

    和 Client Credentials 相比:

    1. 多了用户角色
    2. 可以访问身份认证信息

    image.png

  • 相关阅读:
    Coursera机器学习week11 单元测试
    关于 TypeReference 的解释
    getModifiers 方法解释。
    instanceof isInstance isAssignableFrom 比较
    elasticsearch 基础 语法总结
    kibana 启动 关闭 和进程查找
    MD5 SHA1 SHA256 SHA512 SHA1WithRSA 的区别
    spring boot 项目 热启动
    java zip 压缩文件
    Packet for query is too large (1660 > 1024). You can change this value on the server by setting the max_allowed_packet' variable.
  • 原文地址:https://www.cnblogs.com/springsnow/p/13879207.html
Copyright © 2020-2023  润新知