使用Identity Server 4建立Authorization Server (3)

预备知识: 学习Identity Server 4的预备知识

第一部分: 使用Identity Server 4建立Authorization Server (1)

第二部分: 使用Identity Server 4建立Authorization Server (2)

上一部分简单的弄了个web api 并通过Client_Credentials和ResourceOwnerPassword两种方式获取token然后进行api请求.

这次讲一下Authentication 身份验证 (而Authorization是授权, 注意区分), 使用的是OpenIdConnect.

这次我们使用的是一个MVC客户端. 

建立MVC客户端项目

在同一个解决方案建立一个名字叫MvcClient的asp.net core mvc 项目:

不要配置Authentication(身份验证), 应该是没有验证.

修改运行方式为控制台, 端口改为5002, 也就是修改launchSettings.json, 把IISExpress相关的去掉:

{  "profiles": {    "MvcClient": {      "commandName": "Project",      "launchBrowser": true,      "environmentVariables": {        "ASPNETCORE_ENVIRONMENT": "Development"},      "applicationUrl": "http://localhost:5002/"}}
}

在HomeController的About方法上面添加Authorize属性:

        [Authorize]        public IActionResult About(){ViewData["Message"] = "Your application description page.";            return View();}

然后设置解决方案的启动项目为MvcClient和Authorization Server, 解决方案右键属性:

然后运行项目, 在http://localhost:5002 里点击About菜单:

就会出现以下异常 (500):

我们现在要做的就是, 用户点击About之后, 页面重定向到Authorization Server, 用户填写完信息之后登陆到Authorization Server之后再重定向回到该网站(MvcClient).

这也意味着用户是在Authorization Server使用用户名和密码, 而MvcClient不保存用户的用户名和密码.

下面就开始配置

添加OpenId Connect Authentication

在Startup的ConfigureServices里面添加:

public void ConfigureServices(IServiceCollection services){services.AddMvc();            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();services.AddAuthentication(options =>{options.DefaultScheme = "Cookies";options.DefaultChallengeScheme = "oidc";}).AddCookie("Cookies").AddOpenIdConnect("oidc", options =>{options.SignInScheme = "Cookies";options.Authority = "http://localhost:5000";options.RequireHttpsMetadata = false;options.ClientId = "mvc_implicit";options.SaveTokens = true;});}

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); 这句话是指, 我们关闭了JWT的Claim 类型映射, 以便允许well-known claims.

这样做, 就保证它不会修改任何从Authorization Server返回的Claims.

AddAuthentication()方法是像DI注册了该服务.

这里我们使用Cookie作为验证用户的首选方式: DefaultScheme = "Cookies".

而把DefaultChanllangeScheme设为"oidc"是因为, 当用户需要登陆的时候, 将使用的是OpenId Connect Scheme.

然后的AddCookie, 是表示添加了可以处理Cookie的处理器(handler).

最后AddOpenIdConnect是让上面的handler来执行OpenId Connect 协议.

其中的Authority是指信任的Identity Server ( Authorization Server).

ClientId是Client的识别标志. 目前Authorization Server还没有配置这个Client, 一会我们再弄.

Client名字也暗示了我们要使用的是implicit flow, 这个flow主要应用于客户端应用程序, 这里的客户端应用程序主要是指javascript应用程序. implicit flow是很简单的重定向flow, 它允许我们重定向到authorization server, 然后带着id token重定向回来, 这个 id token就是openid connect 用来识别用户是否已经登陆了. 同时也可以获得access token. 很明显, 我们不希望access token出现在那个重定向中. 这个一会再说.

一旦OpenId Connect协议完成, SignInScheme使用Cookie Handler来发布Cookie (中间件告诉我们已经重定向回到MvcClient了, 这时候有token了, 使用Cookie handler来处理).

SaveTokens为true表示要把从Authorization Server的Reponse中返回的token们持久化在cookie中.

注意正式生产环境要使用https, 这里就不用了.

接下来在Startup的Configure方法配置中间件, 以确保每次请求都执行authentication:

public void Configure(IApplicationBuilder app, IHostingEnvironment env){            if (env.IsDevelopment()){app.UseDeveloperExceptionPage();app.UseBrowserLink();}            else{app.UseExceptionHandler("/Home/Error");}            app.UseAuthentication();app.UseStaticFiles();app.UseMvc(routes =>{routes.MapRoute(name: "default",template: "{controller=Home}/{action=Index}/{id?}");});}

注意在管道配置的位置一定要在useMVC之前.

在Authorization Server添加Client

在Authorization Server的InMemoryConfiguration里面添加Client:

public static IEnumerable<Client> Clients(){            return new[]{                new Client{ClientId = "socialnetwork",ClientSecrets = new [] { new Secret("secret".Sha256()) },AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,AllowedScopes = new [] { "socialnetwork" }},                new Client{ClientId = "mvc_implicit",ClientName = "MVC Client",AllowedGrantTypes = GrantTypes.Implicit,RedirectUris = { "http://localhost:5002/signin-oidc" },PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },AllowedScopes = new List<string>{IdentityServerConstants.StandardScopes.OpenId,IdentityServerConstants.StandardScopes.Profile,
               "socialnetwork"}}
};}

ClientId要和MvcClient里面指定的名称一致.

OAuth是使用Scopes来划分Api的, 而OpenId Connect则使用Scopes来限制信息, 例如使用offline access时的Profile信息, 还有用户的其他细节信息. 

这里GrantType要改为Implicit. 使用Implicit flow时, 首先会重定向到Authorization Server, 然后登陆, 然后Identity Server需要知道是否可以重定向回到网站, 如果不指定重定向返回的地址的话, 我们的Session有可能就会被劫持. 

RedirectUris就是登陆成功之后重定向的网址, 这个网址(http://localhost:5002/signin-oidc)在MvcClient里, openid connect中间件使用这个地址就会知道如何处理从authorization server返回的response. 这个地址将会在openid connect 中间件设置合适的cookies, 以确保配置的正确性.

而PostLogoutRedirectUris是登出之后重定向的网址. 有可能发生的情况是, 你登出网站的时候, 会重定向到Authorization Server, 并允许从Authorization Server也进行登出动作.

最后还需要指定OpenId Connect使用的Scopes, 之前我们指定的socialnetwork是一个ApiResource. 而这里我们需要添加的是让我们能使用OpenId Connect的SCopes, 这里就要使用Identity Resources. Identity Server带了几个常量可以用来指定OpenId Connect预包装的Scopes. 上面的AllowedScopes设定的就是我们要用的scopes, 他们包括 openid Connect和用户的profile, 同时也包括我们之前写的api resource: "socialnetwork". 要注意区分, 这里有Api resources, 还有openId connect scopes(用来限定client可以访问哪些信息), 而为了使用这些openid connect scopes, 我们需要设置这些identity resoruces, 这和设置ApiResources差不多:

        public static IEnumerable<IdentityResource> GetIdentityResources(){            return new List<IdentityResource>{                new IdentityResources.OpenId(),                new IdentityResources.Profile(),};}

然后我们需要配置Authorization Server来允许使用这些Identity Resources, Statup的:

public void ConfigureServices(IServiceCollection services){services.AddIdentityServer()                //.AddDeveloperSigningCredential().AddSigningCredential(new X509Certificate2(@"D:\Projects\test\socialnetwork.pfx", "Bx@steel"))                .AddInMemoryIdentityResources(InMemoryConfiguration.GetIdentityResources()).AddTestUsers(InMemoryConfiguration.Users().ToList()).AddInMemoryClients(InMemoryConfiguration.Clients()).AddInMemoryApiResources(InMemoryConfiguration.ApiResources());services.AddMvc();}

设置完了, 运行一下, 点击About菜单就会重定向到Authorization Server:

注意看URL, 我们确实是在Authorization Server.

然后输入用户名密码(TestUser的), 会看见一个请求允许的画面:

可以看到网站请求了Profile信息和User Identity. 

这个时候看上面菜单处, 可以发现用户已经成功登陆了Authorization Server:

所以这就允许我们做SSO(Single Sign-On) 单点登录了. 这时候其他使用这个Authorization Server的Client应用, 由于用户已经登陆到Authorization Server了, 只需要请求用户的许可来访问用户的数据就行了.

然后点击同意 Yes Allow, 就会重定向返回MvcClient网站的About页面:

在View中显示Claims

 打开MvcClient的About.cshtml:

<dl>@foreach (var claim in User.Claims){        <dt>@claim.Type</dt><dd>@claim.Value</dd>}</dl>

显示所有用户的Claims. Claims就是从Authorization Server返回的Payload里面的数据.

运行进入About页面:

嗯当前用户有这些信息....

想要从MvcClient调用WebApi

我们现在想从MvcClient调用WebApi的api/Values节点, 这就需要使用从Authorization Server返回的token. 但是由于我们使用的是implicit flow, 而使用implicit flow, 一切数据都是被发送到Client的. 这就是说, 为了让MvcClient知道用户已经成功登陆了, Authorization Server将会告诉Client(Chrome浏览器)重定向回到MvcClient网站, 并附带着数据. 这意味着token和其他安全信息将会在浏览器里面被传递. 也就是说从Authorization Server发送access token的时候, 如果有人监听的话就会看见这些数据, 不确定使用ssl是否能有效阻止监听到数据(请大神解答). 当然肯定有办法解决这个问题, 例如使用其他flow. 但是有时候还是必须要使用implicit flow 获取到access token. 我们需要做的就是告诉Authorization Server可以使用implicit flow来获取token.

首先我们把token显示出来:

@using Microsoft.AspNetCore.Authentication
<div><strong>id_token</strong><span>@await ViewContext.HttpContext.GetTokenAsync("id_token")</span>
</div>
<div><strong>access_token</strong><span>@await ViewContext.HttpContext.GetTokenAsync("access_token")</span>
</div><dl>@foreach (var claim in User.Claims){        <dt>@claim.Type</dt><dd>@claim.Value</dd>}</dl>

id_token是openid connect指定的, 你需要从authorization server获得它, 用来验证你的身份, 知道你已经登陆了. id_token不是你用来访问api的.

access_token是用来访问api的.

运行一下:

可以看到id_token有了, 而access_token没有, 这是因为我们还没有告诉Authorization Server在使用implicit flow时可以允许返回Access token.

修改Authorization Server的Client来允许返回Access Token


new Client{ClientId = "mvc_implicit",ClientName = "MVC Client",AllowedGrantTypes = GrantTypes.Implicit,RedirectUris = { "http://localhost:5002/signin-oidc" },PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },AllowedScopes = new List<string>{IdentityServerConstants.StandardScopes.OpenId,IdentityServerConstants.StandardScopes.Profile,                        "socialnetwork"},                    AllowAccessTokensViaBrowser = true}


在某种情况下还是不建议这么做.

然后在运行一下:

还是没有access token. 这是因为我们需要重新登陆来获取access token. 我们首先需要登出.

实现Logout 登出

在MvcClient的homeController添加方法:

        public async Task Logout(){            await HttpContext.SignOutAsync("Cookies");            await HttpContext.SignOutAsync("oidc");}

这里需要确保同时登出本地应用(MvcClient)的Cookies和OpenId Connect(去Identity Server清除单点登录的Session).

运行, 在浏览器输入地址: http://localhost:5002/Home/Logout

然后就会跳转到Identity Server的Logout了的页面:

这写到, 点击here可以返回到mvcclient.

点击here, 回到mvcclient, 然后点击About, 重新登陆. 同意, 重定向回来:

还是没有access token.....

看看authorization server的控制台:

有个地方写到返回类型是id_token. 这表示我们要进行的是Authentication.

而我们想要的是既做Authentication又做Authorization. 也就是说我们既要id_token还要token本身.

这么做, 在MvcClient的CongifureServices:


public void ConfigureServices(IServiceCollection services){services.AddMvc();JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();services.AddAuthentication(options =>{options.DefaultScheme = "Cookies";options.DefaultChallengeScheme = "oidc";}).AddCookie("Cookies").AddOpenIdConnect("oidc", options =>{options.SignInScheme = "Cookies";options.Authority = "http://localhost:5000";options.RequireHttpsMetadata = false;options.ClientId = "mvc_implicit";                options.ResponseType = "id_token token";options.SaveTokens = true;});}


然后重新运行, 退出, 重登录:

这次终于看到access_token了....

现在就可以使用access_token访问api了.

原文地址:http://www.cnblogs.com/cgzl/p/7793241.html


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/322995.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php接口跨域问题

报错是因为接口跨域&#xff0c;不允许访问 只需在php头部加入此行代码就行了 header(Access-Control-Allow-Origin:*);

jzoj4231-寻找神格【线段树,数学】

正题 题目大意 4个操作 单点修改&#xff0c;区间修改&#xff0c;区间求和&#xff0c;区间求方差 方差为:∑(xi−ave)2n\frac{\sum(x_i-ave)^2}{n}n∑(xi​−ave)2​ aveaveave为平均值 解题思路 我们将方差的式子分解一下 ∑(xi−ave)2n\frac{\sum(x_i-ave)^2}{n}n∑(xi​…

Hadoop入门(十四)Mapreduce的数据去重程序

1 实例描述 对数据文件中的数据进行去重。数据文件中的每行都是一个数据 样例输入如下所示&#xff1a; 1&#xff09;file1 2012-3-1 a 2012-3-2 b 2012-3-3 c 2012-3-4 d 2012-3-5 a 2012-3-6 b 2012-3-7 c 2012-3-3 c 2&#xff09;file2 2012-3-1 b 2012-3-2 a 2012…

spring boot输出hello world几种方法

1、手动配置&#xff0c;三个文件 打开创建maven,创建这三个文件从上到下依次复制即可 配置文件&#xff08;重要&#xff09;&#xff08;否则后面会报错&#xff09; pom.xml <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w…

欢乐纪中某B组赛【2019.1.20】

前言 有回来做BBB组了&#xff0c;话说第3道题就是AAA组第一道。 成绩 RankRankRank是有算别人的 今天XJQXJQXJQ不在 RankRankRankPersonPersonPersonScoreScoreScoreAAABBBCCC1112017wyc2017wyc2017wyc2702702701001001001001001007070701010102017hjq2017hjq2017hjq13013013…

向ASP.NET Core迁移

我们首先来看看ASP.NET Core有哪些优势&#xff1f; 跨平台&#xff1a;可以部署到Linux服务器上 内置一套对云和部署环境非常友好的配置模块 内置依赖注入 IIS或者Kestrel&#xff08;或者其它自定义&#xff09; 轻量级、高性能、模块化的Http处理管线 .NET Core 是开源…

Hadoop入门(十五)Mapreduce的数据排序程序

"数据排序"是许多实际任务执行时要完成的第一项工作&#xff0c;比如学生成绩评比、数据建立索引等。这个实例和数据去重类似&#xff0c;都是先对原始数据进行初步处理&#xff0c;为进一步的数据操作打好基础 1 实例描述 对输入文件中数据进行排序。输入文件中的…

Java我来了

引言 原本我也是个计算机的小白&#xff0c;从今年的二月份开始学习java&#xff0c;接触eclipse&#xff0c;终于算是开启了我的编程之旅。 在此之前我也不算是零基础&#xff0c;学校在上学期还未开设c语言课程的时候&#xff0c;我就自学了c语言&#xff0c;学的不深&#x…

jzoj4208-线段树什么的最讨厌了【dfs】

正题 题目大意 一个0∼n0\sim n0∼n的线段树包含l∼rl\sim rl∼r的区间&#xff0c;求最小的nnn 解题思路 dfsdfsdfs一下&#xff0c;从下面开始往上扩展区间&#xff0c;知道变成0∼x0\sim x0∼x的格式就就可以去最小值了。 记得剪枝 codecodecode #include<cstdio> …

Hadoop入门(十六)Mapreduce的单表关联程序

"单表关联"要求从给出的数据中寻找所关心的数据&#xff0c;它是对原始数据所包含信息的挖掘 1 实例描述 给出child-parent&#xff08;孩子——父母&#xff09;表&#xff0c;要求输出grandchild-grandparent&#xff08;孙子——祖父母&#xff09;表 样例输入&…

ASP.NET Core集成现有系统认证

我们现在大多数转向ASP.NET Core来使用开发的团队&#xff0c;应该都不是从0开始搭建系统&#xff0c;而是老的业务系统已经在运行&#xff0c;ASP.NET Core用来开发新模块。那么解决用户认证的问题&#xff0c;成为我们的第一个拦路虎。 认证与授权 什么是认证&#xff1f; …

Hadoop入门(十七)Mapreduce的多表关联程序

多表关联和单表关联类似&#xff0c;它也是通过对原始数据进行一定的处理&#xff0c;从其中挖掘出关心的信息 1 实例描述 输入是两个文件&#xff0c;一个代表工厂表&#xff0c;包含工厂名列和地址编号列&#xff1b;另一个代表地址表&#xff0c;包含地址名列和地址编号列…

jzoj4209-已经没有什么好害怕的了【差分】

正题 题目大意 ansians_iansi​表示包含字符iii的括号匹配子串个数 求∑i1n(ansi∗imod&ThinSpace;&ThinSpace;(1e97))\sum_{i1}^n(ans_i*i\mod (1e97))i1∑n​(ansi​∗imod(1e97)) 解题思路 计算出每个匹配括号的前一个括号位置和后一个括号位置。 一个差分数组 先…

使用Identity Server 4建立Authorization Server (4)

预备知识: 学习Identity Server 4的预备知识 第一部分: 使用Identity Server 4建立Authorization Server (1) 第二部分: 使用Identity Server 4建立Authorization Server (2) 第三部分: 使用Identity Server 4建立Authorization Server (3) 上一篇讲了使用OpenId Connect进行Au…

linux操作命令

uname -r 显示正在使用的内核版本、 docker exec -it mytomcat bash 进入tomcat界面 pwd 显示工作路径 ls 查看当前目录中的文件 docker docker run -d -p 8080:8080 tomcat 启动tomcat镜像 docker start id 编辑文件 touch 文件名 创建文件 mkdir dir1 创建一个叫做 ‘dir1…

Hadoop入门(十八)Mapreduce的倒排索引程序

一、简介 "倒排索引"是文档检索系统中最常用的数据结构&#xff0c;被广泛地应用于全文搜索引擎。它主要是用来存储某个单词&#xff08;或词组&#xff09;在一个文档或一组文档中的存储位置的映射&#xff0c;即提供了一种根据内容来查找文档的方式。由于不是根据…

.NET Core跨平台的奥秘[中篇]:复用之殇

在《.NET Core跨平台的奥秘[上篇]&#xff1a;历史的枷锁》中我们谈到&#xff1a;由于.NET是建立在CLI这一标准的规范之上&#xff0c;所以它天生就具有了“跨平台”的基因。在微软发布了第一个针对桌面和服务器平台的.NET Framework之后&#xff0c;它开始 “乐此不疲” 地对…

jozj4010-我才不是萝莉控呢【哈夫曼树】

正题 题目大意 从(n,1)(n,1)(n,1)到(1,1)(1,1)(1,1)&#xff0c;一个数组AAA&#xff0c;满足Ai≥Ai1A_i\geq A_i1Ai​≥Ai​1 每次有两个选择走到(x−1,y1)(x-1,y1)(x−1,y1)&#xff0c;或(x,⌊y/2⌋)(x,\lfloor y/2\rfloor)(x,⌊y/2⌋)。后者需要消耗∑ixnAi\sum_{ix}^nA_i…

java.sql.SQLException: The server time zone value '�й���׼ʱ��' is unrecognized

在application.yml中 spring:datasource:username: rootpassword:url: jdbc:mysql://127.0.0.1:3306/testdriver-class-name: com.mysql.cj.jdbc.Driver结果报错 java.sql.SQLException: The server time zone value ‘&#xfffd;й&#xfffd;&#xfffd;&#xfffd;׼ʱ…

Hadoop入门(十一)Mapreduce的InputFomrat各种子类

一、TextInputFormat extends FileInputFomrat<LongWritable,Text> 是默认读取文件的切分器&#xff0c;其内的LineRecordReader:用来读取每一行的内容&#xff0c; LineRecordReader:内的 nextKeyValue(){}中&#xff0c;key的赋值在&#xff1a; initialize()方法内&…