本文,主要用来记录IdentityServer4的简单使用。

一. IdentityServer的预备知识

要学习IdentityServer,需要了解下基于Token的验证体系,其中涉及到Token, OAuth&OpenID,JWT,协议规范等。

如图过程,

二.  IdentityServer简单介绍

IdentityServer4 是一个基于OpenID Connect和OAuth 2.0的针对ASP.NET Core 2.0的框架,以中间件的形式存在。

通常你可以构建(或重新使用)包含登录和注销页面的应用程序,IdentityServer中间件会向其添加必要的协议头,以便客户端应用程序可以使用这些标准协议与其对话。

我们可以用IdentityServer来做什么?

  1. 身份验证服务:官方认证的OpenID Connect实现
  2. 单点登录/注销(SSO)
  3. 访问受控的API : 为不同的客户提供访问API的令牌,比如:MVC网站、SPA、Mobile APP等
  4. ...等等

三.简单项目示例

先列出目录结构,以及创建顺序,来方便阅读

IdentityServerDemo --> APIService1和APIService2 --> MVCClient

其中,处MVCClient是asp.net core web mvc项目外,其他都是asp.net core web api 项目

创建名为IdentityServerDemo的认证服务

1. 创建一个asp.net core web api项目:IdentityServerDemo。

注意,不要设置HTTPS,否则后面使用postman测试时,会no response

2. 添加InMemoryConfiguration

public class InMemoryConfiguration
{
public static IConfiguration Configuration { get; set; }
/// <summary>
/// Define which APIs will use this IdentityServer
/// </summary>
/// <returns></returns>
public static IEnumerable<ApiResource> GetApiResources()
{
return new[]
{
new ApiResource("clientservice", "CAS Client Service"),
new ApiResource("productservice", "CAS Product Service"),
new ApiResource("agentservice", "CAS Agent Service")
};
} /// <summary>
/// Define which Apps will use thie IdentityServer
/// </summary>
/// <returns></returns>
public static IEnumerable<Client> GetClients()
{
return new[]
{
new Client
{
ClientId = "client.api.service",
ClientSecrets = new [] { new Secret("clientsecret".Sha256()) },
AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
AllowedScopes = new [] { "clientservice" }
},
new Client
{
ClientId = "product.api.service",
ClientSecrets = new [] { new Secret("productsecret".Sha256()) },
AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
AllowedScopes = new [] { "clientservice", "productservice" }
},
new Client
{
ClientId = "agent.api.service",
ClientSecrets = new [] { new Secret("agentsecret".Sha256()) },
AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
AllowedScopes = new [] { "agentservice", "clientservice", "productservice" }
}
};
} /// <summary>
/// Define which uses will use this IdentityServer
/// </summary>
/// <returns></returns>
public static IEnumerable<TestUser> GetUsers()
{
return new[]
{
new TestUser
{
SubjectId = "",
Username = "test1@hotmail.com",
Password = "test1password"
},
new TestUser
{
SubjectId = "",
Username = "test2@hotmail.com",
Password = "test2password"
},
new TestUser
{
SubjectId = "",
Username = "test3@hotmail.com",
Password = "test3password"
}
};
}
}

3. 使用nuget管理器,添加IdentityServer4 ,并且修改StartUp.cs

修改StartUp.cs中的Configure方法

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
//启用IdentityServer
app.UseIdentityServer();
app.UseMvc();
}

修改StartUp.cs中的ConfigureServices方法

public void ConfigureServices(IServiceCollection services)
{
//添加IdentityServer
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddTestUsers(InMemoryConfiguration.GetUsers().ToList())
.AddInMemoryClients(InMemoryConfiguration.GetClients())
.AddInMemoryApiResources(InMemoryConfiguration.GetApiResources()); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

这个主要是为了把IdentityServer注册到容器中,需要对其进行配置,而这个配置主要包含三个信息:

  1. 哪些api可以使用这个AuthorizationServer
  2. 哪些client可以使用这个AuthorizationServer
  3. 哪些User可以被这个AuthorizationServer识别并授权

这里的AuthorizationServer 指的就是这个项目的服务:用来认证及授权使用的.

这里是使用基于内存的方式。

对于Token签名需要一对公钥和私钥,IdentityServer为开发者提供了一个AddDeveloperSigningCredential()方法,它会帮我们搞定这个事情并且存储到硬盘。当切换到正式环境,需要使用真正的证书,更换为

public void ConfigureServices(IServiceCollection services)
{
InMemoryConfiguration.Configuration = this.Configuration; services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddTestUsers(InMemoryConfiguration.GetUsers().ToList())
.AddInMemoryClients(InMemoryConfiguration.GetClients())
.AddInMemoryApiResources(InMemoryConfiguration.GetApiResources());
}

此项目,暂时不使用正式的证书了。

4.使用postman获取token

启动我们的IdentityServerDemo 项目,

然后使用postman发送请求

5.引入QuickStartUI

IdentityServer为我们提供了一套UI以使我们能快速的开发具有基本功能的认证/授权界面,下载地址:QuickStartUI

把QuickStartUI引入到我们的项目中,目录结构如下:

5.修改StartUp.cs

修改Configure方法

添加静态文件中间件

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
//启用IdentityServer
app.UseIdentityServer();
//for QuickStart-UI 启用静态文件
app.UseStaticFiles();
//app.UseMvc();
app.UseMvcWithDefaultRoute(); //这里带有默认的路由
}

6.运行程序

登录

点击here

登出

IdentityServer集成API Service

1.  添加asp.net core web api项目

注意,这里也是使用http方式;

2.在nuget中安装IdentityServer4.AccessTokenValidation

3.修改StartUp.cs文件

修改configureServices方法

public void ConfigureServices(IServiceCollection services)
{
//IdentityServer
services.AddMvcCore().AddAuthorization().AddJsonFormatters();
services.AddAuthentication(Configuration["Identity:Scheme"])
.AddIdentityServerAuthentication(options =>
{
options.RequireHttpsMetadata = false; //是否需要https
options.Authority = $"http://{Configuration["Identity:IP"]}:{Configuration["Identity:Port"]}"; //IdentityServer授权路径
options.ApiName = Configuration["Service:Name"]; //需要授权的服务名称
}); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

修改Configure方法

在UseMvc()之前启用Authentication中间件

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} //启用Authentication中间件
app.UseAuthentication(); app.UseMvc();
}

修改appsettings.json文件

{
"Service": {
"Name": "clientservice", //本服务的名称
"Port": "",  //本服务的端口号,根据自己服务启动时的端口号进行更改
"DocName": "clientservice",
"Version": "v1",
"Title": "CAS Client Service API",
"Description": "CAS Client Service API provide some API to help you get client information from CAS",
"Contact": {
"Name": "CAS 2.0 Team",
"Email": "EdisonZhou@manulife.com"
},
"XmlFile": "Manulife.DNC.MSAD.IdentityServer4Test.ApiService01.xml"
},
"Identity": { //去请求授权的Identity服务,这里即IdentityServerDemo的服务启动时的地址
"IP": "localhost",
"Port": "", //IdentityServerDemo项目启动时的端口号,根据实际情况修改
"Scheme": "Bearer"
}
}

上面是APIService1的添加,对应的服务名称是clientservice;

APIService2与之类似,只是把appsettings.json中的clientservice改为productservice.

4. 在APIService1和APIService2的Controller添加[Authorize]特性

 [Authorize]
[Route("api/[controller]")]
public class ValuesController : Controller
{
......
}

5. 测试

注意,这里模拟的是clientservice服务(即APIService1)去认证服务器请求token的过程,所以请求到token,也应该在获取clientservice相关授权的时候携带这个token.

如果在请求productservice的授权服务中,使用clientservice的token则会显示未授权

过程总结:

  1. 首先,在授权服务中,设置需要请求的ApiResource,client,user
  2. 在postman(相当于client)中,输入client的相关信息(client_id,client_serect)去请求token
  3. 然后就可以根据授权服务中相应client的AllowedScopes设置的范围来请求服务了。

授权服务中的client设置

IdentityServer集成MVC Web Application

1. 新建一个ASP.NET Core MVC项目:MVCClient

2.为指定方法添加[Authorize]特性

我们为HomeController下的Privacy方法上添加Authorize特性

     [Authorize]
public IActionResult Privacy()
{
return View();
}

这个时候,直接访问Privacy,会报错

而我们希望的效果是:当用户第一次点击Privacy,页面重定向到验证服务(IdentityServerDemo),当用户登录验证授权后,再重定向到该网站。

此后一定时间范围内的第二次,第三次点击,都不需要再重定向到验证服务,而是直接读取保存的token.

3.  给MVCClient项目添加OpenID Connect Authentication

而这部分主要集中于做Authentication(身份验证)而非Authorization(授权)

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().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); //这部分主要是做身份验证的(Authentication),而不是授权(Authorization)
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc"; //oidc => open id connect
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = $"http://{Configuration["Identity:IP"]}:{Configuration["Identity:Port"]}";
options.RequireHttpsMetadata = false;
options.ClientId = "cas.mvc.client.implicit";
options.ResponseType = "id_token token"; //允许返回access token
options.SaveTokens = true;
}); }

这里我们使用的是implicit这个flow,它主要用于客户端应用程序(主要指基于javascript的应用),它允许客户端程序重定向到验证服务(IdentityServerDemo),而后带着token重定向回来。

另外,这里的ResponseType为”id_token token”,表示既获取id_token也获取access_token. 而SaveTokens设置为true,表示会将从验证服务返回的token持久化到cookie中,这样就不用每次请求token了。

另在configure方法中,设置Authentication中间件:

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

主要Authentication中间件,要再UseMvc之前。

4. 修改app.settings

{
"Service": {
"Name": "cas.mvc.client.implicit", //本服务的名称
"Port": "", //服务端口号,根据实际情况调整
"DocName": "cas.mvc.client.implicit",
"Version": "v1",
"Title": "CAS Client Service API",
"Description": "CAS Client Service API provide some API to help you get client information from CAS",
"Contact": {
"Name": "CAS 2.0 Team",
"Email": "EdisonZhou@manulife.com"
},
"XmlFile": "Manulife.DNC.MSAD.IdentityServer4Test.ApiService01.xml"
},
"Identity": { //去请求授权的Identity服务
"IP": "localhost",
"Port": ""
}
}

其中port根据自己此服务启动后的端口号修改

5.在验证服务(IdentityServerDemo)中添加MvcClient

修改 InMemoryConfiguration 中的GetClients方法:

public static IEnumerable<Client> GetClients()
{
return new[]
{
new Client
{
ClientId = "client.api.service",
ClientSecrets = new [] { new Secret("clientsecret".Sha256()) },
AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
AllowedScopes = new [] { "clientservice" }
},
new Client
{
ClientId = "product.api.service",
ClientSecrets = new [] { new Secret("productsecret".Sha256()) },
AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
AllowedScopes = new [] { "clientservice", "productservice" }
},
new Client
{
ClientId = "agent.api.service",
ClientSecrets = new [] { new Secret("agentsecret".Sha256()) },
AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
AllowedScopes = new [] { "agentservice", "clientservice", "productservice" }
},
new Client
{
ClientId = "cas.mvc.client.implicit",
ClientName = "CAS MVC Web App Client",
AllowedGrantTypes = GrantTypes.Implicit,
RedirectUris = { $"http://localhost:56458/signin-oidc" },
PostLogoutRedirectUris = { $"http://localhost:56458/signout-callback-oidc" },
AllowedScopes = new [] {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"agentservice", "clientservice", "productservice"
},
AllowAccessTokensViaBrowser = true // can return access_token to this client
},
};
}

这里ClientId要和MvcClient中设置的一样。

RedirectUris是指登录成功以后需要重定向的地址(即重定向到MvcClient中的地址),

而PostLogoutRedirectUris是指登出之后需要重定向的地址。

和API Service Client的设置不同的就是AllowedScopes中给它增加了OpenId和Profile,因为我们为MvcClient设定的是oidc而不是bearer模式。

最后为了使用这些OpenID Connect Scopes,需要设置这些Identity Resources。

在 InMemoryConfiguration 中增加GetIdentityResources方法:

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

在ConfigureServices方法中修改:

 public void ConfigureServices(IServiceCollection services)
{
//添加IdentityServer
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryIdentityResources(InMemoryConfiguration.GetIdentityResources())
.AddTestUsers(InMemoryConfiguration.GetUsers().ToList())
.AddInMemoryClients(InMemoryConfiguration.GetClients())
.AddInMemoryApiResources(InMemoryConfiguration.GetApiResources()); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

6. 在MvcClient项目的Privacy 页面中修改如下:

@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1> <p>Use this page to detail your site's privacy policy.</p> @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和access_token显示出来

7. 为了退出方便,暂时在HomeController下增加Logout方法

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

8. 简单测试

启动IdentityServerDemo这个验证服务;

启动MvcClient这个Mvc Web Application服务;

这里没有添加可点击的按钮,可直接在url中修改路径来登出

参考网址:

https://www.cnblogs.com/edisonchou/p/identityserver4_foundation_and_quickstart_01.html

另外推荐edisonchou微服务系列,感觉非常棒

https://github.com/Vincent-yuan/IdentityServerDemo

IdentityServer4学习及简单使用的更多相关文章

  1. JMeter学习工具简单介绍

    JMeter学习工具简单介绍   一.JMeter 介绍 Apache JMeter是100%纯JAVA桌面应用程序,被设计为用于测试客户端/服务端结构的软件(例如web应用程序).它可以用来测试静态 ...

  2. (java)selenium webdriver学习---实现简单的翻页,将页面内容的标题和标题链接取出

    selenium webdriver学习---实现简单的翻页,将页面内容的标题和标题链接取出: 该情况适合能能循环page=1~n,并且每个网页随着循环可以打开的情况, 注意一定是自己拼接的url可以 ...

  3. IdentityServer4学习记录

    前言 .NetCore 生态伴随着各位大神的推广,好多小伙伴都学习或应用到现有项目中了:  同时,很多相关组件也提上了学习之旅,如IdentitiServer4.Polly.Ocelot.Consul ...

  4. IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试

    IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试 目的不是为了编写TSC2007驱动,是为了学习IIC驱动的编写,读一下TSC2007的ADC数据进行练习,, Linux主机驱动和外设驱 ...

  5. CSS学习------之简单图片切换

    最近一直在重温纯CSS,学习的时候真的才发现,css真的博大精深啊! 所以趁着学习的劲头,谢了个最简单的CSS图片切换! 先整理下思路: 首先我希望图片居中间,两边有个切换按钮,点击按钮的时候,可以实 ...

  6. CMake学习(1)---简单程序与库

    cmake是linux平台下重要的工具,可以方便的组织makefile.之前一直在windows平台下进行软件开发,在vs2010的IDE里,只要一点run程序就能跑出结果.但是程序的编译并没有那么简 ...

  7. Ajax学习(1)-简单ajax案例

    1.什么是Ajax? Ajax是Asynchronous JavaScript and XML 的缩写,即异步的Javascript和XML. 可以使用Ajax在不加载整个网页的情况下更新部分网页信息 ...

  8. [Struts2学习笔记] -- 简单的类型转换

    接下来学习一下Struts2简单的类型转换,Struts2基于ognl.jar实现了简单类型的数据转换.比如jsp页面中的form值与字段值的转换,下面写一个例子. 1.创建一个jsp页面,编写一个f ...

  9. JavaScript学习笔记——简单无缝循环滚动展示图片的实现

    今天做了一个简单的无缝循环滚动的实例,这种实例在网页中其实还挺常见的,下面分享一下我的学习收获. 首先,无缝滚动的第一个重点就是——动.关于怎么让页面的元素节点动起来,这就得学明白关于JavaScri ...

随机推荐

  1. hadoop hdfs 有内网、公网ip后,本地调试访问不了集群解决

    问题背景: 使用云上的虚拟环境搭建测试集群,导入一些数据,在本地idea做些debug调试,但是发现本地idea连接不上测试环境 集群内部配置hosts映射是内网映射(内网ip与主机名映射),本地只能 ...

  2. springboot 登录实现源代码解析

    springboot 可以使用springsecurity 作为安全框架,我们可以使用springsecurity 实现安全登录. springsecurity 是使用一系列的过滤器来实现安全的. 实 ...

  3. yum无法下载,网关问题

    由于网关地址改变没有及时更新配置,造成无法下载 failure: repodata/repomd.xml from base: [Errno 256] No more mirrors to try h ...

  4. 使用docker搭建redis-cluster环境

    目录 基础环境信息 搭建步骤 搭建中遇到的问题 其他参考     临时接到一个测试任务,而测试需要用到redis-cluster环境,却没有现成的环境可用,于是只能自力更生搭建测试环境.一开始想采用在 ...

  5. 动态加载swiper,默认显示最后一个swiper-slide解决方案???

    问题描述: 用ajax动态加载swiper-slide以后,由于我是自适应屏幕的尺寸来决定一屏显示多少图片,所以加了 slidesPerView:'auto'这条属性,加了这条属性过后,每次刷新页面的 ...

  6. pypi 打包分发

    打包Python项目 本教程将指导您如何打包一个简单的Python项目.它将向您展示如何添加必要的文件和结构来创建包,如何构建包以及如何将其上载到Python包索引. 一个简单的项目 本教程使用一个名 ...

  7. flask实战-个人博客-模板 --

    模板 personalBlog采用典型的博客布局,左侧三分之二为主体,显示文章列表.正文:右侧三分之一为边栏,显示分为类列表.社交链接等.现在的工作是将HTML文件加工为模板,并创建对应的表单类,在模 ...

  8. eclipse IDE for java developers下载与安装

    1.进入eclipse官网下载页面 https://www.eclipse.org/downloads/ 2.点击Download Packages 3.windows 用户 选择 64 bits 4 ...

  9. 微信小程序 vscode 自动保存 保存自动编译 微信开发者工具崩溃

    修改vscode的自动保存延迟时间,将 auto save delay 选项 修改的长一点.

  10. 第05组 团队Git现场编程实战

    一.组员职责分工 组员 分工 卢欢(组长) 前后端接口设计 严喜 寻找相关资料 张火标 设计并描述界面原型 钟璐英 编写随笔 周华 填写完善文档 古力亚尔·艾山 填写完善文档 张聪 前后端接口设计 池 ...