[原创] IdentityServer4权限控制---使用 ASP.NET Core 的交互式应用程序(四)
写了半天,不小心一关浏览器,没了!我也是醉了。。。又重新写一遍吧!
前面三节课我们学习了用一个客户端先去申请令牌,得到令牌后再去访问API资源这样一个简单的流程,也是一个很常见的功能,通过前三节课的学习,我们搭建了一台API资源服务器,一台IDS4SERVER身份认证服务器,这节课我们接着上面的内容继续学习一下交互式登录的流程。
开始之前,我先交待一下今天的学习任务吧!今天我们要用前面的IDS4SERVER服务器为我们新建的一个站点完成身份验证的过程,整个验证过程中有登录交互,有登录成功后的跳转,有登出功能,同样,客户拿到令牌后,对我们的网站拥有了“访问受保护资源的功能”。本文的标题后半部分是根据官方的文档直译的,想看原文的在这里,为了实现实验目的,我们需要做的工作有,创建一个MYMVC的新站点,该站点有首页、WHOAMI、LOGOUT等页面与功能,其中首页和隐私说明页是模板自带的,可以匿名浏览,而WHOAMI是登录后才可访问的,LOGOUT可以现在登出功能。除此之外,我们要做的工作还有搭建一台IDS4SERVER服务器,为站点提供身份验证功能。OK,我们开始吧!
我们需要先搭建一台IDS4SERVER服务器,不会的朋友们请参考这里:
[原创] IdentityServer4权限控制---客户端授权模式之IDS4认证服务器搭建(二)
懒一些的朋友也可以直接这里下载: IdentityServer4 ,注意,因为我们要增加一些交互功能,而第二节课中的IDS4SERVER服务器并没有交互页面,所以我们还需要增加一个控制器和VIEW视图界面,如果你是用我刚刚提到的链接搭建的服务器,则把这些代码下下来放到项目中去,以给服务器添加控制器与视图交互功能,如果用的是官方的,则跳过这一步。上面链接提供的代码都是官方的DEMO,我只是改了一下命名空间名字。对了,代码放进去以后还要把控制器路由打开才行,不然代码无法访问,打开PROGRAM.CS文件,加入代码
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}"); 这样控制器就可以使用了,我们试着运行一下,并在浏览器中访问一下试试。
OK,说明我们的代码可以正常运行了。
紧接着我们再新建一个MYMVC站点。
新建一个MYMVC的站点
指定存放目录
然后选择.NET6.0,HTTPS支持,其它默认,一路生成下来,我们的新站点就OK了,我们为了让站点支持 OpenID Connect authentication协议,我们在PM命令行中运行命令,引入包
dotnet add package Microsoft.AspNetCore.Authentication.OpenIdConnect打开新站点MYMVC的program.cs加入以下代码:
using System.IdentityModel.Tokens.Jwt; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(); JwtSecurityTokenHandler.DefaultMapInboundClaims = false; builder.Services.AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies") .AddOpenIdConnect("oidc", options => { options.Authority = "https://localhost:5001"; options.ClientId = "mvc"; options.ClientSecret = "secret"; options.ResponseType = "code"; options.SaveTokens = true; });
AddAuthentication
将认证服务注入到依赖中去."Cookies"
为默认 DefaultScheme
, DefaultChallengeScheme
登录质询方式改为oidc
指定 OpenID Connect 协议.然后用 AddCookie
添加 cookies处理程序, AddOpenIdConnect
配置 OpenID Connect 协议执行. Authority
受信 token 地址。 ClientId
和 ClientSecret
. 用来标识客户。SaveTokens
用于将来自 IdentityServer 的令牌保存在 cookie 中(因为稍后将需要它们)。再添加以下代码,
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
让以上设置生效。接下来,我们再改造一下我们的新站点,给它增加一些功能。打开HOMECONTROLLER,添加方法:
public IActionResult Logout() { return SignOut("Cookies", "oidc"); } [Authorize] public IActionResult Whoami() { return View(); }WHOAMI VIEW页面
@using Microsoft.AspNetCore.Authentication <h2>Claims</h2> <dl> @foreach (var claim in User.Claims) { <dt>@claim.Type</dt> <dd>@claim.Value</dd> } </dl> <h2>Properties</h2> <dl> @foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items) { <dt>@prop.Key</dt> <dd>@prop.Value</dd> } </dl>再改造一下模块布局页,加上相应的链接,运行一下,如下图
这时候我们不开身份认证服务器,HOME\PRIVACY均可正常访问,当我们点击WHOAMI页面时报以下错误:
An unhandled exception occurred while processing the request.
Microsoft.IdentityModel.Protocols.ConfigurationManager<T>.GetConfigurationAsync(CancellationToken cancel)
接下来我们对IDS4SERVER服务器做一些配置
添加对 OpenID Connect 身份 scopes(范围)的支持
与 OAuth 2.0 类似,OpenID Connect 也使用 scopes(范围)概念。 同样, scopes范围代表您想要保护并且客户想要访问的东西。 与 OAuth 相比,OIDC 中的范围不代表 API,而是代表用户 ID、姓名或电子邮件地址等身份数据。
通过修改 Config.cs 中的 IdentityResources 属性,添加对标准 openid(subject id)和配置文件(first name, last name 等)范围的支持:
打开config.cs (无语的输入法...)
public static IEnumerable<IdentityResource> IdentityResources => new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile(), };然后打开PROGRAM.CS把上面的代码在 IdentityServer 身份资源中注册
builder.Services.AddIdentityServer() .AddInMemoryIdentityResources(Config.IdentityResources) .AddInMemoryApiScopes(Config.ApiScopes) .AddInMemoryClients(Config.Clients) .AddTestUsers(TestUsers.Users) .AddDeveloperSigningCredential();
注意,官网在这里漏写了最下面一条,如果是测试用,会报错,用我上面的代码。标准scopes \claims 的申明,大家可以参考这个链接 :All standard scopes and their corresponding claims can be found in the OpenID Connect specification
添加测试用户:
官方说:示例 UI 带有一个内存中的“用户数据库”。 您可以通过添加 AddTestUsers 扩展方法在 IdentityServer 中启用此功能:AddTestUsers(TestUsers.Users)
我们打开TestUsers.Users
可以看到有alice\bob两个用户。注意一下他们的一些NAME/FAMILYNAME等字段信息,后面会用到。
将客户端标识注册IdentityServer 服务中:
最后一步是将 MVC 客户端的新配置条目添加到 IdentityServer。
基于 OpenID Connect 的客户端与我们目前添加的 OAuth 2.0 客户端非常相似。 但由于 OIDC 中的流程始终是交互式的,因此我们需要在配置中添加一些重定向 URL。
public static IEnumerable<Client> Clients => new List<Client> { // machine to machine client (from quickstart 1) new Client { ClientId = "client", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials, // scopes that client has access to AllowedScopes = { "api1" } }, // interactive ASP.NET Core MVC client new Client { ClientId = "mvc", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Code, // where to redirect to after login RedirectUris = { "https://localhost:5002/signin-oidc" }, // where to redirect to after logout PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" }, AllowedScopes = new List<string> { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile } } };OK,大功告成!不出意外的话,我们的服务器已经搭建好了。我们把它运行起来,然后把我们新建的MYMVC站点也运行起来,再去点击WHOAMI页面试试,发现被导航到了认证服务器登录页面,注意看后面的端口后5002变成5001了,这是两个不同的站点。
我们用ALICE:ALICE(小写,该死的输入法!)登录,成功以后又返回到我们的新站点,注意看前后端口号的变化
这时候还要注意一个细节,我让大家在前面注意的那些NAME之类的字段并没有成功返回。我们打开MYMVC的PROGRAM.CS,改动代码如下:
.AddOpenIdConnect("oidc", options => //AddOpenIdConnect is used to configure the handler that performs the OpenID Connect protocol.
{
options.Authority = "https://localhost:5001"; //The Authority indicates where the trusted token service is located.
options.ClientId = "mvc"; //We then identify this client via the ClientId and the ClientSecret
options.ClientSecret = "secret";
options.ResponseType = "code";
options.SaveTokens = true; //SaveTokens is used to persist the tokens from IdentityServer in the cookie (as they will be needed later).
// ... 让服务器返回profile identity scop,如名称,网站,家庭等
options.Scope.Add("profile");
options.GetClaimsFromUserInfoEndpoint = true;
// ...
});
然后登出重新请求试试,字段成功返回了。大家自己测试,我就不配图了。
写一个控制器Alice,只允许Alice一个人访问,其他用户拒绝,该如何实现呢?方法:注册一个安全策略,
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("Alice", policy => policy.RequireClaim("name", "Alice Smith"));
});
var app = builder.Build();
控制器
[Authorize(Policy = "Alice")]
public IActionResult Alice()
{
return View();
}
进一步的实验
随意向测试用户添加更多声明 - 以及更多身份资源。
定义身份资源的过程如下:
将新的身份资源添加到列表中 - 为其命名并指定在请求此资源时应返回哪些声明
通过客户端配置上的 AllowedScopes 属性授予客户端对资源的访问权限
通过将资源添加到客户端中 OpenID Connect 处理程序配置上的 Scopes 集合来请求资源
(可选)如果身份资源与非标准声明(例如 myclaim1)相关联,则在客户端添加出现在 JSON 中的声明(从 UserInfo 端点返回)和用户声明之间的 ClaimAction 映射
使用 Microsoft.AspNetCore.Authentication
// ...
.AddOpenIdConnect("oidc", options=>
{
// ...
options.ClaimActions.MapUniqueJsonKey("myclaim1", "myclaim1");
// ...
});
还值得注意的是,令牌声明的检索是一个可扩展点 - IProfileService。由于我们使用的是 AddTestUsers,因此默认使用 TestUserProfileService。您可以在此处检查源代码以了解其工作原理。
原创文章,创作不易,转载请注明出处:https://www.qhwins.com/article-30