IdentityServer4系列 | 简化模式
一、前言
从上一篇关于资源密码凭证模式中,通过使用client_id和client_secret以及用户名密码通过应用Client(客户端)直接获取,从而请求获取受保护的资源,但是这种方式存在client可能存了用户密码这不安全性问题,所以需要做到client是高可信的应用。因此,我们可以考虑通过其他方式来解决这个问题。
我们通过Oauth2.0的简化授权模式了解到,可以使用这种方式来解决这个问题,让用户自己在IdentityServer服务器进行登录验证,客户端不需要知道用户的密码,从而实现用户密码的安全性。
所以在这一篇中,我们将通过多种授权模式中的简化授权模式进行说明,主要针对介绍IdentityServer保护API的资源,简化授权访问API资源。
二、初识
有些 Web 应用是纯前端应用,没有后端,必须将令牌储存在前端。RFC 6749 就规定了这种方式,允许直接向前端颁发令牌。这种方式没有授权码这个中间步骤,所以称为(授权码)"简化"(implicit)。
简化模式(implicit grant type)不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了"授权码"这个步骤(授权码模式后续会说明)。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。
这种方式把令牌直接传给前端,是很不安全的。因此,只能用于一些安全要求不高的场景,并且令牌的有效期必须非常短,通常就是会话期间(session)有效,浏览器关掉,令牌就失效了。
2.1 适用范围
这种模式的使用场景是基于浏览器的应用
这种模式基于安全性考虑,建议把token时效设置短一些, 不支持refresh token
2.2 授权流程:
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI --->| |
| User- | | Authorization |
| Agent -|----(B)-- User authenticates -->| Server |
| | | |
| |<---(C)--- Redirection URI ----<| |
| | with Access Token +---------------+
| | in Fragment
| | +---------------+
| |----(D)--- Redirection URI ---->| Web-Hosted |
| | without Fragment | Client |
| | | Resource |
| (F) |<---(E)------- Script ---------<| |
| | +---------------+
+-|--------+
| |
(A) (G) Access Token
| |
^ v
+---------+
| |
| Client |
| |
+---------+
简化授权流程描述
(A)客户端携带客户端标识以及重定向URI到授权服务器;
(B)用户确认是否要授权给客户端;
(C)授权服务器得到许可后,跳转到指定的重定向地址,并将令牌也包含在了里面;
(D)客户端不携带上次获取到的包含令牌的片段,去请求资源服务器;
(E)资源服务器会向浏览器返回一个脚本;
(F)浏览器会根据上一步返回的脚本,去提取在C步骤中获取到的令牌;
(G)浏览器将令牌推送给客户端。
2.2.1 过程详解
访问令牌请求
参数 | 是否必须 | 含义 |
---|---|---|
response_type | 必需 | 表示授权类型,此处的值固定为"token" |
client_id | 必需 | 客户端ID |
redirect_uri | 可选 | 表示重定向的URI |
scope | 可选 | 表示授权范围。 |
state | 可选 | 表示随机字符串 |
(1)资源服务器生成授权URL并将用户重定向到授权服务器
(用户的操作:用户访问https://resourcesServer/index.html跳转到登录地址,选择授权服务器方式登录)
在授权开始之前,它首先生成state参数(随机字符串)。client端将需要存储这个(cookie,会话或其他方式),以便在下一步中使用。
第一步,A 网站提供一个链接,要求用户跳转到 B 网站,授权用户数据给 A 网站使用。
https://oauth2Server/oauth2/default/v1/authorize?
response_type=token
&client_id=${clientId}
&redirect_uri=https://resourcesServer/implicit.html
&scope=授权范围
&state=随机字符串
生成的授权URL如上所述(如上),请求这个地址后重定向访问授权服务器,其中 response_type参数为token,表示直接返回令牌。
(2)验证授权服务器登陆状态
(用户的操作:如果未登陆用账号 User,密码12345登陆https://oauth2Server/login,如果已登陆授权服务器不需要此步骤)
如果未登陆账号,自动跳转到授权服务器登陆地址,登陆授权服务器以后用户被重定向client端
https://resourcesServer/implicit.html
如已提前登陆授权服务器或授权服务器登陆会话还存在自动重定向到client端
https://resourcesServer/implicit.html
(3)验证状态参数
(用户的操作:无需操作)
用户被重定向回客户机,URL中现在有一个片段包含访问令牌以及一些其他信息。
用户跳转到 B 网站,登录后同意给予 A 网站授权。这时,B 网站就会跳回redirect_uri
参数指定的跳转网址,并且把令牌作为 URL 参数,传给 A 网站。
https://resourcesServer/authorization-code.html
\#access_token=&token_type=Bearer&expires_in=3600&scope=photo&state=随机字符串
其中,token参数就是令牌,A网站因此直接在前端拿到令牌。
注意,令牌的位置是 URL 锚点(fragment),而不是查询字符串(querystring),这是因为 OAuth 2.0 允许跳转网址是 HTTP 协议,因此存在"中间人攻击"的风险,而浏览器跳转时,锚点不会发到服务器,就减少了泄漏令牌的风险。
用户使用这个令牌访问资源服务器,当令牌失效时使用刷新令牌去换取新的令牌
三、实践
在示例实践中,我们将创建一个授权访问服务,定义一个MVC客户端,MVC客户端通过IdentityServer上请求访问令牌,并使用它来访问API。
3.1 搭建 Authorization Server 服务
搭建认证授权服务
3.1.1 安装Nuget包
IdentityServer4
程序包
3.1.2 配置内容
建立配置内容文件Config.cs
public static class Config
{
public static IEnumerable<IdentityResource> IdentityResources =>
new IdentityResource[]
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
public static IEnumerable<ApiScope> ApiScopes =>
new ApiScope[]
{
new ApiScope("Implicit_scope1")
};
public static IEnumerable<ApiResource> ApiResources =>
new ApiResource[]
{
new ApiResource("api1","api1")
{
Scopes={ "Implicit_scope1" },
ApiSecrets={new Secret("apipwd".Sha256())} //api密钥
}
};
public static IEnumerable<Client> Clients =>
new Client[]
{
new Client
{
ClientId = "Implicit_client",
ClientName = "Implicit Auth",
AllowedGrantTypes = GrantTypes.Implicit,
RedirectUris ={
"http://localhost:5002/signin-oidc", //跳转登录到的客户端的地址
},
PostLogoutRedirectUris ={
"http://localhost:5002/signout-callback-oidc",//跳转登出到的客户端的地址
},
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"Implicit_scope1"
},
// 是否需要同意授权 (默认是false)
RequireConsent=true
},
};
}
RedirectUris
: 登录成功回调处理的客户端地址,处理回调返回的数据,可以有多个。
PostLogoutRedirectUris
:跳转登出到的客户端的地址。这两个都是配置的客户端的地址,且是identityserver4组件里面封装好的地址,作用分别是登录,注销的回调
因为是简化授权的方式,所以我们通过代码的方式来创建几个测试用户。
新建测试用户文件TestUsers.cs
public class TestUsers
{
public static List<TestUser> Users
{
get
{
var address = new
{
street_address = "One Hacker Way",
locality = "Heidelberg",
postal_code = 69118,
country = "Germany"
};
return new List<TestUser>
{
new TestUser
{
SubjectId = "1",
Username = "i3yuan",
Password = "123456",
Claims =
{
new Claim(JwtClaimTypes.Name, "i3yuan Smith"),
new Claim(JwtClaimTypes.GivenName, "i3yuan"),
new Claim(JwtClaimTypes.FamilyName, "Smith"),
new Claim(JwtClaimTypes.Email, "i3yuan@email.com"),
new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
new Claim(JwtClaimTypes.WebSite, "http://i3yuan.top"),
new Claim(JwtClaimTypes.Address, JsonSerializer.Serialize(address), IdentityServerConstants.ClaimValueTypes.Json)
}
}
};
}
}
}
返回一个TestUser的集合。
通过以上添加好配置和测试用户后,我们需要将用户注册到IdentityServer4服务中,接下来继续介绍。
3.1.3 注册服务
在startup.cs中ConfigureServices方法添加如下代码:
public void ConfigureServices(IServiceCollection services)
{
var builder = services.AddIdentityServer()
.AddTestUsers(TestUsers.Users); //添加测试用户
// in-memory, code config
builder.AddInMemoryIdentityResources(Config.IdentityResources);
builder.AddInMemoryApiScopes(Config.ApiScopes);
builder.AddInMemoryApiResources(Config.ApiResources);
builder.AddInMemoryClients(Config.Clients);
// not recommended for production - you need to store your key material somewhere secure
builder.AddDeveloperSigningCredential();
}
3.1.4 配置管道
在startup.cs中Configure方法添加如下代码:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseRouting();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseAuthorization();
app.UseIdentityServer();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
以上内容是快速搭建简易IdentityServer项目服务的方式。
这搭建 Authorization Server 服务跟上一篇资源密码凭证模式有何不同之处呢?
- 在Config中配置客户端(client)中定义了一个
AllowedGrantTypes
的属性,这个属性决定了Client可以被哪种模式被访问,GrantTypes.Implicit为简化授权。所以在本文中我们需要添加一个Client用于支持简化授权(implicit)。- 简化授权不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,所有步骤在浏览器中完成,所以需要配置对应的回调地址和登出地址。这也是不同于之前的资源所有者凭证模式。
3.2 搭建MVC 客户端
实现对客户端认证授权访问资源
3.2.1 快速搭建一个MVC项目
3.2.2 安装Nuget包
IdentityServer4.AccessTokenValidation 包
3.2.3 注册服务
要将对 OpenID Connect 身份认证的支持添加到MVC应用程序中。
在startup.cs中ConfigureServices方法添加如下代码:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddAuthorization();
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.Authority = "http://localhost:5001";
options.RequireHttpsMetadata = false;
options.ClientId = "Implicit_client";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
});
}
AddAuthentication
注入添加认证授权,当需要用户登录时,使用cookie
来本地登录用户(通过“Cookies”作为DefaultScheme
),并将DefaultChallengeScheme
设置为“oidc”,使用
AddCookie
添加可以处理 cookie 的处理程序。因为简化模式的实现是就是
OpenID Connect
,所以在AddOpenIdConnect
用于配置执行OpenID Connect
协议的处理程序。Authority
表明之前搭建的 IdentityServer 授权服务地址。然后我们通过ClientId
。识别这个客户端。SaveTokens
用于在 cookie 中保留来自IdentityServer 的令牌。
3.2.4 配置管道
然后要确保认证服务执行对每个请求的验证,加入UseAuthentication
和UseAuthorization
到Configure
中,在startup.cs中Configure方法添加如下代码:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
UseAuthentication将身份验证中间件添加到管道中;
UseAuthorization 将启动授权中间件添加到管道中,以便在每次调用主机时执行身份验证授权功能。
3.2.5 添加授权
在HomeController控制器并添加[Authorize]
特性到其中一个方法。在进行请求的时候,需进行认证授权通过后,才能进行访问。
[Authorize]
public IActionResult Privacy()
{
ViewData["Message"] = "Secure page.";
return View();
}
还要修改主视图以显示用户的Claim以及cookie属性。
@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>
访问 Privacy 页面,跳转到认证服务地址,进行账号密码登录,Logout 用于用户的注销操作。
3.3 效果
3.3.1 项目测试
四、问题
4.1 SameSite策略
在Chrome浏览器中,进行认证授权的时候,用户登录之后,无法跳转到原网页,还是停留在登录页中,可以看控制台就发现上图的效果。
最后查找资料发现,是Google将于2020年2月份发布Chrome 80版本。本次发布将推进Google的“渐进改良Cookie”策略,打造一个更为安全和保障用户隐私的网络环境。所以本次更新可能导致浏览器无法向服务端发送Cookie。如果你有多个不同域名的应用,部分用户很有可能出现会话时常被打断的情况,还有部分用户可能无法正常登出系统。
所以我们需要解决这个问题:
方法一:将域名升级为 HTTPS
方法二:使用代码修改 SameSite 设置
新增 SameSiteCookiesServiceCollectionExtensions 类 (可以下载源码查看)
private const SameSiteMode Unspecified = (SameSiteMode)(-1);
改为
private const SameSiteMode Unspecified = SameSiteMode.Lax;
如果没有域名或内网环境,可以使用该方法,在 Startup 添加引用。
public IServiceProvider ConfigureServices(IServiceCollection services)
{
...
services.ConfigureNonBreakingSameSiteCookies();
...
五、总结
- 本篇主要阐述以简化授权,编写一个MVC客户端,并通过客户端以浏览器的形式请求IdentityServer上请求获取访问令牌,从而访问资源。
- 简化模式解决了客户端模式用户身份验证和授权的问题,也解决了上一篇中资源所有者密码凭证授权面临的用户密码暴露的问题,是基于浏览器的应用。但由于token携带在url中,安全性方面不能保证,建议把token时效设置短一些
- 在后续会对在安全性方面做得更好的模式进行说明,数据库持久化问题,以及如何应用在API资源服务器中和配置在客户端中,会进一步说明。
- 如果有不对的或不理解的地方,希望大家可以多多指正,提出问题,一起讨论,不断学习,共同进步。
- 项目地址
六、附加
IdentityServer4系列 | 简化模式的更多相关文章
- IdentityServer4[5]简化模式
Implicit简化模式(直接通过浏览器的链接跳转申请令牌) 简化模式是相对于授权码模式而言的.其不再需要[Client]的参与,所有的认证和授权都是通过浏览器来完成的. 创建项目 IdentityS ...
- IdentityServer4系列 | 授权码模式
一.前言 在上一篇关于简化模式中,通过客户端以浏览器的形式请求IdentityServer服务获取访问令牌,从而请求获取受保护的资源,但由于token携带在url中,安全性方面不能保证.因此,我们可以 ...
- asp.net权限认证:OWIN实现OAuth 2.0 之简化模式(Implicit)
asp.net权限认证系列 asp.net权限认证:Forms认证 asp.net权限认证:HTTP基本认证(http basic) asp.net权限认证:Windows认证 asp.net权限认证 ...
- IdentityServer4系列之中文文档及实际项目经验分享
0.前言 原文:http://docs.identityserver.io/en/release/声明: 1.目录一至五章节根据IdentityServer英文文档翻译而来,有些内容会根据自己的理解来 ...
- Core篇——初探IdentityServer4(OpenID Connect模式)
Core篇——初探IdentityServer4(OpenID Connect客户端验证) 目录 1.Oauth2协议授权码模式介绍2.IdentityServer4的OpenID Connect客户 ...
- 认证授权:IdentityServer4 - 各种授权模式应用
前言: 前面介绍了IdentityServer4 的简单应用,本篇将继续讲解IdentityServer4 的各种授权模式使用示例 授权模式: 环境准备 a)调整项目结构如下: b)调整cz.Id ...
- 接口测试工具-Jmeter使用笔记(八:模拟OAuth2.0协议简化模式的请求)
背景 博主的主要工作是测试API,目前已经用Jmeter+Jenkins实现了项目中的接口自动化测试流程.但是马上要接手的项目,API应用的是OAuth2.0协议授权,并且采用的是简化模式(impli ...
- Core篇——初探IdentityServer4(客户端模式,密码模式)
Core篇——初探IdentityServer4(客户端模式,密码模式) 目录 1.Oatuth2协议的客户端模式介绍2.IdentityServer4客户端模式实现3.Oatuth2协议的密码模式介 ...
- 6. oracle学习入门系列之六 模式
oracle学习入门系列之六 模式 上篇咱们学习记录了ORACLE数据库中的数据库结构.内存结构和进程等.篇幅 蛤蟆感觉偏多了.这次要休整下,每次笔记不宜太多,不然与书籍有何差别. 我们要保证的是每次 ...
随机推荐
- python-找不到tk包
找不到-tk包 直接sudo apt-get install python3-tk 或者sudo apt-get install python-tk 百度上的方法不可信,还是直接这样一句命令来的实在
- JIRA、Confluence等产品明年2月停售本地化版本,将影响中国近90%的客户!
作为目前应用最为广泛的软件开发管理软件,JIRA.Confluence等产品几乎被所有的科技型公司所应用.我们的每天的任务管理.文档编写等工作几乎都在这些软件的帮助下进行和管理.当然我也不例外,在读书 ...
- Pytest学习(六) - conftest.py结合接口自动化的举例使用
一.conftest.py作用 可以理解成存放fixture的配置文件 二.conftest.py配置fixture注意事项 pytest会默认读取conftest.py里面的所有fixture co ...
- WSL-Ubuntu18.04 磁盘迁移 与 ns3-gym 安装
WSL 安装 win10 版本应大于或等于 1903 win10 设置页面 输入 控制面板 并点击进入 找到 程序和功能 并打开 找到 启动或关闭 Windows 功能 并打开 向下拉 勾选 适用于L ...
- 04 . Vue组件注册,数据交互,调试工具及组件插槽介绍及使用
vue组件 组件(Component)是 Vue.js 最强大的功能之一. 组件可以扩展 HTML 元素,封装可重用的代码. 组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的 ...
- 浅析TCP协议---转载
https://cloud.tencent.com/developer/article/1150971 前言 说到TCP协议,相信大家都比较熟悉了,对于TCP协议总能说个一二三来,但是TCP协议又是一 ...
- TCP中RTT的测量和RTO的计算
https://blog.csdn.net/zhangskd/article/details/7196707 tcp传输往返时间是指:发送方发送tcp断开时, 到发送方接收到改段立即响应的所耗费的时间 ...
- 有关String的那点事
(1)String str1 = "abc"; System.out.println(str1 == "abc"); 步骤: 1) 栈中开辟一块空间存放引用st ...
- 鸿蒙系统freeModbusTcp移植简介
freeModebus是工业中常用的一种通信, 在鸿蒙系统来移植 细节查看代码中,博文只是一些参考以及注意点, 参考了 wifi连接: https://harmonyos.51cto.com/post ...
- go返回json数据
package main import ( "encoding/json" ) type Repay struct { Code uint64 `json:"code&q ...