IdentityServer4学习及简单使用
本文,主要用来记录IdentityServer4的简单使用。
一. IdentityServer的预备知识
要学习IdentityServer,需要了解下基于Token的验证体系,其中涉及到Token, OAuth&OpenID,JWT,协议规范等。
如图过程,
二. IdentityServer简单介绍
IdentityServer4 是一个基于OpenID Connect和OAuth 2.0的针对ASP.NET Core 2.0的框架,以中间件的形式存在。
通常你可以构建(或重新使用)包含登录和注销页面的应用程序,IdentityServer中间件会向其添加必要的协议头,以便客户端应用程序可以使用这些标准协议与其对话。
我们可以用IdentityServer来做什么?
- 身份验证服务:官方认证的OpenID Connect实现
- 单点登录/注销(SSO)
- 访问受控的API : 为不同的客户提供访问API的令牌,比如:MVC网站、SPA、Mobile APP等
- ...等等
三.简单项目示例
先列出目录结构,以及创建顺序,来方便阅读
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注册到容器中,需要对其进行配置,而这个配置主要包含三个信息:
- 哪些api可以使用这个AuthorizationServer
- 哪些client可以使用这个AuthorizationServer
- 哪些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则会显示未授权
过程总结:
- 首先,在授权服务中,设置需要请求的ApiResource,client,user
- 在postman(相当于client)中,输入client的相关信息(client_id,client_serect)去请求token
- 然后就可以根据授权服务中相应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学习及简单使用的更多相关文章
- JMeter学习工具简单介绍
JMeter学习工具简单介绍 一.JMeter 介绍 Apache JMeter是100%纯JAVA桌面应用程序,被设计为用于测试客户端/服务端结构的软件(例如web应用程序).它可以用来测试静态 ...
- (java)selenium webdriver学习---实现简单的翻页,将页面内容的标题和标题链接取出
selenium webdriver学习---实现简单的翻页,将页面内容的标题和标题链接取出: 该情况适合能能循环page=1~n,并且每个网页随着循环可以打开的情况, 注意一定是自己拼接的url可以 ...
- IdentityServer4学习记录
前言 .NetCore 生态伴随着各位大神的推广,好多小伙伴都学习或应用到现有项目中了: 同时,很多相关组件也提上了学习之旅,如IdentitiServer4.Polly.Ocelot.Consul ...
- IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试
IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试 目的不是为了编写TSC2007驱动,是为了学习IIC驱动的编写,读一下TSC2007的ADC数据进行练习,, Linux主机驱动和外设驱 ...
- CSS学习------之简单图片切换
最近一直在重温纯CSS,学习的时候真的才发现,css真的博大精深啊! 所以趁着学习的劲头,谢了个最简单的CSS图片切换! 先整理下思路: 首先我希望图片居中间,两边有个切换按钮,点击按钮的时候,可以实 ...
- CMake学习(1)---简单程序与库
cmake是linux平台下重要的工具,可以方便的组织makefile.之前一直在windows平台下进行软件开发,在vs2010的IDE里,只要一点run程序就能跑出结果.但是程序的编译并没有那么简 ...
- Ajax学习(1)-简单ajax案例
1.什么是Ajax? Ajax是Asynchronous JavaScript and XML 的缩写,即异步的Javascript和XML. 可以使用Ajax在不加载整个网页的情况下更新部分网页信息 ...
- [Struts2学习笔记] -- 简单的类型转换
接下来学习一下Struts2简单的类型转换,Struts2基于ognl.jar实现了简单类型的数据转换.比如jsp页面中的form值与字段值的转换,下面写一个例子. 1.创建一个jsp页面,编写一个f ...
- JavaScript学习笔记——简单无缝循环滚动展示图片的实现
今天做了一个简单的无缝循环滚动的实例,这种实例在网页中其实还挺常见的,下面分享一下我的学习收获. 首先,无缝滚动的第一个重点就是——动.关于怎么让页面的元素节点动起来,这就得学明白关于JavaScri ...
随机推荐
- pyTorch 基于以resnet50为backbone的PSPNet 训练VOC2012数据集
代码链接:https://github.com/ggyyzm/pytorch_segmentation 使用PSPNet作为主干分类网络 1.将VOC2012数据集下载并解压到data/VOCtrai ...
- 大数据技术原理与应用:【第二讲】大数据处理架构Hadoop
2.1 Hadoop概论 创始人:Doug Cutting 1.简介: 开源免费; 操作简单,极大降低使用的复杂性; Hadoop是Java开发的; 在Hadoop上开发应用支持多种编程语言.不限于J ...
- BSGS算法(大小步算法)
$BSGS$ 算法 $Baby\ Steps\ Giant\ Steps$. 致力于解决给定两个互质的数 $a,\ p$ 求一个最小的非负整数 $x$ 使得 $a^x\equiv b(mod\ p)$ ...
- In Java, how can I test if an Array contains a certain value?
public static final String[] VALUES = newString[]{"AB","BC","CD"," ...
- FileZilla 连接站点 编辑配置文件
- 实现js版StringBuilder
.net中StringBuilder的好处不多说了,下面是js版的StringBuilder var StringBuilder = (function () { function StringBui ...
- Java多态中成员的调用的特殊情况
1.当子类和父类中有相同的成员属性的时候 public class Demo { public static void main(String[] args) { Father son=new Son ...
- three.js 居中-组
原文:https://blog.csdn.net/qq_30100043/article/details/78921224 代码: <!DOCTYPE html> <html lan ...
- vb.net 对字符串中的括号匹配进行判断,容许嵌套
' 括号检查 ' 括号共有四种: 1(英文圆括号),2(中文圆括号),3[方括号],4{花括号} ' 要左右匹配(可以嵌套) Private Shared Function i ...
- Java编程思想之十二 通过异常处理错误
Java的基本概念是结构不佳的代码不能运行余下的问题必须在运行期间解决,这就需要错误源能通过某种方式,把适当的信息传递给某个接收者--该接收者将知道如何正确处理这里问题. 12.1 概念 使用异常所带 ...