原文:从Client应用场景介绍IdentityServer4(四)

上节以对话形式,大概说了几种客户端授权模式的原理,这节重点介绍Hybrid模式在MVC下的使用。且为实现IdentityServer4从数据库获取User进行验证,并对Claim进行权限设置打下基础(第五节介绍)。

本节内容比较多,且涉及一、二节的内容,如有不懂,可先熟悉一、二节知识。


一、新建授权服务,命名为AuthServer

(1)新建Web API项目,不用配置HTTPS,不进行身份验证。

设置成控制台方式运行,端口设为5000。

安装IdentityServer4

在Config.cs类中,添加如下代码:

public class Config
{ public static List<TestUser> GetUsers()
{
return new List<TestUser>
{
new TestUser
{
SubjectId = "1",
Username = "test",
Password = "123", Claims = new List<Claim>
{
new Claim("role", "user")
}
},
new TestUser
{
SubjectId = "2",
Username = "admin",
Password = "123", Claims = new List<Claim>
{
new Claim("role", "admin")
}
}
};
} public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
//new IdentityResource("roles","role",new List<string>{ "role"})
};
} public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("api1", "My API")
//new ApiResource("api1", "My API",new List<string>(){ "role"})
};
} // clients want to access resources (aka scopes)
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "AuthServer",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = { "api1" },
Claims= new List<Claim>(){new Claim("role","AuthServer") },
ClientClaimsPrefix = ""
},
// OpenID Connect implicit flow client (MVC)
new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.Hybrid,
ClientSecrets =
{
new Secret("secret".Sha256())
},
// where to redirect to after login
RedirectUris = { "http://localhost:5002/signin-oidc" }, // where to redirect to after logout
PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" }, AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
//"roles"
}
}
};
}
}

这里IdentityResource映射于那些关于用户信息的scope, ApiResource映射于API资源的scopes。

(2)打开Startup.cs,在ConfigureServices里面调用AddIdentityServer来把Identity Server注册到ASP.NET Core的容器里面;随后我调用了AddDeveloperSigningCredentials方法,它会创建一个用于对token签名的临时密钥材料(但是在生产环境中应该使用可持久的密钥材料)

 public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(); services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddTestUsers(Config.GetUsers())
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients());
}

(3)打开Configure方法,把IdentityServer添加到ASP.NET Core的管道里。

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} app.UseIdentityServer();
//MVC配置
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}

(4)然后下载登录用的UI: https://github.com/IdentityServer/IdentityServer4.Quickstart.UI

把图中三个文件复制到AuthServer项目目录下。

复制完后项目如下:


二、新建MVC客户端,命名为MvcClient

(1)设置端口为5002。

修改Start.cs的ConfigureServices方法为:

public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
}); services.AddMvc(); JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies", options =>
{
//无权限,显示的页面
options.AccessDeniedPath = "/Authorization/AccessDenied";
})
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies"; options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false; options.ClientId = "mvc";
options.ResponseType = "code id_token";
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
//options.Scope.Add("roles"); options.SaveTokens = true;
options.ClientSecret = "secret";
options.GetClaimsFromUserInfoEndpoint = true; //options.ClaimActions.MapUniqueJsonKey("role", "role"); //options.TokenValidationParameters = new TokenValidationParameters
//{
// NameClaimType = JwtClaimTypes.GivenName,
// RoleClaimType = JwtClaimTypes.Role
//};
});
}

AddAuthentication方法来添加和配置身份认证中间件。这里使用Cookie作为验证用户的首选方式,而DefaultScheme = "Cookies",这个"Cookies"字符串是可以任意填写的,只要与后边的一致即可。但是如果同一个服务器上有很多应用的话,这个Scheme的名字不能重复。

DefaultChanllangeScheme设为"oidc", 这个名字与后边配置OpenIdConnect的名字要一样. 当用户需要登陆的时候, 将使用的是OpenId Connect Scheme。

AddCookie其参数是之前配置的DefaultScheme名称,这配置了Cookie的处理者,并让应用程序为我们的DefaultScheme启用了基于Cookie的身份认证。一旦ID Token验证成功并且转化为Claims身份标识后,这些信息就将会保存于被加密的Cookie里。

AddOpenIdConnect方法添加了对OpenID Connect流程的支持,它让配置了用来执行OpenId Connect 协议的处理者。这个处理者会负责创建身份认证请求,Token请求和其它请求,并负责ID Token的验证工作。它的身份认证scheme就是之前配置的"oidc",它的意思就是如果该客户端的某部分要求身份认证的时候,OpenID Connect将会作为默认方案被触发(因为之前设置的DefaultChallengeScheme是"oidc", 和这里的名字一样)。

SignInScheme和上面的DefaultScheme一致,它保证身份认证成功的结果将会被保存在方案名为"Cookies"的Cookie里。

Authority就是Identity Provider的地址。

ClientId和Secret要与IdentityProvider里面的值一样。

请求的Scope有openid和profile,其实中间件默认也包括了这些scope,但是写出来更明确一些。

SaveTokens=true,表示允许存储从Identity Provider那里获得的tokens。

(2)修改Configure方法为:

 public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
} app.UseAuthentication(); app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}

(3)然后对HomeController加上身份验证。[Authorize]

(4)再修改About的页面,显示User的Claim信息。

@{
ViewData["Title"] = "About";
}
<h2>@ViewData["Title"]</h2> @*<dt>Access Token</dt>
<dd>@ViewData["AccessToken"]</dd>*@ <dl>
@foreach (var claim in User.Claims)
{
<dt>@claim.Type</dt>
<dd>@claim.Value</dd>
}
</dl>

(5)现在,可以运行AuthServer和MvcClient项目了。

(6)输入Config文件中的TestUser的用户,密码都设为123,点击Login

允许授权

查看About页面,显示了user相关的claim信息。

(7)当然,登出功能还没实现,这里先实现登出。打开图中cshtml文件

添加如下代码:

 @if (User.Identity.IsAuthenticated)
{
<li><a asp-area="" asp-controller="Home" asp-action="Logout">Logout</a></li>
}

然后在HomeController控制器中添加Logout方法

public async Task Logout()

{

await HttpContext.SignOutAsync("Cookies");

await HttpContext.SignOutAsync("oidc");

}

首先要清除本地的Cookie,这个Cookie的名字要与之前配置的默认方案里的名字一致,这一步就相当于登出MVC客户端。

后一行代码的作用是跳转回到Identity Provider,然后用户可以继续登出IDP, 也就是IDP会清除它的Cookie。

(8)接着在AuthServer中的Quickstart/Account/AccountOptions实现自动跳转回登录页面。

好了,登录登出实现完了,我们接着实现Claim权限限制。


三、为MVC客户端设置Claim身份验证

(1)添加TestUser的Claim中Type为role

(2)定义用户信息scope的role信息

第一个参数是scope的名字,第二个参数是scope的显示名,第三个参数是它所包含的claim类型,这里就是“role”。

(3)然后还需要客户端允许请求“roles”这个scope

(4)MVC客户端的配置,打开MVC的startup,添加“roles”这个scope:options.Scope.Add("roles");

把role claim 映射到User.Claims里:options.ClaimActions.MapUniqueJsonKey("role", "role");

role claim映射成ASP.NET Core MVC可以识别的角色Roles。

options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = JwtClaimTypes.GivenName,
RoleClaimType = JwtClaimTypes.Role
};

这样MVC中的role就可以识别User.Claims的role了。

(6)最后在MvcClient项目HomeController中   About前,加上role为admin身份验证。[Authorize(Roles ="admin")]

然后运行,先用test账号登录进行验证。

发现点About页面没有权限进不去

然后登出,换admin账号登录

User.Claims的role成功被MVC中角色role识别,展示About页面。


这节主要介绍Hybrid在MVC下的使用,包括User的登录登出和Claim对MVC的身份授权。

然而,这只是针对内存用户TestUser进行操作的,显示实际项目中不能满足我们需求。下节将在本节的基础上介绍如何实现IdentityServer4从数据库获取User进行验证并对Claim进行身份验证。

参考博客: https://www.cnblogs.com/cgzl/p/9268371.html

源码地址:https://github.com/Bingjian-Zhu/Mvc-HybridFlowV0.git

从Client应用场景介绍IdentityServer4(四)的更多相关文章

  1. 从Client应用场景介绍IdentityServer4(五)

    原文:从Client应用场景介绍IdentityServer4(五) 本节将在第四节基础上介绍如何实现IdentityServer4从数据库获取User进行验证,并对Claim进行权限设置. 一.新建 ...

  2. 从Client应用场景介绍IdentityServer4(一)

    原文:从Client应用场景介绍IdentityServer4(一) 一.背景 IdentityServer4的介绍将不再叙述,百度下可以找到,且官网的快速入门例子也有翻译的版本.这里主要从Clien ...

  3. 从Client应用场景介绍IdentityServer4(二)

    原文:从Client应用场景介绍IdentityServer4(二) 本节介绍Client的ClientCredentials客户端模式,先看下画的草图: 一.在Server上添加动态新增Client ...

  4. 从Client应用场景介绍IdentityServer4(三)

    原文:从Client应用场景介绍IdentityServer4(三) 在学习其他应用场景前,需要了解几个客户端的授权模式.首先了解下本节使用的几个名词 Resource Owner:资源拥有者,文中称 ...

  5. Android bluetooth介绍(四): a2dp connect流程分析

    关键词:蓝牙blueZ  A2DP.SINK.sink_connect.sink_disconnect.sink_suspend.sink_resume.sink_is_connected.sink_ ...

  6. 消息中间件activemq的使用场景介绍(结合springboot的示例)

    一.消息队列概述 消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题.实现高性能,高可用,可伸缩和最终一致性架构.是大型分布式系统不可缺少的中间件. 目前在生产环境,使 ...

  7. Redis 中 5 种数据结构的使用场景介绍

    这篇文章主要介绍了Redis中5种数据结构的使用场景介绍,本文对Redis中的5种数据类型String.Hash.List.Set.Sorted Set做了讲解,需要的朋友可以参考下 一.redis ...

  8. JavaWeb_(Mybatis框架)主配置文件介绍_四

    系列博文: JavaWeb_(Mybatis框架)JDBC操作数据库和Mybatis框架操作数据库区别_一 传送门 JavaWeb_(Mybatis框架)使用Mybatis对表进行增.删.改.查操作_ ...

  9. SharePoint Server 2013开发之旅(一):新的开发平台和典型开发场景介绍

    我终于开始写这个系列文章,实际上确实有一段时间没有动笔了.最近重新安装了一套SharePoint Server 2013的环境,计划利用工作之余的时间为大家写一点新的东西. SharePoint Se ...

随机推荐

  1. 关于android的2.2与4.4的文件读取的一点发现

    好久没有写文章了,本来想写的东西,时间一长,就感觉不想写了.没什么用,到用时.又不知道去哪找了或怎么解决. 有一句话说的好啊,好记性不如烂笔头. 我要做到善于总结.及时整理,额............ ...

  2. matlab 程序发布

    将matlab程序发布为可执行程序包 说明,这种可执行程序包可以在没有安装matlab的计算机上运行. 1. 打开Applicaiton Compler 如果下拉列表中没有这个APPLICATIOND ...

  3. ATL入门

    服务端代码----------------------------------------------------------------------------------------------- ...

  4. C語言 rand函数 进阶探讨与实现

    C语言中随机函数应用        可能大家都知道C语言中的随机函数random,但是random函数并非ANSI C标准,所以说.random函数不能在gcc,vc等编译器下编译通过. 那么怎么实现 ...

  5. js 字符串操作函数有哪些

    js 字符串操作函数有哪些 一.总结 一句话总结:js字符串函数都是字符串对象的方法,是通过调用字符串方法的方式调用,和java,php里面不一样. 1.字符串替换函数怎么用? 这里的正则表示是加双引 ...

  6. gdb常用调试命令以及多线程堆栈的查看

    GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具.或许,各位比较喜欢那种图形界面方式的,像VC.BCB等IDE的调试,但如果你是在UNIX平台下做软件,你会发现GDB这个调试工具有比VC ...

  7. MHA 一主两从搭建-脚本VIP-自动切换

    环境介绍:主机名 IP MHA角色 MySQL角色node1 192.168.56.26 Node MySQL Master node2 192.168.56.27 Node MySQL Master ...

  8. vuejs及相关工具介绍

    轻量级前端mvm的框架 图片.png 对es6语法的简单描述 图片.png 融合了react和angular的优点,组件化和灵活应用和指令,在国际上是一款极有潜力的前端框架. 1.双向绑定 两段相加得 ...

  9. Visual Stdio 环境下使用 GSL (GNU Scientific Library)

    Visual Stdio 环境下使用 GSL (GNU Scientific Library) 经測试.这里的方法不适用于VS2015. * 这篇文章有点过时了.建议从以下网址下载能够在 vs 环境下 ...

  10. Java编程思想第四版 *第五章 个人练习

    练习3:(1)创建一个带默认构造器(即无參构造器)的类.在构造器中打印一条消息.为这个类创建一个对象.P116 public class Test{ public Test(){ System.out ...