介绍
作用
http://www.identityserver.com.cn/
IdentityServer4 是为ASP.NET Core 系列量身打造的一款基于 OpenID Connect 和 OAuth 2.0 认证框架。
官网
https://www.identityserver.io/
准备工作
安装模板
安装模板的作用是一会可以自动生成代码。
dotnet new -i IdentityServer4.Templates
创建项目
输入如下语句,即可创建一个客户端程序。
dotnet new is4aspid -n IdentityServerAspNetIdentity
或
dotnet new is4empty -n AuthenticationCenterIDS4
项目结构
Config配置使用什么模式进行鉴权授权。
使用dotnet new is4aspid -n IdentityServerAspNetIdentity默认带了客户端模式与code模式,密码模式简单进行下配置即可。
使用
完成模板创建后,就可以使用了
这里以控制台程序来访问IdentityServer。返回Token后,再带上Toen对受保护的鉴权授权api进行访问
class Program
{
static async Task Main(string[] args)
{
//如果要授权
var client = new HttpClient();
DiscoveryDocumentResponse disco = await client.GetDiscoveryDocumentAsync("http://localhost:5001/");
if (disco.IsError)
{
Console.WriteLine(disco.Error);
}
var accessTokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "m2m.client",
ClientSecret = "511536EF-F270-4058-80CA-1C89C192F69A",
Scope = "scope1"
});
if (accessTokenResponse.IsError)
{
Console.WriteLine(accessTokenResponse.Error);
}
var apiclient = new HttpClient();
apiclient.SetBearerToken(accessTokenResponse.AccessToken);
var resultResponse = await apiclient.GetAsync("http://localhost:5002/WeatherForecast/Get");
if (!resultResponse.IsSuccessStatusCode)
{
Console.WriteLine(resultResponse.StatusCode);
}
else
{
var content = await resultResponse.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content));
}
//之前都是postman ;如果需要使用postman,参数如何传递?
Console.Read();
}
}
WebApi受保护的资源
Startup文件中注册服务
#region 客户端模式
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.Authority = "http://localhost:5001";
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "http://localhost:5001",
ValidateAudience = true,
ValidAudience = "http://localhost:5001/resources",
ValidateIssuerSigningKey = true
};
});
#endregion
引用客户端验证
#region Identityserver4
{
app.UseAuthentication();
app.UseAuthorization();
}
#endregion
客户端模式
1>特点描述:
1.客户端模式不代表用户,授权是授权给某一个应用程序客户端;客户端本身就是资源所有者
2.通常用于机器和机器的通信
3.客户端也需要身份验证
2>流程实操:
1.授权,配置密码模式,生成Token;
Startup中的ConfigureServices 方法中添加:
var builder = services.AddIdentityServer(options =>
{
// see https://identityserver4.readthedocs.io/en/latest/topics/resources.html
options.EmitStaticAudienceClaim = true;
})
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients);
builder.AddDeveloperSigningCredential();
支持配置:
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<Client> Clients =>
new Client[]
{
new Client
{
ClientId = "m2m.client",
ClientName = "Client Credentials Client",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets = { new Secret("511536EF-F270-4058-80CA-1C89C192F69A".Sha256()) },
AllowedScopes = { "scope1" }
}
};
}
2.获取token,Postman访问ids4鉴权中心:http://localhost:5400/connect/token
3.Postman访问Api:带token:http://localhost:5200/api/Third/getlist
3>请求规则:
获取Token:
访问受保护的Api:
密码模式
1>特点描述
1.资源所有的密码凭证(用户名密码),直接用来请求accessToken
2.通常用于遗留的应用
3.资源所有者和客户端之间必须高度信任
4.尽量不用,其他授权方式不可用的时候才使用
2>流程实操
1.IdentityServer 增加Ui,计入AuthenticationCenterIDS4执行命令
dotnet new is4ui
命令执行完毕后,会出现Quickstart、Views 之类的一个MVC界面
2.增加用户: .AddTestUsers(PasswordInitConfig.GetUsers()); 来自于Quickstart文件夹下
Startup中的ConfigureServices 方法中添加:
services.AddIdentityServer()
.AddDeveloperSigningCredential()//默认的开发者证书
.AddInMemoryIdentityResources(PasswordInitConfig.GetIdentityResourceV4X())//API访问授权资源
.AddInMemoryClients(PasswordInitConfig.GetClients()) //客户端
.AddTestUsers(PasswordInitConfig.GetUsers());//添加用户
3.默认TestUser
public class TestUsers
{
public static List<TestUser> Users
{
get
{
var address = new
{
street_address = "One Hacker Way",
locality = "Heidelberg",
postal_code = 69118,
country = "Germany"
};
return new List<TestUser>
{
new TestUser
{
SubjectId = "818727",
Username = "alice",
Password = "alice",
Claims =
{
new Claim(JwtClaimTypes.Name, "Alice Smith"),
new Claim(JwtClaimTypes.GivenName, "Alice"),
new Claim(JwtClaimTypes.FamilyName, "Smith"),
new Claim(JwtClaimTypes.Email, "AliceSmith@email.com"),
new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
new Claim(JwtClaimTypes.WebSite, "http://alice.com"),
new Claim(JwtClaimTypes.Address, JsonSerializer.Serialize(address), IdentityServerConstants.ClaimValueTypes.Json)
}
},
new TestUser
{
SubjectId = "88421113",
Username = "bob",
Password = "bob",
Claims =
{
new Claim(JwtClaimTypes.Name, "Bob Smith"),
new Claim(JwtClaimTypes.GivenName, "Bob"),
new Claim(JwtClaimTypes.FamilyName, "Smith"),
new Claim(JwtClaimTypes.Email, "BobSmith@email.com"),
new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
new Claim(JwtClaimTypes.WebSite, "http://bob.com"),
new Claim(JwtClaimTypes.Address, JsonSerializer.Serialize(address), IdentityServerConstants.ClaimValueTypes.Json)
}
}
};
}
}
}
4.获取Token: 这个是一个Winform程序
private async void RequestaccessTokenBtn_Click(object sender, EventArgs e)
{
//如果要授权
var client = new HttpClient();
disco = await client.GetDiscoveryDocumentAsync("https://localhost:5001");
if (disco.IsError)
{
MessageBox.Show(disco.Error.ToString());
return;
}
string userName = this.userNameTxbox.Text;
string password = this.PassordTxbox.Text;
var accessTokneResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest()
{
Address = disco.TokenEndpoint,
ClientId = "passwordClientId",
ClientSecret = "AAAAAAAA-F270-4058-80CA-1C89C192F69A",
Scope = "scope1 openid",
UserName = userName,
Password = password
});
if (accessTokneResponse.IsError)
{
MessageBox.Show(accessTokneResponse.Error);
}
this.accessTokenTxBox.Text = accessTokneResponse.AccessToken;
this.accessToken = accessTokneResponse.AccessToken;
}
5.请求Api
private async void RequestApiResource_Click(object sender, EventArgs e)
{
HttpClient client = new HttpClient();
client.SetBearerToken(this.accessToken);
var apiResourceResponse = await client.GetAsync("http://localhost:5003/WeatherForecast/Get");
if (!apiResourceResponse.IsSuccessStatusCode)
{
Console.WriteLine(apiResourceResponse.StatusCode);
}
else
{
var content = await apiResourceResponse.Content.ReadAsStringAsync();
Console.WriteLine(Newtonsoft.Json.Linq.JArray.Parse(content));
this.apiResourConsole.Text = content;
}
}
6.请求userInfo 用户信息
private async void ReauestIdentityBtn_Click(object sender, EventArgs e)
{
HttpClient client = new HttpClient();
client.SetBearerToken(this.accessToken);
var apiResourceResponse = await client.GetAsync(disco.UserInfoEndpoint);
if (!apiResourceResponse.IsSuccessStatusCode)
{
Console.WriteLine(apiResourceResponse.StatusCode);
}
else
{
var content = await apiResourceResponse.Content.ReadAsStringAsync();
this.IdentityResourceConsole.Text = content;
}
}
7.请求更多用户信息
授权层配置:IdentityResources配置:
public static IEnumerable<IdentityResource> IdentityResources =>
new IdentityResource[]
{
new IdentityResources.OpenId(),
new IdentityResources.Address(), //更多内容
new IdentityResources.Email(), //更多内容
new IdentityResources.Phone(),
new IdentityResources.Profile(),
};
授权层配置:Client配置:
new Client
{
ClientId = "passwordClientId",
ClientName = "passwordClientName",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets = { new Secret("AAAAAAAA-F270-4058-80CA-1C89C192F69A".Sha256()) },
AllowedScopes = {
"scope1",
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.Phone,
IdentityServerConstants.StandardScopes.Address,
IdentityServerConstants.StandardScopes.Profile
}
}