• IdentityServer4 实现 OAuth 2.0(密码模式


    之前写了一篇文章:《IdentityServer4 实现 OpenID Connect 和 OAuth 2.0

    上面这篇文章虽然详细,但都是点到为止的介绍,并没有实际应用的示例,所以,后面在真正去实现的时候,踩到了自己之前种下的很多坑。

    业务场景:前后端分离项目,前端调用后端业务服务需要授权访问(提供access_token),access_token在用户登录的时候(用户名和密码登录),由授权中心生成access_token并返回给前端,这样前端就可以拿到access_token,去调用后端业务服务了。

    一开始,我使用的GrantTypes.Implicit模式,登录页面在授权中心,登录成功之后会跳到callback.htm#access_token=*页面,前端调用使用oidc-client组件,然后获取access_token,当时使用还没什么,现在觉得真是一团乱麻,前后端分离的项目,在授权中心居然把登录页面放在服务中了,但我后面还是没有意识到GrantTypes.Implicit的问题,而是尝试在这种模式下,写HTTP Post请求授权中心(提供用户名和密码),然后没然后,一团糟。。。

    使用 IdentityServer4 实现上面的业务场景,其实很简单,只要使用GrantTypes.GrantTypes.ResourceOwnerPassword模式,就可以了。

    Startup.ConfigureServices配置代码:

    var builder = services.AddIdentityServer();
    builder.AddTemporarySigningCredential()
            //.AddInMemoryIdentityResources(Config.GetIdentityResources())
            .AddInMemoryApiResources(Config.GetApiResources())
            .AddInMemoryClients(new List<Client>
            {
                new Client
                {
                    ClientId = "client_id_1",
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
                    AllowOfflineAccess = true,
                    AccessTokenLifetime = 3600 * 6, //6小时
                    SlidingRefreshTokenLifetime = 1296000, //15天
                    ClientSecrets =
                    {
                        new Secret("secret".Sha256())
                    },
                    AllowedScopes =
                    {
                        IdentityServerConstants.StandardScopes.OfflineAccess, 
                        "api1"
                    }
                }});
    builder.AddResourceOwnerValidator<ResourceOwnerPasswordValidator>();
    

    ResourceOwnerPasswordValidator示例代码:

    public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
    {
        private readonly IUserService _userService;
    
        public ResourceOwnerPasswordValidator(IUserService userService)
        {
            _userService = userService;
        }
    
        public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
        {
            var userId = await _userService.Login(context.UserName, context.Password);
            if (userId != 0)
            {
                context.Result = new GrantValidationResult(userId.ToString(), OidcConstants.AuthenticationMethods.Password);
            }
        }
    }
    

    使用ResourceOwnerPasswordValidator的作用,就是自定义用户登录的用户名密码判断,而不是使用 IdentityServer4 的TestUser

    请求示例:IdentityServer4 Token Endpoint

    获取access_token请求示例:

    刷新access_token请求示例:

    也可以服务端进行请求,示例代码:

    private async Task<TokenResponse> GetToken(string clientId, string clientSecret, string grantType, string userName, string password, string scope)
    {
        var client = new DiscoveryClient($"http://localhost:5001");
        client.Policy.RequireHttps = false;
        var disco = await client.GetAsync();
        var tokenClient = new TokenClient(disco.TokenEndpoint, clientId, clientSecret);
        return await tokenClient.RequestResourceOwnerPasswordAsync(userName, password, scope);
    }
    
    private async Task<TokenResponse> GetRefreshToken(string clientId, string clientSecret, string grantType, string refreshToken)
    {
        var client = new DiscoveryClient($"http://localhost:5001");
        client.Policy.RequireHttps = false;
        var disco = await client.GetAsync();
        var tokenClient = new TokenClient(disco.TokenEndpoint, clientId, clientSecret);
        return await tokenClient.RequestRefreshTokenAsync(refreshToken);
    }
    

    参考资料:

  • 相关阅读:
    final有什么用?
    数组的定义
    作业
    List 、Set数据结构
    报表工具实现单据套打
    动态格报表的制作
    图形钻取
    报表工具轻松搞定卡片式报表
    列表钻取
    报表中如何实现不规则布局
  • 原文地址:https://www.cnblogs.com/xishuai/p/identityserver4-ResourceOwnerPassword-GrantType-and-http-post.html
Copyright © 2020-2023  润新知