接上一篇:IdentityServer4 初识,上一篇介绍了客户端模式保护API访问。这一篇讲IdentityServer4 使用用户名和密码模式保护API访问。
public static List<TestUser> GetUsers() {
return new List<TestUser>()
{
new TestUser()
{
//用户名
Username="apiUser",
//密码
Password="apiUserPassword",
//用户Id
SubjectId="0"
}
};
}
添加好用户还需要要将用户注册到IdentityServer4,修改IdentityServer项目的Startup类ConfigureServices方法
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
//添加IdentityServer
var builder = services.AddIdentityServer()
//身份信息授权资源
.AddInMemoryIdentityResources(Config.GetIdentityResources())
//API访问授权资源
.AddInMemoryApiResources(Config.GetApis())
//客户端
.AddInMemoryClients(Config.GetClients())
//添加用户
.AddTestUsers(Config.GetUsers());
if (Environment.IsDevelopment())
{
builder.AddDeveloperSigningCredential();
}
else
{
throw new Exception("need to configure key material");
}
}
public static IEnumerable<Client> GetClients()
{
return new Client[] {
new Client()
{
//客户端Id
ClientId="apiClientCd",
//客户端密码
ClientSecrets={new Secret("apiSecret".Sha256()) },
//客户端授权类型,ClientCredentials:客户端凭证方式
AllowedGrantTypes=GrantTypes.ClientCredentials,
//允许访问的资源
AllowedScopes={
"secretapi"
}
},
new Client()
{
//客户端Id
ClientId="apiClientPassword",
//客户端密码
ClientSecrets={new Secret("apiSecret".Sha256()) },
//客户端授权类型,ClientCredentials:客户端凭证方式
AllowedGrantTypes=GrantTypes.ResourceOwnerPassword,
//允许访问的资源
AllowedScopes={
"secretapi"
}
}
};
}
至此,服务端工作完成。转到IentityApi项目。
[HttpGet]
[Route("api/getToken")]
public async Task<object> GetCdTokenAsync(string type,bool? request)
{
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");
TokenResponse resp = null;
switch (type)
{
case "cd":
resp = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
//获取Token的地址
Address = disco.TokenEndpoint,
//客户端Id
ClientId = "apiClientCd",
//客户端密码
ClientSecret = "apiSecret",
//要访问的api资源
Scope = "secretapi"
});
break;
case "pass":
resp = await client.RequestPasswordTokenAsync(new PasswordTokenRequest()
{
//获取Token的地址
Address = disco.TokenEndpoint,
//客户端Id
ClientId = "apiClientPassword",
//客户端密码
ClientSecret = "apiSecret",
//要访问的api资源
Scope = "secretapi",
//用户名
UserName = "apiUser",
//密码
Password = "apiUserPassword"
});
break;
}
//如果request为true,直接利用token访问被保护的api
if (request??false&&null!=resp)
{
//添加Bearer认证头
client.SetBearerToken(resp.AccessToken);
var reslut =await client.GetStringAsync("https://localhost:5001/api/identity");
JArray json = JArray.Parse(reslut);
return json;
}
return resp?.Json;
}

获取到Token后,访问受保护的API和通过客户端模式一样。

到目前为止,昨们还没有搞清这两个模式有什么区别,如果仅仅是为了能访问这个API,那加不加用户名和密码有什么区别呢。昨们对比下这两种模式取得Token后访问api返回的数据,可以发现用户名密码模式返回的Claim的数量要多一些。Claim是什么呢,简尔言之,是请求方附带在Token中的一些信息。但客户端模式不涉及到用户信息,所以返回的Claim数量会少一些。在IdentityServer4中,TestUser有一个Claims属性,允许自已添加Claim,有一个ClaimTypes枚举列出了可以直接添加的Claim。添加一个ClaimTypes.Role试试。
IdentityServer.Config.GetUsers
public static List<TestUser> GetUsers() {
return new List<TestUser>()
{
new TestUser()
{
//用户名
Username="apiUser",
//密码
Password="apiUserPassword",
//用户Id
SubjectId="0",
Claims=new List<Claim>(){
new Claim(ClaimTypes.Role,"admin")
}
}
};
}
这时如果启动两个项目,采用用户密码和密码模式获取Token访问Api,返回的值依然是没有role:admin的Claim的。这时又要用到ApiResouce,ApiResouce的构造函数有一个重载支持传进一个Claim集合,用于允许该Api资源可以携带那些Claim。
IdentityServer.Config.GetApis
public static IEnumerable<ApiResource> GetApis()
{
return new ApiResource[] {
//secretapi:标识名称,Secret Api:显示名称,可以自定义
new ApiResource("secretapi","Secret Api",new List<string>(){ ClaimTypes.Role})
};
}
现在可以启动IdentityApi和IdentityServer两个项目测试一下,可以发现已经可以返回role这个claim了。

Role(角色)这个Claim很有用,可以用来做简单的权限管理。
首先修改下被保护Api的,使其支持Role验证
IdentityApi.Controllers.IdentityController.GetUserClaims
[HttpGet]
[Route("api/identity")]
[Microsoft.AspNetCore.Authorization.Authorize(Roles ="admin")]
public object GetUserClaims()
{
return User.Claims.Select(r => new { r.Type, r.Value });
}
然后在IdentityServer端添加一个来宾角色用户
IdentityServer.Config.GetUsers
public static List<TestUser> GetUsers() {
return new List<TestUser>()
{
new TestUser()
{
//用户名
Username="apiUser",
//密码
Password="apiUserPassword",
//用户Id
SubjectId="0",
Claims=new List<Claim>(){
new Claim(ClaimTypes.Role,"admin")
}
},
new TestUser()
{
//用户名
Username="apiUserGuest",
//密码
Password="apiUserPassword",
//用户Id
SubjectId="1",
Claims=new List<Claim>(){
new Claim(ClaimTypes.Role,"guest")
}
}
};
}
再回到IdentityApi,修改下测试接口,把用户名和密码参数化,方便调试
IdentityApi.Controllers.IdentityController.getCdTokenAsync
[HttpGet]
[Route("api/getToken")]
public async Task<object> GetCdTokenAsync(string type,bool? request,string username,string password)
{
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");
TokenResponse resp = null;
switch (type)
{
case "cd":
resp = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
//获取Token的地址
Address = disco.TokenEndpoint,
//客户端Id
ClientId = "apiClientCd",
//客户端密码
ClientSecret = "apiSecret",
//要访问的api资源
Scope = "secretapi"
});
break;
case "pass":
resp = await client.RequestPasswordTokenAsync(new PasswordTokenRequest()
{
//获取Token的地址
Address = disco.TokenEndpoint,
//客户端Id
ClientId = "apiClientPassword",
//客户端密码
ClientSecret = "apiSecret",
//要访问的api资源
Scope = "secretapi",
//用户名
UserName = username,
//密码
Password = password
});
break;
}
if (request??false&&null!=resp)
{
//添加Bearer认证头
client.SetBearerToken(resp.AccessToken);
var reslut =await client.GetStringAsync("https://localhost:5001/api/identity");
JArray json = JArray.Parse(reslut);
return json;
}
return resp?.Json;
}
分别用apiUser和apiUserGuest访问


apiUserGuest访问被拒绝。
上边是添加ClaimTypes枚举里定义好的Claim,但如果要定义的Claim不在Claim枚举里应该怎么办呢,比如我想所有用户都有一个项目编号,要添加一个名为prog的Claim。
先在ApiResouce里允许携带名为prog.Claim
IdentityServer.Config.GetApis
public static IEnumerable<ApiResource> GetApis()
{
return new ApiResource[] {
//secretapi:标识名称,Secret Api:显示名称,可以自定义
new ApiResource("secretapi","Secret Api",new List<string>(){ ClaimTypes.Role,ClaimTypes.Name,"prog"})
};
}
在用户定义的Claims属性里添加prog信息
IdentityServer.Config.
GetUsers
public static List<TestUser> GetUsers() {
return new List<TestUser>()
{
new TestUser()
{
//用户名
Username="apiUser",
//密码
Password="apiUserPassword",
//用户Id
SubjectId="0",
Claims=new List<Claim>(){
new Claim(ClaimTypes.Role,"admin"),
new Claim("prog","正式项目"),
}
},
new TestUser()
{
//用户名
Username="apiUserGuest",
//密码
Password="apiUserPassword",
//用户Id
SubjectId="1",
Claims=new List<Claim>(){
new Claim(ClaimTypes.Role,"guest"),
new Claim("prog","测试项目"),
}
}
};
}
使用apiUser访问

用户密码和密码模式就讲到这,两种模式讲完,Config类里的IdentityResource还一点都没用上,它倒底有什么用呢,下一节讲另外二种模式:授权码模式和隐藏模式会用到它。
IdentityServer4 使用用户名和密码保护API访问
原文:https://www.cnblogs.com/liujiabing/p/11460486.html