IdentityServer4之Claim(基于角色的权限控制)
IdentityServer4之Claim(基于角色的权限控制)
前言
Claim是什么?每个用户都有多个Claim,每个Claim声明了用户的某个信息比如:Role=Admin,UserID=1000等,这里Role,UserID每个都是用户的Claim,都是表示用户信息的单元 ,不妨就把它称为用户信息单元 。它使用一个字典进行存储,一个Key,一个Value。
环境
Visual Studio 2019 + .net 5.0
IdentityServer4 (4.0.0)
Microsoft.AspNetCore.Authentication.JwtBearer (5.0.0)
IdentityModel (6.0.0)
实现步骤
创建IDS4项目
dotnet CLI快速安装模板Identityserver模板
dotnet new -i IdentityServer4.Templates
创建IdentityServery 项目
dotnet new is4empty -n IdentityServer_ResourceOwnerPasswordCredentials
注意项目名不可以取IdentityServery 4,不然会和IdentityServery 4.Dll引用重名。
引用IdentityServer4依赖
这里使用4.0.0版本,如图:
修改端口
此模板中使用的协议是https,在 Kestrel 上运行时端口设置为 5001 或在 IISExpress 上运行时设置为随机端口。可以在Properties\launchSettings.json文件中更改它。
"profiles": {
"SelfHost": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:5001;http://localhost:5000"
}
}
这里修改为:
"applicationUrl": "https://localhost:5001;http://localhost:5000"
定义API接口的活动范围API Scope
API 是系统中要保护的资源。资源定义可以通过多种方式加载。模板已经创建了 Config.cs。打开它,将代码更新为如下所示:
public static IEnumerable
new ApiScope[]
{ new ApiScope("api1", "My API",new List
添加用户
就像基于内存存储的资源(即范围 Scopes)和客户端一样,对于用户也可以这样做。
TestUser类型表示一个测试用户及其身份信息。 Config.cs文件中添加以下代码以创建用户:
public static List
{
return new List
{
new TestUser
{
SubjectId = "1",
Username = "alice",
Password = "password",
Claims = new List
},
new TestUser
{
SubjectId = "2",
Username = "bob",
Password = "password",
Claims = new List
},
new TestUser
{
SubjectId = "3",
Username = "yak",
Password = "yakpassword",
Claims = new List
}
};
}
配置身份服务器
在Startup文件中配置身份服务器,加载资源和客户端,然后将测试用户注册到 IdentityServer中。代码如下:
public void ConfigureServices(IServiceCollection services)
{
var builder = services.AddIdentityServer()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.GetClients())
.AddTestUsers(Config.GetUsers());
builder.AddDeveloperSigningCredential();
}
AddTestUsers扩展方法在背后做了以下几件事:
(1)为资源所有者密码授权添加支持
(2)添加对用户相关服务的支持,这服务通常为登录 UI 所使用
(3)为基于测试用户的身份信息服务添加支持
添加客户端定义
可以通过修改AllowedGrantTypes属性简单地添加对已有客户端授权类型的支持。
通常要为资源所有者用例创建独立的客户端,添加以下代码到你配置中的客户端定义中
public static IEnumerable
{
return new List
{
// resource owner password grant client
new Client
{
ClientId = "yakclient",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret("yaksecret".Sha256())
},
AllowedScopes = { "api1", IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile }//需要额外添加
}
};
}
项目生成后会生成jwk加密文件,如图:
使用Postman测试工具测试
调用接口后返回access_token,测试结果如下
解密后:
新建WebAPI接口
新建接口,命名为Api,同时引用依赖:
Microsoft.AspNetCore.Authentication.JwtBearer.dll
修改接口:
注册认证相关组件和配置
在Startup.cs文件中修改ConfigureServices方法,代码如下:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
// 注册认证相关组件和配置defaultScheme为Bearer
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "http://localhost:5000";
// 在验证token时,不验证Audience
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false
};
options.RequireHttpsMetadata = false; // 不适用Https
});
services.AddAuthorization(options =>
{
options.AddPolicy("api1Scope", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireClaim("scope", "api1");
});
});
}
注意Configure方法中添加认证和授权
app.UseAuthentication();
app.UseAuthorization();
新建测试客户端
新建一个WinForm客户端,添加按钮及引用IdentityModel依赖,这里使用6.0.0版本。
按钮事件代码如下:
private void button1_Click(object sender, EventArgs e)
{
Task.FromResult(MainAsync());
}
private async Task MainAsync()
{
// discover endpoints from metadata
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");
if (disco.IsError)
{
textBox1.Text = disco.Error;
return;
}
textBox2.Text = disco.TokenEndpoint + "\r\n";
// request token
var tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "yakclient",
ClientSecret = "yaksecret",
UserName = "yak",
Password = "yakpassword",
Scope = "api1"
});
if (tokenResponse.IsError)
{
textBox1.Text = tokenResponse.Error;
return;
}
textBox2.Text += tokenResponse.Json.ToString();
// call api
var apiClient = new HttpClient();
apiClient.SetBearerToken(tokenResponse.AccessToken);
var response = await apiClient.GetAsync("http://localhost:4571/weatherforecast");
if (!response.IsSuccessStatusCode)
{
textBox1.Text += response.StatusCode.ToString();
}
else
{
var content = response.Content.ReadAsStringAsync().Result;
textBox1.Text += content;
}
}
启动程序
启动多个程序项目,注意IdentityServer_ResourceOwnerPasswordCredentials项目先启动。
测试结果如下
使用Postman测试如下:
总结
本想写简单点,可是不知不觉写了好多,因为在做的过程中可能很小的细节就可能导致项目运行不了,为了方便大家了解,还是尽可能详细吧!读者照着步骤做,印象会更深。
鸣谢
https://www.cnblogs.com/savorboard/p/aspnetcore-identity.html