点击上方蓝字"小黑在哪里"关注我吧
前言
IdentityServer4 是为ASP.NET Core系列量身打造的一款基于 OpenID Connect 和 OAuth 2.0 认证的框架
IdentityServer4官方文档:https://identityserver4.readthedocs.io/
看这篇文章前默认你对IdentityServer4 已经有一些了解。
本篇使用IdentityServer4的4.x版本,跟老版本的稍微有些差别。下面直接进入正题。
鉴权中心
创建IdentityServer4项目
使用IdentityServer4 来搭建一个鉴权中心,首先建议安装一下IdentityServer4的官方项目模板。也可以不安装,自己创建项目,然后NuGet安装需要的包也行。(不过还是推荐用官方的模板,很方便)。
命令行执行:dotnet new -i IdentityServer4.Templates
![](https://i-blog.csdnimg.cn/blog_migrate/c3ed0aa65856ed54ea704da3ac1a8327.png)
安装完成后会多出以下项目模板:
![](https://i-blog.csdnimg.cn/blog_migrate/3841b87491ed23ccee8ad7d0c912b273.png)
我这里选用is4inmem这个模板来创建项目,这个模板的数据都是写死在内存中的,并且包含了Quickstart页面,比较简单方便。
来到我的项目目录下执行:dotnet new is4inmem --name Idp
![](https://i-blog.csdnimg.cn/blog_migrate/c73a466b01f07d6e7654f17e39b9fd07.png)
执行完成会生成以下文件:
![](https://i-blog.csdnimg.cn/blog_migrate/8d2478b86a9c9c30bc942ada5cd009af.png)
VS2019打开项目:
![](https://i-blog.csdnimg.cn/blog_migrate/1ccbec2cb431ae2c285bcb3ef965e6ed.png)
运行项目:
![](https://i-blog.csdnimg.cn/blog_migrate/107344809efed36c1cd81fbc642eef87.png)
配置ApiResource、ApiScope、Clients
修改Startup:
// in-memory, code config
builder.AddInMemoryIdentityResources(Config.IdentityResources);
builder.AddInMemoryApiScopes(Config.ApiScopes);
//添加API资源
builder.AddInMemoryApiResources(Config.ApiResources);
builder.AddInMemoryClients(Config.Clients);
这里比之前版本多了一个添加ApiScopes的方法:
builder.AddInMemoryApiScopes(Config.ApiScopes);
因为我接下来有要保护的API资源,所以需要添加一行:
builder.AddInMemoryApiResources(Config.ApiResources);
Config中的代码:
public static class Config
{
public static IEnumerable<IdentityResource> IdentityResources =>
new IdentityResource[]
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
public static IEnumerable<ApiScope> ApiScopes =>
new ApiScope[]
{
new ApiScope("scope1"),
//new ApiScope("scope2"),
};
public static IEnumerable<ApiResource> ApiResources =>
new ApiResource[]
{
new ApiResource("api1","#api1")
{
//!!!重要
Scopes = { "scope1"}
},
//new ApiResource("api2","#api2")
//{
// //!!!重要
// Scopes = { "scope2"}
//},
};
public static IEnumerable<Client> Clients =>
new Client[]
{
new Client
{
ClientId = "postman client",
ClientName = "Client Credentials Client",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets = { new Secret("postman secret".Sha256()) },
AllowedScopes = { "scope1" }
},
};
}
我添加了一个ID为postman client的客户端,授权模式就用最简单的ClientCredentials客户端模式。需要注意的是4.x版本的ApiScope和ApiResource是分开配置的,然后在ApiResource中一定要添加Scopes。如果你在网上搜的IdentityServer4教程比较老的,都是没有这个ApiScope的,默认ApiResource的Name作为Scope。类似这样:
public static IEnumerable<ApiResource> ApiResources =>
new ApiResource[]
{
new ApiResource("api1","#api1"),//错误
new ApiResource("api2","#api2"),//错误
};
public static IEnumerable<Client> Clients =>
new Client[]
{
new Client
{
......
AllowedScopes = { "api1", "api2" }
},
};
如果你这么写的话,虽然不影响你获取token,但是你访问api资源的话,永远会得到一个401错误!!!
ApiResource
下面添加一个api1资源,新建asp.netcore web应用并使用webapi模板:
![](https://i-blog.csdnimg.cn/blog_migrate/49d2472c93a2105c784838346b9df1b7.png)
NuGet安装:Microsoft.AspNetCore.Authentication.JwtBearer
Startup部分代码:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
//IdentityServer地址
options.Authority = "http://localhost:5001";
//对应Idp中ApiResource的Name
options.Audience = "api1";
//不使用https
options.RequireHttpsMetadata = false;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
//身份验证
app.UseAuthentication();
//授权
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
给WeatherForecastController添加[Authorize]
标记:
![](https://i-blog.csdnimg.cn/blog_migrate/ef009918dd6bb49ee6d8f48c82f01a05.png)
运行Api1Resource,用postman测试访问weatherforecast接口:
![](https://i-blog.csdnimg.cn/blog_migrate/8fe4cb94de4910d1a6e53300d6284058.png)
此时得到401错误。下面先去Idp获取一个token:
![](https://i-blog.csdnimg.cn/blog_migrate/8b89ea61f0a0af0815e9b76815cb52b9.png)
拿到token后再去访问weatherforecast就没问题了:
![](https://i-blog.csdnimg.cn/blog_migrate/4a873f11dd08e5136c045be0fa149d77.png)
进行到这里,好像跟scope都没什么关系,那么scope到底有什么用处呢?
ApiScope策略授权
继续修改代码。
Api1Resource项目NuGet安装:IdentityServer4.AccessTokenValidation
![](https://i-blog.csdnimg.cn/blog_migrate/eab35267a9ada502e892c0f0d64d9819.png)
再新建一个TestController用于区分:
![](https://i-blog.csdnimg.cn/blog_migrate/13dc33d341a56322375224740d3385fa.png)
下面我需要做的是使用scope结合策略授权来分别限制TestController和WeatherForecastController的访问权限。
修改Startup:
public void ConfigureServices(IServiceCollection services)
{
......
services.AddAuthorization(options =>
{
//基于策略授权
options.AddPolicy("WeatherPolicy", builder =>
{
//客户端Scope中包含api1.weather.scope才能访问
builder.RequireScope("api1.weather.scope");
});
//基于策略授权
options.AddPolicy("TestPolicy", builder =>
{
//客户端Scope中包含api1.test.scope才能访问
builder.RequireScope("api1.test.scope");
});
});
}
为了好理解,我把scope名称分别改成了:api1.weather.scope和api1.test.scope。
WeatherForecastController的Authorize标记修改一下:[Authorize(Policy = "WeatherPolicy")]
TestController的代码很简单:
![](https://i-blog.csdnimg.cn/blog_migrate/c796c4a5716c4bcfbdf2d08b5dea7cbc.png)
因为修改了scope名称,需要把Idp中的scope名称也改一下:
public static IEnumerable<ApiScope> ApiScopes =>
new ApiScope[]
{
new ApiScope("api1.weather.scope"),
new ApiScope("api1.test.scope"),
//new ApiScope("scope2"),
};
public static IEnumerable<ApiResource> ApiResources =>
new ApiResource[]
{
new ApiResource("api1","#api1")
{
//!!!重要
Scopes = { "api1.weather.scope", "api1.test.scope" }
},
//new ApiResource("api2","#api2")
//{
// //!!!重要
// Scopes = { "scope2"}
//},
};
客户端定义,AllowedScopes暂时只给一个api1.weather.scope测试一下
public static IEnumerable<Client> Clients =>
new Client[]
{
new Client
{
ClientId = "postman client",
ClientName = "Client Credentials Client",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets = { new Secret("postman secret".Sha256()) },
AllowedScopes = { "api1.weather.scope" }
},
};
postman获取token:
![](https://i-blog.csdnimg.cn/blog_migrate/965c75dad6004072e2d2ece5ce405626.png)
访问weatherforecast接口,正常响应200。
![](https://i-blog.csdnimg.cn/blog_migrate/09f714bed44ae93fee8a514f4ec597c8.png)
再访问test,得到403错误:
![](https://i-blog.csdnimg.cn/blog_migrate/d46690259fdf40febebb7f706598d219.png)
接下来修改一下Idp的客户端定义,添加api1.test.scope:
AllowedScopes = { "api1.weather.scope", "api1.test.scope" }
修改Idp后一定要重新获取token,jwt就是这样,一旦生成就无法改变。
![](https://i-blog.csdnimg.cn/blog_migrate/1b5c996998d6c1114481073464d81fc3.png)
拿到新的token后访问test和weatherforecast,这时候就都可以正常响应了。
![](https://i-blog.csdnimg.cn/blog_migrate/5081ad085b210cf333da89109facdb74.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bf68dd98a110f03986412ba97603f44f.png)
总结
以上使用IdentityServer4搭建了一个鉴权中心,保护API资源,并使用ApiScope配合策略授权完成了一个简单的权限控制。IdentityServer4的玩法非常多,知识点也很多。强烈推荐B站的@solenovex 杨老师的视频,地址:https://www.bilibili.com/video/BV16b411k7yM 多看几遍,会有收获。。。
需要代码的点这里:https://github.com/xiajingren/IdentityServer4-4.x-Scope-Demo
- END -如果本文对您有用,
不妨点个“在看”或者转发朋友圈支持一下