网站制作主要公司,珠海建设工程备案网站,长沙哪里有创建网站的公司,昆山小程序制作一、前言从上一篇关于资源密码凭证模式中#xff0c;通过使用client_id和client_secret以及用户名密码通过应用Client(客户端)直接获取#xff0c;从而请求获取受保护的资源#xff0c;但是这种方式存在client可能存了用户密码这不安全性问题#xff0c;所以需要做到client… 一、前言从上一篇关于资源密码凭证模式中通过使用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必需表示授权类型此处的值固定为tokenclient_id必需客户端IDredirect_uri可选表示重定向的URIscope可选表示授权范围。state可选表示随机字符串「1资源服务器生成授权URL并将用户重定向到授权服务器」 (用户的操作用户访问https://resourcesServer/index.html跳转到登录地址选择授权服务器方式登录)
在授权开始之前它首先生成state参数(随机字符串)。client端将需要存储这个cookie会话或其他方式以便在下一步中使用。第一步A 网站提供一个链接要求用户跳转到 B 网站授权用户数据给 A 网站使用。https://oauth2Server/oauth2/default/v1/authorize?
response_typetoken
client_id${clientId}
redirect_urihttps://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_tokentoken_typeBearerexpires_in3600scopephotostate随机字符串
其中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 IEnumerableIdentityResource IdentityResources new IdentityResource[]{new IdentityResources.OpenId(),new IdentityResources.Profile(),};public static IEnumerableApiScope ApiScopes new ApiScope[]{new ApiScope(Implicit_scope1)};public static IEnumerableApiResource ApiResources new ApiResource[]{new ApiResource(api1,api1){Scopes{ Implicit_scope1 },ApiSecrets{new Secret(apipwd.Sha256())} //api密钥}};public static IEnumerableClient 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},// 是否需要同意授权 默认是falseRequireConsenttrue}, };}
❝RedirectUris : 登录成功回调处理的客户端地址处理回调返回的数据可以有多个。PostLogoutRedirectUris 跳转登出到的客户端的地址。这两个都是配置的客户端的地址且是identityserver4组件里面封装好的地址作用分别是登录注销的回调❞因为是「简化」授权的方式所以我们通过代码的方式来创建几个测试用户。新建测试用户文件TestUsers.cs public class TestUsers{public static ListTestUser Users{get{var address new{street_address One Hacker Way,locality Heidelberg,postal_code 69118,country Germany};return new ListTestUser{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, i3yuanemail.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 configbuilder.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 securebuilder.AddDeveloperSigningCredential();}
3.1.4 配置管道在startup.cs中Configure方法添加如下代码 public void Configure(IApplicationBuilder app, IWebHostEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseRouting();app.UseIdentityServer();app.UseEndpoints(endpoints {endpoints.MapGet(/, async context {await context.Response.WriteAsync(Hello World!);});});}
以上内容是快速搭建简易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();} app.UseRouting();app.UseCookiePolicy();app.UseAuthentication();app.UseAuthorization();app.UseEndpoints(endpoints {endpoints.MapDefaultControllerRoute();});}
❝UseAuthentication将身份验证中间件添加到管道中UseAuthorization 将启动授权中间件添加到管道中以便在每次调用主机时执行身份验证授权功能。❞3.2.5 添加授权在HomeController控制器并添加[Authorize]特性到其中一个方法。在进行请求的时候需进行认证授权通过后才能进行访问。 [Authorize]public IActionResult Privacy(){ViewData[Message] Secure page.;return View();}
还要修改主视图以显示用户的Claim以及cookie属性。using Microsoft.AspNetCore.Authenticationh2Claims/h2dlforeach (var claim in User.Claims){dtclaim.Type/dtddclaim.Value/dd}
/dlh2Properties/h2dlforeach (var prop in (await Context.AuthenticateAsync()).Properties.Items){dtprop.Key/dtddprop.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();...❝参考资料 Chrome80调整SameSite策略对IdentityServer4的影响以及处理方案❞五、总结本篇主要阐述以「简化授权」编写一个MVC客户端并通过客户端以浏览器的形式请求「IdentityServer」上请求获取访问令牌从而访问资源。「简化模式」解决了客户端模式用户身份验证和授权的问题也解决了上一篇中「资源所有者密码凭证授权」面临的用户密码暴露的问题是基于浏览器的应用。但由于token携带在url中安全性方面不能保证建议把token时效设置短一些在后续会对在安全性方面做得更好的模式进行说明数据库持久化问题以及如何应用在API资源服务器中和配置在客户端中会进一步说明。如果有不对的或不理解的地方希望大家可以多多指正提出问题一起讨论,不断学习,共同进步。项目地址https://github.com/i3yuan/Yuan.IdentityServer4.Demo/tree/main/DiffAuthMode/ImplicitMVC六、附加「OpenID Connect」资料「Implicit Grant资料」「samesite问题解决」