今天跟大家分享的是第一种模式,客户端模式
上一章我们讲过,客户端模式是跟用户无关的,只是通过获取token,得到授权,进而请求API资源而已。
下面我们就详细讲述,如何实现客户端模式。
在这里,我们需要新建3个项目,身份认证的服务端、Api资源站、客户端。
以下项目均在Net Core 2.2上开发。
一、身份认证服务端
我们新建一个ASP.NET Core MVC项目,命名 ServerDemo
从nuget上,引入IdentityServer4的安装包
在根目录下,新建一个配置类 Config.cs
public class Config { //定义身份资源 public static IEnumerable<IdentityResource> GetIdentityResources() { return new List<IdentityResource>() { new IdentityResources.OpenId(), new IdentityResources.Profile(), }; } //定义Api资源 public static IEnumerable<ApiResource>GetApiResources() { return new List<ApiResource> { new ApiResource("api1","测试Api") //定义Api站点的name和显示名称,这个name必须唯一,作为站点资源识别,在Api站点上也需要指定这个name,必须相等,表示请求这个Api站点 }; } //定义客户端授权类型 public static IEnumerable<Client> GetClients() { return new List<Client> { //第一个,客户端模式Client Credentials new Client{ ClientId="CredentialsClient", //客户端Id,必须且唯一 AllowedGrantTypes=GrantTypes.ClientCredentials, //指定为客户端授权模式 ClientSecrets={ new Secret("111111".Sha256()) //自定义密码,客户端请求时,密码传参数,必须与这个密码一致 }, AllowedScopes={ "api1"}//设置允许授权请求的范围,表示这个客户端Id可请求的资源范围,若不在范围内,请求会被拒绝。 }, }; } }
在这个配置类里面,我们定义了3个方法
第一个是身份资源信息,因客户端模式下,与用户无关,所以这个在客户端模式下,没有也没关系,这里只是与其他模式同个项目,写一块了。
第二个是Api资源,主要陈列API站点的name和显示名称,主要是name,必须唯一。
第三个是客户端,陈列所有客户端标识,授权方式、密码和允许请求的资源范围。
接下来,我们需要在Startup.cs文件中进行注入。
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.Configure<CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); //配置IdentityServer4服务 services.AddIdentityServer() .AddDeveloperSigningCredential() //客户端授权时,必须增加这行,不然客户端会出现keyset missing的错误 .AddInMemoryApiResources(Config.GetApiResources()) .AddInMemoryIdentityResources(Config.GetIdentityResources()) .AddInMemoryClients(Config.GetClients()) ; services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); } // 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()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseCookiePolicy(); //使用IdS中间件 app.UseIdentityServer(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } }
主要是下面2个
//配置IdentityServer4服务 services.AddIdentityServer() .AddDeveloperSigningCredential() //客户端授权时,必须增加这行,不然客户端会出现keyset missing的错误 .AddInMemoryApiResources(Config.GetApiResources()) //加载定义的Api资源 .AddInMemoryIdentityResources(Config.GetIdentityResources())//加载定义的身份资源 .AddInMemoryClients(Config.GetClients())//加载定义的客户端 ;
//使用IdS中间件 app.UseIdentityServer();
自此,我们就完成了服务端的开发。
但有一点需要注意的是AddInMemoryApiResources是存储内存里面的,若是要自定义存储的介质,比如redis、数据库等中间件上,则可以对Memory进行重载定义,再注入即可。
在id4的官网文档上有写,一时找不到哪个页面上,大家自己去找哈,https://identityserver4.readthedocs.io/en/latest/topics/signin.html#cookie-authentication
同时,我们在项目中右键-属性-调试,设置身份认证服务端启动端口号为7000,这个自定义。
二、新建API站点资源
新建一个asp.net core WebApi项目,命名 ApiSourcce
从Nuget上引入 IdentityServer4.AccessTokenValidation 包
新建一个 命名为 IdentityController 的控制器,定义接口
namespace ApiSourcce.Controllers { [Route("[controller]")] //设置路由 [Authorize] //身份验证,添加了这个,就会去身份认证服务端进行验证 public class IdentityController : Controller { [HttpGet] public IActionResult Get() { //这里是返回当前授权通过后的一些声明信息,当然,这个信息我们也可以自定义,返回字符串类型也可以 return new JsonResult(from c in User.Claims select new { c.Type, c.Value }); } } }
这里,我们返回授权通过后,获取到的一些声明,当然这个内容可以自定义,在实际项目当中,返回的是业务需求的内容。
第二步就是在Startup中进行服务注册
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); // services.AddAuthorization(); //注册授权 services.AddAuthentication("Bearer") //采用Bearer token的接口认证方式 .AddIdentityServerAuthentication(option => { option.ApiName = "api1"; //这里的name必须与客户端定义的Api资源中的name一致,不然请求不通过 option.Authority = "http://localhost:7000"; //身份认证服务端 option.RequireHttpsMetadata = false; //非https,设置为false }); } // 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()) { app.UseDeveloperExceptionPage(); } //使用身份验证中间件 app.UseAuthentication(); app.UseMvc(); } }
就是添加标红的几行代码。
设置Api资源站的端口
这样,我们就完成了API资源站点的设置
三、客户端
客户端,即调用端。
客户端通过身份认证服务端获取到token,带着token去请求API资源
我们新建一个asp.net core mvc项目 ClientCredentials
从nuget上引入IdentityModel包
在Home控制器上,编写我们的测试代码
public async Task<IActionResult> Index() { var client = new HttpClient(); var disco = await client.GetDiscoveryDocumentAsync("http://localhost:7000"); //请求身份认证服务端,即Id4的服务端 if (disco.IsError) { Console.WriteLine(disco.Error); return View(); } // request token 请求获取token var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = disco.TokenEndpoint, ClientId = "CredentialsClient", //必须与身份认证服务端定义的客户端Id一致 ClientSecret = "111111",//密码 Scope = "api1" //请求的api name,必须与身份认证服务端定义的Api name一致 }); if (tokenResponse.IsError) { Console.WriteLine(tokenResponse.Error); return View(); } Console.WriteLine(tokenResponse.Json); Console.WriteLine(" "); // call api var apiClient = new HttpClient(); apiClient.SetBearerToken(tokenResponse.AccessToken); var response = await apiClient.GetAsync("http://localhost:6010/identity/"); //带着token请求api资源站点 if (!response.IsSuccessStatusCode) { Console.WriteLine(response.StatusCode); } else { var content = await response.Content.ReadAsStringAsync(); //调用成功 Console.WriteLine(JArray.Parse(content)); return Content(content); } return View(); }
这样,我们就完成了整个客户端模式的编码工作。
我们运行起来看看,看客户端返回了什么
客户端收到了这个,这个就是我们Api资源 Identity控制器下接口返回的用户声明的信息。
至此,我们就完成了客户端模式的调用。