zoukankan      html  css  js  c++  java
  • IdentityServer4 实现OAuth2.0四种模式之密码模式

    接上一篇:IdentityServer4 实现OAuth2.0四种模式之客户端模式,这一篇讲IdentityServer4 使用密码模式保护API访问。

    一,IdentityServer配置

    1,添加用户

    要用到用户名称密码当然得添加用户,在IdentityServer项目的Config类中的新增一个方法,GetUsers。返回一个TestUser的集合。

        public static List<TestUser> GetUsers() {
                return new List<TestUser>()
                {
                    new TestUser()
                    {
                        //用户名
                         Username="apiUser",
                         //密码
                         Password="apiUserPassword",
                         //用户Id
                         SubjectId="0"
                    }
                };
            }
    

    添加好用户还需要要将用户注册到IdentityServer4,修改IdentityServer项目的Startup类ConfigureServices方法

     public void ConfigureServices(IServiceCollection services)
            {
                services.AddMvc();
                //添加IdentityServer
                var builder = services.AddIdentityServer()
                    //身份信息授权资源
                    .AddInMemoryIdentityResources(Config.GetIdentityResources())
                    //API访问授权资源
                    .AddInMemoryApiResources(Config.GetApis())
                    //客户端
                    .AddInMemoryClients(Config.GetClients())
                    //添加用户
                    .AddTestUsers(Config.GetUsers());
                if (Environment.IsDevelopment())
                {
                    builder.AddDeveloperSigningCredential();
                }
                else
                {
                    throw new Exception("need to configure key material");
                }
            }

    2,添加客户端

    添加一个客户端用于用户名和密码模式的访问。客户端(Client)定义里有一个AllowedGrantTypes的属性,这个属性决定了Client可以被那种模式被访问,GrantTypes.ClientCredentials为客户端凭证模式,GrantTypes.ResourceOwnerPassword为用户名密码模式。上一节添加的Client是客户端凭证模式,所以还需要添加一个Client用于支持用户名密码模式。

    public static IEnumerable<Client> GetClients()
            {
                return new Client[] {
                  
                    new Client()
                    {
                        //客户端Id
                         ClientId="apiClientCd",
                         //客户端密码
                         ClientSecrets={new Secret("apiSecret".Sha256()) },
                         //客户端授权类型,ClientCredentials:客户端凭证方式
                         AllowedGrantTypes=GrantTypes.ClientCredentials,
                         //允许访问的资源
                         AllowedScopes={
                            "secretapi"
                        }
                    },
                    new Client()
                    {
                        //客户端Id
                         ClientId="apiClientPassword",
                         //客户端密码
                         ClientSecrets={new Secret("apiSecret".Sha256()) },
                         //客户端授权类型,ClientCredentials:客户端凭证方式
                         AllowedGrantTypes=GrantTypes.ResourceOwnerPassword,
                         //允许访问的资源
                         AllowedScopes={
                            "secretapi"
                        }
                    }
    
                };
            }
    

    二,保用密码模式访问受保护的Api

    1,使用IdentityMvc项目访问受保护的Api

    修改GetData控制器,使其支持密码模式访问

     public async Task<IActionResult> GetData(string type)
            {
                type = type ?? "client";
                var client = new HttpClient();
                var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");
                if (disco.IsError)
                    return new JsonResult(new { err=disco.Error});
                TokenResponse token = null;
                switch (type)
                {
                    case "client":
                        token = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest()
                        {
                            //获取Token的地址
                            Address = disco.TokenEndpoint,
                            //客户端Id
                            ClientId = "apiClientCd",
                            //客户端密码
                            ClientSecret = "apiSecret",
                            //要访问的api资源
                            Scope = "secretapi"
                        });
                        break;
                    case "password":
                        token = await client.RequestPasswordTokenAsync(new PasswordTokenRequest()
                        {
                            //获取Token的地址
                            Address = disco.TokenEndpoint,
                            //客户端Id
                            ClientId = "apiClientPassword",
                            //客户端密码
                            ClientSecret = "apiSecret",
                            //要访问的api资源
                            Scope = "secretapi",
                            UserName = "apiUser",
                            Password = "apiUserPassword"
                        });
                        break;
                }
                if (token.IsError)
                    return new JsonResult(new { err = token.Error });
                client.SetBearerToken(token.AccessToken);
                string data = await client.GetStringAsync("https://localhost:5001/api/identity");
                JArray json = JArray.Parse(data);
                return new JsonResult(json); 
            }  

     运行三个项目后访问:https://localhost:5002/home/getdata?type=password

    2,使用原生HTTP请求访问受保护的Api

     获取access_token

     获取到Token后,访问受保护的API和通过客户端模式一样。

     三,密码模式与客户端凭证模式的区别

    到目前为止,昨们还没有搞清这两个模式有什么区别,如果仅仅是为了能访问这个API,那加不加用户名和密码有什么区别呢。昨们对比下这两种模式取得Token后访问api返回的数据,可以发现用户名密码模式返回的Claim的数量要多一些。Claim是什么呢,简尔言之,是请求方附带在Token中的一些信息。但客户端模式不涉及到用户信息,所以返回的Claim数量会少一些。在IdentityServer4中,TestUser有一个Claims属性,允许自已添加Claim,有一个ClaimTypes枚举列出了可以直接添加的Claim。添加一个ClaimTypes.Role试试。

    IdentityServer.Config.GetUsers

        public static List<TestUser> GetUsers() {
                return new List<TestUser>()
                {
                    new TestUser()
                    {
                        //用户名
                         Username="apiUser",
                         //密码
                         Password="apiUserPassword",
                         //用户Id
                         SubjectId="0",
                         Claims=new List<Claim>(){
                             new Claim(ClaimTypes.Role,"admin")
                         }
                    }
                };
            }
    

    这时如果启动两个项目,采用用户密码和密码模式获取Token访问Api,返回的值依然是没有role:admin的Claim的。这时又要用到ApiResouce,ApiResouce的构造函数有一个重载支持传进一个Claim集合,用于允许该Api资源可以携带那些Claim。

    IdentityServer.Config.GetApis

    public static IEnumerable<ApiResource> GetApis()
            {
                return new ApiResource[] {
                    //secretapi:标识名称,Secret Api:显示名称,可以自定义
                    new ApiResource("secretapi","Secret Api",new List<string>(){ ClaimTypes.Role})
                };
            }
    

    现在可以启动项目测试一下,可以发现已经可以返回role这个claim了。

      Role(角色)这个Claim很有用,可以用来做简单的权限管理。

    首先修改下被保护Api的,使其支持Role验证

    IdentityApi.Controllers.IdentityController.GetUserClaims

     [HttpGet]
            [Route("api/identity")]
            [Microsoft.AspNetCore.Authorization.Authorize(Roles ="admin")]
            public object GetUserClaims()
            {
                return User.Claims.Select(r => new { r.Type, r.Value });
            }
    

    然后在IdentityServer端添加一个来宾角色用户

     IdentityServer.Config.GetUsers

    public static List<TestUser> GetUsers() {
                return new List<TestUser>()
                {
                    new TestUser()
                    {
                        //用户名
                         Username="apiUser",
                         //密码
                         Password="apiUserPassword",
                         //用户Id
                         SubjectId="0",
                         Claims=new List<Claim>(){
                             new Claim(ClaimTypes.Role,"admin")
    
                         }
                    },
                     new TestUser()
                    {
                        //用户名
                         Username="apiUserGuest",
                         //密码
                         Password="apiUserPassword",
                         //用户Id
                         SubjectId="1",
                         Claims=new List<Claim>(){
                             new Claim(ClaimTypes.Role,"guest")
                         }
                    }
                };
            }
    

    再回到IdentityMvc项目,修改下获取数据的测试接口GetData,把用户名和密码参数化,方便调试

    IdentityMvc.HomeContoller.GetData

     public async Task<IActionResult> GetData(string type,string userName,string password)
            {
                type = type ?? "client";
                var client = new HttpClient();
                var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");
                if (disco.IsError)
                    return new JsonResult(new { err=disco.Error});
                TokenResponse token = null;
                switch (type)
                {
                    case "client":
                        token = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest()
                        {
                            //获取Token的地址
                            Address = disco.TokenEndpoint,
                            //客户端Id
                            ClientId = "apiClientCd",
                            //客户端密码
                            ClientSecret = "apiSecret",
                            //要访问的api资源
                            Scope = "secretapi"
                        });
                        break;
                    case "password":
                        token = await client.RequestPasswordTokenAsync(new PasswordTokenRequest()
                        {
                            //获取Token的地址
                            Address = disco.TokenEndpoint,
                            //客户端Id
                            ClientId = "apiClientPassword",
                            //客户端密码
                            ClientSecret = "apiSecret",
                            //要访问的api资源
                            Scope = "secretapi",
                            UserName =userName,
                            Password = password
                        });
                        break;
                }
                if (token.IsError)
                    return new JsonResult(new { err = token.Error });
                client.SetBearerToken(token.AccessToken);
                string data = await client.GetStringAsync("https://localhost:5001/api/identity");
                JArray json = JArray.Parse(data);
                return new JsonResult(json); 
            }
    

    分别用apiUser和apiUserGuest访问,用apiUserGuest访问时请求被拒绝

    https://localhost:5002/home/getdata?type=password&userName=apiUserGuest&password=apiUserPassword

    上边是添加ClaimTypes枚举里定义好的Claim,但如果要定义的Claim不在Claim枚举里应该怎么办呢,比如我想所有用户都有一个项目编号,要添加一个名为prog的Claim。

    先在ApiResouce里允许携带名为prog.Claim

    IdentityServer.Config.GetApis

     public static IEnumerable<ApiResource> GetApis()
            {
                return new ApiResource[] {
                    //secretapi:标识名称,Secret Api:显示名称,可以自定义
                    new ApiResource("secretapi","Secret Api",new List<string>(){ ClaimTypes.Role,ClaimTypes.Name,"prog"})
                };
            }
    

    在用户定义的Claims属性里添加prog信息

    IdentityServer.Config.GetUsers

        public static List<TestUser> GetUsers() {
                return new List<TestUser>()
                {
                    new TestUser()
                    {
                        //用户名
                         Username="apiUser",
                         //密码
                         Password="apiUserPassword",
                         //用户Id
                         SubjectId="0",
                         Claims=new List<Claim>(){
                             new Claim(ClaimTypes.Role,"admin"),
                             new Claim("prog","正式项目"),
    
                         }
                    },
                     new TestUser()
                    {
                        //用户名
                         Username="apiUserGuest",
                         //密码
                         Password="apiUserPassword",
                         //用户Id
                         SubjectId="1",
                         Claims=new List<Claim>(){
                             new Claim(ClaimTypes.Role,"guest"),
                             new Claim("prog","测试项目"),
                         }
                    }
                };
            }
    

     使用apiUser访问

    https://localhost:5002/home/getdata?type=password&userName=apiUser&password=apiUserPassword

     密码模式需要知道用户的密码,那能不能用户自己从identityServer登录,不把密码给到第三方呢?,下一篇讲的隐藏模式就解决了这个问题。

  • 相关阅读:
    jQuery+ajax实现文件上传
    Jquery异步上传文件
    jQuery插件综合应用(三)发布文章页面
    jQuery插件综合应用(二)文字为主的页面
    网站开发常用jQuery插件总结(13)定位插件scrollto
    网站开发常用jQuery插件总结(12)固定元素插件scrolltofixed
    网站开发常用jQuery插件总结(11)折叠插件Akordeon
    网站开发常用jQuery插件总结(十)菜单插件superfish
    网站开发常用jQuery插件总结(九)侧边栏插件pageslide
    网站开发常用jQuery插件总结(八)标签编辑插件Tagit
  • 原文地址:https://www.cnblogs.com/liujiabing/p/11460486.html
Copyright © 2011-2022 走看看