IdentityServer4:IdentityServer4+API+Client实践OAuth2.0客户端模式(1) - huoit - 博客园
Excerpt 一、OAuth2.0 1、OAuth2.0概念 OAuth2.0(Open Authorization)是一个开放授权协议;第三方应用不需要接触到用户的账户信息(如用户名密码),通过用户的授权访问用户资源 OAuth的步骤一般如下: 1、客户端要求用户给予授权2、用户同意给予授权3、根据上一步获得的
2017-08-28 11:52 huoit 阅读(1137 ) 评论() 编辑 收藏 举报
一、OAuth2.0 1、OAuth2.0概念 OAuth2.0(Open Authorization)是一个开放授权协议;第三方应用不需要接触到用户的账户信息(如用户名密码),通过用户的授权访问用户资源
OAuth的步骤一般如下:
1、客户端要求用户给予授权 2、用户同意给予授权 3、根据上一步获得的授权,向认证服务器请求令牌(token) 4、认证服务器对授权进行认证,确认无误后发放令牌 5、客户端使用令牌向资源服务器请求资源 6、资源服务器使用令牌向认证服务器确认令牌的正确性,确认无误后提供资源
该协议的参与者至少包含:
RO (resource owner): 资源所有者:用户。
RS (resource server): 资源服务器:数据中心;它存储资源,并处理对资源的访问请求。如:API资源,相册服务器、博客服务器。
AS (authorization server): 授权服务器
Client : 第三方应用
2、授权模式 四种模式:
1、授权码模式(authorization code) 2、简化模式(implicit) 3、密码模式(resource owner password credentials) 4、客户端模式(client credentials)
二、IdentityServer + API+Client演示客户端模式 客户端模式(ClientCredentials):经常运用于服务器对服务器中间通讯使用;步骤如下:
1、客户端直接用自身的信息向授权服务器请求token:
HTTP请求:
granttype:授权类型
scope:授权范围
1 2 3 4 5 6 POST /token HTTP/<span>1.1</span><span> Host: server.example.com Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW Content</span>-Type: application/x-www-form-<span>urlencoded grant_type</span>=client_credentials&scope=api001
2、授权服务器验证信息后返回token
1 2 3 4 5 6 7 8 9 10 11 HTTP/<span>1.1</span> <span>200</span><span> OK Content</span>-Type: application/json;charset=UTF-<span>8</span><span> Cache</span>-Control: no-<span>store Pragma: no</span>-<span>cache { </span><span>"</span><span>access_token</span><span>"</span>:<span>"</span><span>2YotnFZFEjr1zCsicMWpAA</span><span>"</span><span>, </span><span>"</span><span>token_type</span><span>"</span>:<span>"</span><span>example</span><span>"</span><span>, </span><span>"</span><span>expires_in</span><span>"</span>:<span>3600</span><span>, </span><span>"</span><span>example_parameter</span><span>"</span>:<span>"</span><span>example_value</span><span>"</span><span> }</span>
下面通过一个快速示例理解;快速示例将通过服务器与服务器直接通过api访问数据;
1、授权服务端; 这里将通过IdnetityServer4实现一个标准的Oauth2.0协议的服务端;
引用IdentityServer4包 新建ASP.NET Core Web Application ——Empty项目;这里通过程序包控制台添加IdentityServer4引用包
Install-Package IdentityServer4
定义API资源、定义客户端 新建类Config.cs;定义 资源Scopes、Client;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <span>using</span><span> IdentityServer4.Models; </span><span>using</span><span> System; </span><span>using</span><span> System.Collections.Generic; </span><span>using</span><span> System.Linq; </span><span>using</span><span> System.Threading.Tasks; </span><span>namespace</span><span> Practice.IdentityServer { </span><span>public</span> <span>class</span><span> Config { </span><span>//</span><span>scopes定义</span> <span>public</span> <span>static</span> IEnumerable<ApiResource><span> GetApiResource() { </span><span>return</span> <span>new</span> List<ApiResource><span> { </span><span>//</span><span>给api资源定义一个scopes</span> <span>new</span> ApiResource(<span>"</span><span>api1</span><span>"</span>,<span>"</span><span>my api</span><span>"</span><span>) }; } </span><span>//</span><span>客户端注册,客户端能够访问的资源(通过:AllowedScopes)</span> <span>public</span> <span>static</span> IEnumerable<Client><span> GetClient() { </span><span>return</span> <span>new</span> List<Client><span> { </span><span>new</span><span> Client { ClientId</span>=<span>"</span><span>client</span><span>"</span><span>, AllowedGrantTypes</span>=<span>GrantTypes.ClientCredentials, ClientSecrets</span>={<span>new</span> Secret(<span>"</span><span>secrect</span><span>"</span><span>.Sha256())}, AllowedScopes</span>={<span>"</span><span>api</span><span>"</span><span>} } }; } } }</span>
把资源和客户端、存储方式、添加到service container(DI system)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 <span>using</span><span> System; </span><span>using</span><span> System.Collections.Generic; </span><span>using</span><span> System.Linq; </span><span>using</span><span> System.Threading.Tasks; </span><span>using</span><span> Microsoft.AspNetCore.Builder; </span><span>using</span><span> Microsoft.AspNetCore.Hosting; </span><span>using</span><span> Microsoft.AspNetCore.Http; </span><span>using</span><span> Microsoft.Extensions.DependencyInjection; </span><span>using</span><span> Microsoft.Extensions.Logging; </span><span>namespace</span><span> Practice.IdentityServer { </span><span>public</span> <span>class</span><span> Startup { </span><span>//</span><span> 添加服务到容器(add services to the container)DI系统. </span> <span>public</span> <span>void</span><span> ConfigureServices(IServiceCollection services) { services.AddIdentityServer() .AddTemporarySigningCredential() .AddInMemoryApiResources(Config.GetApiResource()) .AddInMemoryClients(Config.GetClient()); } </span><span>//</span><span>配置HTTP request 管道(pipeline).</span> <span>public</span> <span>void</span><span> Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(LogLevel.Debug); </span><span>if</span><span> (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseIdentityServer(); </span><span>//</span><span>app.Run(async (context) => </span><span>//</span><span>{ </span><span>//</span><span> await context.Response.WriteAsync("Hello World!"); </span><span>//</span><span>});</span> <span> } } }</span>
配置 注意:使用自宿的方式调试;会把日志输出到控制台;自宿的配置方式:
方法1:
方法2:
配置地址:
在program.cs添加一句:.UseUrls(“http://localhost:5000 “) 设置调试url;
运行 运行、通过http://localhost:5000/.well-known/openid-configuration访问 ;可以看到是一个restful的api;
2、API资源 新建ASP.NET Core Web API 项目;添加中间件IdentityServer4.AccessTokenValidation 包引用
配置api的地址
添加控制器
1 2 3 4 5 6 7 8 9 10 [Route(<span>"</span><span>identity</span><span>"</span><span>)] [Authorize] </span><span>public</span> <span>class</span><span> IdentityController : Controller { [HttpGet] </span><span>public</span><span> IActionResult Get() { </span><span>return</span> <span>new</span> JsonResult(<span>from</span> a <span>in</span> User.Claims <span>select</span> <span>new</span><span> { a.Type,a.Value}); } }</span>
配置 把授权中间件配置到api host里;IdentityServer4.AccessTokenValidation这里的主要作用
1、验证token令牌,确保token令牌的Issuer发行者是经过注册认证可信任的发行者;
2、验证token令牌,确保这个令牌的授权范围(scope)包括授权使用这个api
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 <span>public</span> <span>class</span><span> Startup { </span><span>public</span><span> Startup(IHostingEnvironment env) { </span><span>var</span> builder = <span>new</span><span> ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile(</span><span>"</span><span>appsettings.json</span><span>"</span>, optional: <span>false</span>, reloadOnChange: <span>true</span><span>) .AddJsonFile($</span><span>"</span><span>appsettings.{env.EnvironmentName}.json</span><span>"</span>, optional: <span>true</span><span>) .AddEnvironmentVariables(); Configuration </span>=<span> builder.Build(); } </span><span>public</span> IConfigurationRoot Configuration { <span>get</span><span>; } </span><span>//</span><span> This method gets called by the runtime. Use this method to add services to the container.</span> <span>public</span> <span>void</span><span> ConfigureServices(IServiceCollection services) { </span><span>//</span><span> Add framework services.</span> <span> services.AddMvcCore() .AddAuthorization() .AddJsonFormatters(); } </span><span>//</span><span> This method gets called by the runtime. Use this method to configure the HTTP request pipeline.</span> <span>public</span> <span>void</span><span> Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection(</span><span>"</span><span>Logging</span><span>"</span><span>)); loggerFactory.AddDebug(); app.UseIdentityServerAuthentication(</span><span>new</span><span> IdentityServerAuthenticationOptions { Authority </span>= <span>"</span><span>http://localhost:5000</span><span>"</span><span>, RequireHttpsMetadata</span>=<span>false</span><span>, ApiName</span>=<span>"</span><span>api1</span><span>"</span><span> }); app.UseMvc(); } }</span>
运行后,直接浏览器访问http://localhost:5001/identity会被拒绝说明成功;访问这个api需要在http请求的header加入token才可以访问;
3、Client客户端 新建.Net Core——控制台应用;添加中间件
IdentityModel是官方提供给我们的一个Client类库;当然用户也可以自行构建原始的http协议访问API;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <span>public</span> <span>class</span><span> Program { </span><span>public</span> <span>static</span> <span>void</span> Main(<span>string</span>[] args) =><span> MainAsync().GetAwaiter().GetResult(); </span><span>private</span> <span>static</span> <span>async</span><span> Task MainAsync() { </span><span>// </span> <span>var</span> dico = <span>await</span> DiscoveryClient.GetAsync(<span>"</span><span>http://localhost:5000</span><span>"</span><span>); </span><span>//</span><span>token</span> <span>var</span> tokenClient = <span>new</span> TokenClient(dico.TokenEndpoint, <span>"</span><span>client</span><span>"</span>, <span>"</span><span>secret</span><span>"</span><span>); </span><span>var</span> tokenresp = <span>await</span> tokenClient.RequestClientCredentialsAsync(<span>"</span><span>api1</span><span>"</span><span>); </span><span>if</span><span> (tokenresp.IsError) { Console.WriteLine(tokenresp.Error); </span><span>return</span><span>; } Console.WriteLine(tokenresp.Json); Console.WriteLine(</span><span>"</span><span>\n\n</span><span>"</span><span>); </span><span>var</span> client = <span>new</span><span> HttpClient(); client.SetBearerToken(tokenresp.AccessToken); </span><span>var</span> resp = <span>await</span> client.GetAsync(<span>"</span><span>http://localhost:5000/identity</span><span>"</span><span>); </span><span>if</span> (!<span>resp.IsSuccessStatusCode) { Console.WriteLine(resp.StatusCode); } </span><span>else</span><span> { </span><span>var</span> content = <span>await</span><span> resp.Content.ReadAsStringAsync(); Console.WriteLine(JArray.Parse(content)); } } }</span>
DiscoveryClient类:IdentityModel提供给我们通过基础地址(如:http://localhost:5000)就可以访问令牌服务端;当然可以根据上面的restful api里面的url自行构建;上面就是通过基础地址,获取一个TokenClient;(对应restful的url:token_endpoint “http://localhost:5000/connect/token")
RequestClientCredentialsAsync方法:请求令牌;
获取令牌后,就可以通过构建http请求访问API接口;这里使用HttpClient构建请求,获取内容;
运行效果:
我们换一种原始的方式模拟这个流程
打开Postman:按照restful api页面的说明,依次进行下面的步骤操作,一个很原始的http流程就熟悉了;自行查看原图
资料:
http://wiki.connect.qq.com/%E4%BD%BF%E7%94%A8implicit\_grant%E6%96%B9%E5%BC%8F%E8%8E%B7%E5%8F%96access\_token