- 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
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
TestUsers
没有做修改,用项目模板默认生成的就行。这里定义了2个用户 alice,bob,密码与用户名相同。
至此,鉴权中心的代码修改就差不多了。这个项目也不放 docker
了,直接用 vs
来启动,让他运行在9080端口。/Properties/launchSettings.json
修改一下:
"applicationUrl": "http://localhost:9080"
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
二、Ocelot集成ids4
2.1 Ocelot保护api资源
鉴权中心搭建完成,下面整合到之前的 Ocelot.APIGateway
网关项目中。
首先NuGet安装 IdentityServer4.AccessTokenValidation

修改Startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication("orderService", options =>
{
options.Authority = "http://localhost:9080";
options.ApiName = "orderApi";
options.SupportedTokens = SupportedTokens.Both;
options.ApiSecret = "orderApi secret";
options.RequireHttpsMetadata = false;
})
.AddIdentityServerAuthentication("productService", options =>
{
options.Authority = "http://localhost:9080";
options.ApiName = "productApi";
options.SupportedTokens = SupportedTokens.Both;
options.ApiSecret = "productApi secret";
options.RequireHttpsMetadata = false;
});
services.AddOcelot()
.AddConsul()
.AddCacheManager(x =>
{
x.WithDictionaryHandle();
})
.AddPolly();
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
- 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
修改 ocelot.json
配置文件:
{
"DownstreamPathTemplate": "/products",
"DownstreamScheme": "http",
"UpstreamPathTemplate": "/products",
"UpstreamHttpMethod": [ "Get" ],
"ServiceName": "ProductService",
......
"AuthenticationOptions": {
"AuthenticationProviderKey": "productService",
"AllowScopes": []
}
},
{
"DownstreamPathTemplate": "/orders",
"DownstreamScheme": "http",
"UpstreamPathTemplate": "/orders",
"UpstreamHttpMethod": [ "Get" ],
"ServiceName": "OrderService",
......
"AuthenticationOptions": {
"AuthenticationProviderKey": "orderService",
"AllowScopes": []
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
- 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
添加了 AuthenticationOptions
节点,AuthenticationProviderKey
对应的是上面 Startup
中的定义。
2.2 Ocelot代理ids4
既然网关是客户端访问 api
的统一入口,那么同样可以作为鉴权中心的入口。使用 Ocelot
来做代理,这样客户端也无需知道鉴权中心的地址,同样修改 ocelot.json
:
{
"DownstreamPathTemplate": "/{url}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 9080
}
],
"UpstreamPathTemplate": "/auth/{url}",
"UpstreamHttpMethod": [
"Get",
"Post"
],
"LoadBalancerOptions": {
"Type": "RoundRobin"
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
添加一个鉴权中心的路由,实际中鉴权中心也可以部署多个实例,也可以集成 Consul
服务发现,实现方式跟前面章节讲的差不多,这里就不再赘述。
让网关服务运行在9070端口,/Properties/launchSettings.json
修改一下:
"applicationUrl": "http://localhost:9070"
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
三、客户端集成
首先NuGet安装 Microsoft.AspNetCore.Authentication.OpenIdConnect

修改 Startup
:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.Authority = "http://localhost:9070/auth";
options.ClientId = "web client";
options.ClientSecret = "web client secret";
options.ResponseType = "code";
options.RequireHttpsMetadata = false;
options.SaveTokens = true;
options.Scope.Add("orderApiScope");
options.Scope.Add("productApiScope");
});
services.AddControllersWithViews();
services.AddSingleton<IServiceHelper, GatewayServiceHelper>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceHelper serviceHelper)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
- 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
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
修改 /Helper/IServiceHelper
,方法定义增加 accessToken
参数:
Task<string> GetProduct(string accessToken);
Task<string> GetOrder(string accessToken);
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
修改 /Helper/GatewayServiceHelper
,访问接口时增加 Authorization
参数,传入 accessToken
:
public async Task<string> GetOrder(string accessToken)
{
var Client = new RestClient("http://localhost:9070");
var request = new RestRequest("/orders", Method.GET);
request.AddHeader("Authorization", "Bearer " + accessToken);
var response = await Client.ExecuteAsync(request);
if (response.StatusCode != HttpStatusCode.OK)
{
return response.StatusCode + " " + response.Content;
}
return response.Content;
}
public async Task<string> GetProduct(string accessToken)
{
var Client = new RestClient("http://localhost:9070");
var request = new RestRequest("/products", Method.GET);
request.AddHeader("Authorization", "Bearer " + accessToken);
var response = await Client.ExecuteAsync(request);
if (response.StatusCode != HttpStatusCode.OK)
{
return response.StatusCode + " " + response.Content;
}
return response.Content;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
- 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
最后是 /Controllers/HomeController
的修改。添加 Authorize
标记:
[Authorize]
public class HomeController : Controller
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
修改 Index action
,获取 accessToken
并传入:
public async Task<IActionResult> Index()
{
var accessToken = await HttpContext.GetTokenAsync("access_token");
ViewBag.OrderData = await _serviceHelper.GetOrder(accessToken);
ViewBag.ProductData = await _serviceHelper.GetProduct(accessToken);
return View();
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
至此,客户端集成也已完成。
四、测试
为了方便,鉴权中心、网关、web客户端这3个项目都使用vs来启动,他们的端口分别是9080,9070,5000。之前的 OrderAPI
和 ProductAPI
还是在 docker
中不变。
为了让vs能同时启动多个项目,需要设置一下,解决方案右键属性:

Ctor+F5启动项目。
3个项目都启动完成后,浏览器访问 web
客户端:http://localhost:5000/

因为我还没登录,所以请求直接被重定向到了鉴权中心的登录界面。使用 alice/alice
这个账户登录系统。

登录成功后,进入授权同意界面,你可以同意或者拒绝,还可以选择勾选 scope
权限。点击 Yes,Allow
按钮同意授权:

清除 Cookie
后,刷新页面又会转到 ids4
的登录界面,这次使用 bob/bob
登录:

这次只勾选 orderApiScope
,点击Yes,Allow
:

这次客户端就只能访问订单服务了。当然也可以在鉴权中心去限制客户端的api权限,也可以在网关层面ocelot.json中限制,相信你已经知道该怎么做了。
五、总结
本文主要完成了 IdentityServer4
鉴权中心、Ocelot
网关、web
客户端之间的整合,实现了系统的统一授权认证。授权认证是几乎每个系统必备的功能,而 IdentityServer4
是 .Net Core 下优秀的授权认证方案。
再次推荐一下B站@solenovex 杨老师的视频,地址:https://www.bilibili.com/video/BV16b411k7yM ,虽然视频有点老了,但还是非常受用。
需要代码的点这里:https://github.com/xiajingren/NetCoreMicroserviceDemo

评论记录:
回复评论: