一、在 ImageGallery Client 项目启用 IDP
- 更改项目属性为使用 SLL, 方法同之前配置 IDP 一样
- 修改 IDP 项目的 Config 类中的 GetClients 方法,增加一个客户端
public static IEnumerable GetClients()
{
return new List()
{
new Client()
{
ClientName = "Image Gallery",
ClientId = "junguoguoimagegalleryclient",
AllowedGrantTypes = GrantTypes.Hybrid,
RedirectUris = new List()
{
"https://localhost:44314/signin-oidc"
},
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile
},
ClientSecrets =
{
new Secret("junguoguosecret".Sha256())
}
}
};
}
- 修改 ImageGallery.Client 的 startup 类, 支持 IDP 权限验证
- ConfigureServices 方法增加如下:
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
}).AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
// IDP server 的url
options.Authority = "https://localhost:44319/";
options.ClientId = "junguoguoimagegalleryclient";
// specify use hybrid mode
options.ResponseType = "code id_token";
//options.CallbackPath = new PathString("oidc");
options.Scope.Add("openid");
options.Scope.Add("profile");
options.SaveTokens = true;
options.ClientSecret = "junguoguosecret";
});
- Configure 方法增加如下
// 需要放在 UseMvc 前
app.UseAuthentication();
二、权限验证实际操作
- 在需要增加权限的地方加上属性标记
[Authorize]
,这里我们直接在类GalleryController
上添加
添加如下方法
private async Task WriteOutIdentityInformation()
{
// 获取保存的 identity token
var identityToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.IdToken);
Debug.WriteLine($"Identity token:{identityToken}");
// 打印出 User Claims
foreach (var claim in User.Claims)
{
Debug.WriteLine($"Claim type : {claim.Type} - Claim value : {claim.Value}");
}
}
并在 Index() 开头调用 await WriteOutIdentityInformation();
调试查看Log
IdentityServer4.Endpoints.AuthorizeEndpoint:Information: ValidatedAuthorizeRequest
{
"ClientId": "junguoguoimagegalleryclient",
"ClientName": "Image Gallery",
"RedirectUri": "https://localhost:44314/signin-oidc",
"AllowedRedirectUris": [
"https://localhost:44314/signin-oidc"
],
"SubjectId": "anonymous",
"ResponseType": "code id_token",
"ResponseMode": "form_post",
"GrantType": "hybrid",
"RequestedScopes": "openid profile",
"State": "CfDJ8MUXARYmDzBChfPQYArN2SFocvVoTFAx3QMN6tR6pIKi3s3D5eabWiyKUI45I_TYXf3VFhHYOR8v_k97C5vARQJKugB4mGVzinSNi3-Lbn7N0tMsSH1n4iTlVdQqXCqUqSx-SEuwB3dEzJ2KnFJycV2jgDvZkcHmz5VL_F_TaCOA0moKECkjmEww1RDVJPrLvOtZtwz9lDiHCXNAdJIfHIFlFYJryZMAq9_J4O0TDTT6CHPQ1iEZMTGV_aTL17E6rov5ubPegbLtCE75KUnYxN-ujnxLY4UKqAGsmsNrz2KVGautm3500_Av-XVzQnZeD0S39eRUyCL31iY2mxHhwb8",
"Nonce": "636690543330175298.Y2I2ODYwNzMtNTJlYi00Y2FiLTgzY2YtYjI0YjBlOWRkMDg0MDUxOGZjOTktNWViZS00MmZjLTg2MjQtMjUwODAwNWU0N2Rl",
"Raw": {
"client_id": "junguoguoimagegalleryclient",
"redirect_uri": "https://localhost:44314/signin-oidc",
"response_type": "code id_token",
"scope": "openid profile",
"response_mode": "form_post",
"nonce": "636690543330175298.Y2I2ODYwNzMtNTJlYi00Y2FiLTgzY2YtYjI0YjBlOWRkMDg0MDUxOGZjOTktNWViZS00MmZjLTg2MjQtMjUwODAwNWU0N2Rl",
"state": "CfDJ8MUXARYmDzBChfPQYArN2SFocvVoTFAx3QMN6tR6pIKi3s3D5eabWiyKUI45I_TYXf3VFhHYOR8v_k97C5vARQJKugB4mGVzinSNi3-Lbn7N0tMsSH1n4iTlVdQqXCqUqSx-SEuwB3dEzJ2KnFJycV2jgDvZkcHmz5VL_F_TaCOA0moKECkjmEww1RDVJPrLvOtZtwz9lDiHCXNAdJIfHIFlFYJryZMAq9_J4O0TDTT6CHPQ1iEZMTGV_aTL17E6rov5ubPegbLtCE75KUnYxN-ujnxLY4UKqAGsmsNrz2KVGautm3500_Av-XVzQnZeD0S39eRUyCL31iY2mxHhwb8",
"x-client-SKU": "ID_NET",
"x-client-ver": "2.1.4.0"
}
}
Login 界面登陆后
IdentityServer4.Endpoints.AuthorizeCallbackEndpoint:Information: Authorize endpoint response
{
"SubjectId": "b7539694-97e7-4dfe-84da-b4256e1ff5c7",
"ClientId": "junguoguoimagegalleryclient",
"RedirectUri": "https://localhost:44314/signin-oidc",
"State": "CfDJ8MUXARYmDzBChfPQYArN2SFocvVoTFAx3QMN6tR6pIKi3s3D5eabWiyKUI45I_TYXf3VFhHYOR8v_k97C5vARQJKugB4mGVzinSNi3-Lbn7N0tMsSH1n4iTlVdQqXCqUqSx-SEuwB3dEzJ2KnFJycV2jgDvZkcHmz5VL_F_TaCOA0moKECkjmEww1RDVJPrLvOtZtwz9lDiHCXNAdJIfHIFlFYJryZMAq9_J4O0TDTT6CHPQ1iEZMTGV_aTL17E6rov5ubPegbLtCE75KUnYxN-ujnxLY4UKqAGsmsNrz2KVGautm3500_Av-XVzQnZeD0S39eRUyCL31iY2mxHhwb8",
"Scope": "openid profile"
}
然后是 token request
IdentityServer4.Validation.TokenRequestValidator:Information: Token request validation success
{
"ClientId": "junguoguoimagegalleryclient",
"ClientName": "Image Gallery",
"GrantType": "authorization_code",
"AuthorizationCode": "7be30468050e4af46c5a806adc6abeba09755766bd1abdd3a01464e03b4fe61d",
"Raw": {
"client_id": "junguoguoimagegalleryclient",
"client_secret": "***REDACTED***",
"code": "7be30468050e4af46c5a806adc6abeba09755766bd1abdd3a01464e03b4fe61d",
"grant_type": "authorization_code",
"redirect_uri": "https://localhost:44314/signin-oidc"
}
}
然后是自定义方法输出的
Identity token:eyJhbGciOiJSUzI1NiIsImtpZCI6ImM5ODExYjU4NDg5Yzk4Y2RlOTNlYWM2NmJmMjVhNzUzIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1MzM0NTc3MjMsImV4cCI6MTUzMzQ1ODAyMywiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzMTkiLCJhdWQiOiJqdW5ndW9ndW9pbWFnZWdhbGxlcnljbGllbnQiLCJub25jZSI6IjYzNjY5MDU0MzMzMDE3NTI5OC5ZMkkyT0RZd056TXROVEpsWWkwMFkyRmlMVGd6WTJZdFlqSTBZakJsT1dSa01EZzBNRFV4T0daak9Ua3ROV1ZpWlMwME1tWmpMVGcyTWpRdE1qVXdPREF3TldVME4yUmwiLCJpYXQiOjE1MzM0NTc3MjMsImF0X2hhc2giOiJ6cUJtYjJIZmtQZk5XcnZxUlU3bVNnIiwic2lkIjoiNzc1YTRkM2E0YWY3OTQ3M2YxMTNmMmM3NjliZmVlYTMiLCJzdWIiOiJiNzUzOTY5NC05N2U3LTRkZmUtODRkYS1iNDI1NmUxZmY1YzciLCJhdXRoX3RpbWUiOjE1MzM0NTc2NzIsImlkcCI6ImxvY2FsIiwiYW1yIjpbInB3ZCJdfQ.F0SVNnvhAiG9MW_BSr8YRql4KLaAGnD6xGFBDubbmmzQBGRc9198_NVQlhOtS-w9aV0daIwZaJZEvwoUS_qV03Jb_3J9VIuJ6NY44BVtnLo600KhYMlsqEoDZmjADLNqWCY4hdE-S31PXlmUNawq8jL6t9kbZRPiaSbKciAp5OHZL6UcZuAdASb-0Ivt3TE5QpwrsQ3AyHjUmYPgN4Ob3sN7wkhrDv9dD11VfZN4mtqOI-t7U3W9omloGsi22KSOBePbNQbYX3d_qcSttNyi-dW1A2TQS6WFNV8zq2ZrH-tvWU67njH-jokt10uKI7FEBAqVQ5MgApKTjahMkAlTxA
Claim type : sid - Claim value : 775a4d3a4af79473f113f2c769bfeea3
Claim type : http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier - Claim value : b7539694-97e7-4dfe-84da-b4256e1ff5c7
Claim type : http://schemas.microsoft.com/identity/claims/identityprovider - Claim value : local
Claim type : http://schemas.microsoft.com/claims/authnmethodsreferences - Claim value : pwd
复制 identity token 到 jwt.io, 可以看到解析后如下所示:
- 添加注销登录的功能
打开 _Layout.cshtml, 在导航栏添加如下代码
@if (User.Identity.IsAuthenticated)
{
注销登录
}
在 Controller 的 Logout
中编写:
public async Task Logout()
{
// 从 Client 端注销登录
await HttpContext.SignOutAsync("Cookies");
// 从 IdentityServer 注销登录
await HttpContext.SignOutAsync("oidc");
}
运行后看日志发现有如下错误
IdentityServer4.Validation.EndSessionRequestValidator:Information: End session request validation failure: Invalid post logout URI
{
"ClientId": "junguoguoimagegalleryclient",
"ClientName": "Image Gallery",
"SubjectId": "b7539694-97e7-4dfe-84da-b4256e1ff5c7",
"Raw": {
"post_logout_redirect_uri": "https://localhost:44314/signout-callback-oidc",
"id_token_hint": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImM5ODExYjU4NDg5Yzk4Y2RlOTNlYWM2NmJmMjVhNzUzIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1MzM0NTg3NDQsImV4cCI6MTUzMzQ1OTA0NCwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzMTkiLCJhdWQiOiJqdW5ndW9ndW9pbWFnZWdhbGxlcnljbGllbnQiLCJub25jZSI6IjYzNjY5MDU1NTM2OTI2MDc1NS5NVGhpTm1SbE5EUXRNbVZrWXkwME0yRmxMV0l5T1RjdE9ETmxOMlUyTVRJMU1qbGxNRGt3WlRNMU5EQXRORE0wWmkwME0yVXdMVGxpTW1NdE1EZzBNamMzT0dKa01EWTUiLCJpYXQiOjE1MzM0NTg3NDQsImF0X2hhc2giOiJDZmxFZXBZQXJiblZsa3NmampteWtRIiwic2lkIjoiZjc1MjBjNzBlZTQxZWZhNWI5NjZkNmRjNWEyODkzMDUiLCJzdWIiOiJiNzUzOTY5NC05N2U3LTRkZmUtODRkYS1iNDI1NmUxZmY1YzciLCJhdXRoX3RpbWUiOjE1MzM0NTg3NDAsImlkcCI6ImxvY2FsIiwiYW1yIjpbInB3ZCJdfQ.n9AbUy1zl90Xg35aSpW-iDPQJVbEXBCDzLZxI7EVk-zdnyqnZnh--CyqIg2SGmypzSCx2H3LhA4KW6SCmlFaZj-BvEmxIpALXpgzxsA55GLf1xWB5OA7H8u94e3mx1231xqh3_GGNLjDdohp8qiuktdVoWuz3M5nNbuiTHzPAjCuwDsS1OTV9CCOX_WFpdct_7RaiOetlzeRY1Msc_MRQ3vnYYGGE4pwW57IhXM7oQAJ7ruLnzAkZ6h3bugY_c7vHKkG4gAmrBxrI0IcniXsY-DkkFQBZRyGaj-AhSNbTet7Nz-vEQOWtgo4OBmoIlcVbhPLTHt1xSuSx5pn4Fdfpg",
"state": "CfDJ8MUXARYmDzBChfPQYArN2SH3JdRYY7x6NjAVdLn5flJFBf5lLyeOw88_tyPtFtS-0oeM80VZDjEtp1ypnpT30J8tUAJdsFONzgPtFOA7bBl3DBKIT83uyss72ZrEteN07tjGRP9YSxhE4_s3lJu83vXJCBT6ElCA055pBg_xRFaN",
"x-client-SKU": "ID_NET",
"x-client-ver": "2.1.4.0"
}
}
- 在 IDP 的
Config
中配置登出的 redirect url, 添加如下代码
PostLogoutRedirectUris = new List()
{
"https://localhost:44314/signout-callback-oidc"
},
Client 端对应配置
这两句也可以不要,因为是默认的配置
再次运行,注销登录会发现如下日志
IdentityServer4.Validation.EndSessionRequestValidator:Information: End session request validation success
{
"ClientId": "junguoguoimagegalleryclient",
"ClientName": "Image Gallery",
"SubjectId": "b7539694-97e7-4dfe-84da-b4256e1ff5c7",
"PostLogOutUri": "https://localhost:44314/signout-callback-oidc",
"State": "CfDJ8MUXARYmDzBChfPQYArN2SHO-kKXV181UqBNd_rBw7RP_frYMPrHFlBWao7R7_IUiOv5l9zbB3PHEHSk4O74Uk9EPD2PeFQ3HMRm5DvHRTylXgMDGUKbEYag7sKpSLLyhBFWUXsYpjCujxr8ZxUvSUeyx8OzvCQzhzFpA5QEnUJ0",
"Raw": {
"post_logout_redirect_uri": "https://localhost:44314/signout-callback-oidc",
"id_token_hint": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImM5ODExYjU4NDg5Yzk4Y2RlOTNlYWM2NmJmMjVhNzUzIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1MzM0NTkxNTQsImV4cCI6MTUzMzQ1OTQ1NCwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzMTkiLCJhdWQiOiJqdW5ndW9ndW9pbWFnZWdhbGxlcnljbGllbnQiLCJub25jZSI6IjYzNjY5MDU1OTIzNjM0Mzc2OS5PVEZpT0RZM1ltVXRNakk0TmkwME1tSTFMV0UzWm1JdE5qWmtORFpsWWpNNU5EY3paRGd5WWpBeFkyTXROV1l4TkMwME9USTFMVGt3TmprdFpEazJOamhoT1dJNE0ySTIiLCJpYXQiOjE1MzM0NTkxNTQsImF0X2hhc2giOiI2Zjdxa0ZtdFg2ZXJVa1pIRTdkTEdnIiwic2lkIjoiYjA5ZTc5Mjc2ZWYzNTE4MWJlNTk2OTkzYWM3YWMzYWQiLCJzdWIiOiJiNzUzOTY5NC05N2U3LTRkZmUtODRkYS1iNDI1NmUxZmY1YzciLCJhdXRoX3RpbWUiOjE1MzM0NTkxNTAsImlkcCI6ImxvY2FsIiwiYW1yIjpbInB3ZCJdfQ.FZOb_EWRZklp9uhiWR-mLaOu0Or41rjztnwIk888t178fgFSvBn0ei_Zg3x1FuDb_0nHUXxwK_kHceCPrbNBGdmKyjpyfKDRtdX7Fnhhm91UGATlrtNG2VQs4srkwkkoj3iQUq05IhIPxBWAS3YR482WrQ3HEqe90b8cWgoN9S2OObO7d-Pjc7CH5fmDEAvkUAfC5HA4MGv3ludSJixK6Vyt3M_HyA-Wnva8m4UOWbMHcWRgZGNbFnwjYWr9oZ7wO03rlLRuZ8bmsl4sZ-oSK_YY_DUwzdxRSS1gZCwA58Xg1vITjK5XQn3nTt_0A7KmMBAjNMTRc81ckM6GvgKHtg",
"state": "CfDJ8MUXARYmDzBChfPQYArN2SHO-kKXV181UqBNd_rBw7RP_frYMPrHFlBWao7R7_IUiOv5l9zbB3PHEHSk4O74Uk9EPD2PeFQ3HMRm5DvHRTylXgMDGUKbEYag7sKpSLLyhBFWUXsYpjCujxr8ZxUvSUeyx8OzvCQzhzFpA5QEnUJ0",
"x-client-SKU": "ID_NET",
"x-client-ver": "2.1.4.0"
}
}
此时注销登录会显示一个是否跳转回 Client 配置的 redirect uri 的页面,如果要实现自动跳转
打开 IDP 项目的 AccountOptions
, 将下面的字段改为 true 即可
public static bool AutomaticRedirectAfterSignOut = true;
- 我们配置了
options.Scope.Add("profile");
但是查看日志并没有返回自定义的那些 Claims
Client 端对应配置增加
从 IDP 的 UserInfoEndpoint 获取数据
可以发现也只有name
相关属性被返回了,至于其他属性信息,下来的课程再讨论