Identity Server4学习系列四之用户名密码获得访问令牌
1、简介
Identity Server4支持用户名密码模式,允许调用客户端使用用户名密码来获得访问Api资源(遵循Auth 2.0协议)的Access Token,MS可能考虑兼容老的系统,实现了这个功能,但是不建议这么做.
2、实战一服务端配置
接着Identity Server4学习系列三的基础上,直接扩展里面的项目代码,让服务端同时支持密钥认证和用户名密码认证
第一步:扩展ThirdClients类,如下:
/// <summary> /// 配置可以访问IdentityServer4 保护的Api资源模型的第三方客户端 /// 配置客户端访问的密钥 /// </summary> public class ThirdClients { public static IEnumerable<Client> GetClients() { return new List<Client>() { new Client() { //客户端的唯一Id,客户端需要指定该ClientId才能访问 ClientId = $"client", //no interactive user, use the clientid/secret for authentication //使用客户端密钥进行认证 AllowedGrantTypes = GrantTypes.ClientCredentials, // 认证密钥,客户端必须使用secret密钥才能成功访问 ClientSecrets = { //用Sha256对"secret"进行加密 new Secret("secret".Sha256()) }, // scopes that client has access to //如果客户端的密钥认证成功,限定该密钥可以访问的Api范围 AllowedScopes = { "api1" } }, //添加支持用户名密码模式访问的客户端类型 new Client() { ClientId = "userPwd.client", AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, ClientSecrets = { new Secret("secret".Sha256()) }, AllowedScopes = { "api1" } } }; } /// <summary> /// 配置可以访问IdentityServer4 保护的Api资源模型的第三方客户端 /// 使用用户名密码模式 /// </summary> /// <returns></returns> public static List<TestUser> GetUsers() { return new List<TestUser>() { new TestUser() { SubjectId = "1", Username = "alice", Password = "password" } }; } }
第二步:注册TestUser到Identity Server4,修改StartUp文件如下:
public class Startup { // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { //优雅的链式编程 //注入Identity Server4服务到DI容器中 services.AddIdentityServer() //注入临时签名凭据到DI容器,后期可用签名证书的密钥替换,用于生成零时密钥 .AddDeveloperSigningCredential() //注入需要受Identity Server4保护的Api资源添注入到DI容器中 -内存级别 .AddInMemoryApiResources(Apis.GetApiResources()) //注入需要访问受Identity Server4保护的Api资源的客户端(密钥模式)注入到DI容器中 -内存级别 .AddInMemoryClients(ThirdClients.GetClients()) //注入需要访问受Identity Server4保护的Api资源的客户端(用户名密码访问模式)注入到DI容器中 -内存级别 .AddTestUsers(ThirdClients.GetUsers()); //注入基本的MVC服务 services.AddMvcCore() //注入MVC的认证服务,对应控制器的Authorize特性 .AddAuthorization() //注入MVC格式化程序,对应JsonResult等等的格式化操作,主要用于控制器返回值的格式化操作 .AddJsonFormatters(); //注入身份认证服务,设置Bearer为默认方案 services.AddAuthentication("Bearer") //注入并配置Bearer为默认方案的基本参数 .AddIdentityServerAuthentication(options => { //设置令牌的发布者 options.Authority = "http://localhost:5000"; //设置Https options.RequireHttpsMetadata = false; //需要认证的api资源名称 options.ApiName = "api1"; }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { //如果当前时开发者模式 if (env.IsDevelopment()) { //从管道中捕获同步和异步System.Exception实例并生成HTML错误响应。 app.UseDeveloperExceptionPage(); } //将IdentityServer 4服务注入到管道模型中(对应上面的IdentityServer 4服务的配置) app.UseIdentityServer(); //将认证服务通过Microsoft.AspNetCore.Authentication.AuthenticationMiddleware中间件 //注入到管道模型中(对应上面认证服务的配置) app.UseAuthentication(); //将mvc添加到Microsoft.AspNetCore.Builder.IApplicationBuilder请求执行中(对应上的MVC配置) app.UseMvc(); } }
ok,到这一步,Identity Server4服务端配置完成!
3、实战一客户端发起调用
调用代码如下:
class Program { static void Main(string[] args) { Request(); Console.ReadKey(); } async static void Request() { //请求Identity Server4服务 var disco = await DiscoveryClient.GetAsync("http://localhost:5000"); if (disco.IsError) { Console.WriteLine(disco.Error); return; } var tokenClient = new TokenClient(disco.TokenEndpoint, "userPwd.client", "secret"); var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("alice", "password", "api1"); if (tokenResponse.IsError) { Console.WriteLine(tokenResponse.Error); return; } //通过Identity Server4的认证过后,拿到AccessToken var client = new HttpClient(); client.SetBearerToken(tokenResponse.AccessToken); var response = await client.GetAsync("http://localhost:5000/identity"); if (!response.IsSuccessStatusCode) { Console.WriteLine(response.StatusCode); } else { //认证成功,输出Identity控制器的返回值 var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(JArray.Parse(content)); } Console.WriteLine(tokenResponse.Json); Console.WriteLine("\n\n"); } }
ok,使用用户名加密钥模式,访问Api成功拿到Api返回值,注意密钥任然需要给,因为这个密钥是用与给Token加密的,而用户名和密码无非是继续加一了一层认证,如果密钥认证成功,必须进行用户名和密码的认证,两者必须同时认证成功,才能成功的发起Api的调用.
用户名和密码必须和服务端给定的一致,否则客户端会报这个错:
无效的授权.
至此,用户名密码加密钥模式介绍完毕!