0%

IdentityServer4:IdentityServer4+API+Client实践OAuth2.0客户端模式(1) - huoit - 博客园

Excerpt

一、OAuth2.0 1、OAuth2.0概念 OAuth2.0(Open Authorization)是一个开放授权协议;第三方应用不需要接触到用户的账户信息(如用户名密码),通过用户的授权访问用户资源 OAuth的步骤一般如下: 1、客户端要求用户给予授权2、用户同意给予授权3、根据上一步获得的


  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&amp;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&lt;ApiResource&gt;<span> GetApiResource()
{
</span><span>return</span> <span>new</span> List&lt;ApiResource&gt;<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&lt;Client&gt;<span> GetClient()
{
</span><span>return</span> <span>new</span> List&lt;Client&gt;<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) =&gt;
</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) =&gt;<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