织梦网站是不是容易做seo,百度合伙人官网app,网站建设 中关村,做域名交易网站一、前言在上一篇关于简化模式中#xff0c;通过客户端以浏览器的形式请求「IdentityServer」服务获取访问令牌#xff0c;从而请求获取受保护的资源#xff0c;但由于token携带在url中#xff0c;安全性方面不能保证。因此#xff0c;我们可以考虑通过其他方式来解决这个… 一、前言在上一篇关于简化模式中通过客户端以浏览器的形式请求「IdentityServer」服务获取访问令牌从而请求获取受保护的资源但由于token携带在url中安全性方面不能保证。因此我们可以考虑通过其他方式来解决这个问题。我们通过Oauth2.0的「授权码模式」了解这种模式不同于简化模式「在于授权码模式不直接返回token而是先返回一个授权码然后再根据这个授权码去请求token」。这显得更为安全。所以在这一篇中我们将通过多种授权模式中的「授权码」模式进行说明主要针对介绍「IdentityServer」保护API的资源「授权码」访问API资源。二、初识❝「指的是第三方应用先申请一个授权码然后再用该码获取令牌实现与资源服务器的通信。」❞看一个常见的QQ登陆第三方网站的流程如下图所示2.1 适用范围授权码模式(authorization code)是功能最完整、流程最严密的授权模式。❝「授权码模式适用于有后端的应用因为客户端根据授权码去请求token时是需要把客户端密码转进来的为了避免客户端密码被暴露所以请求token这个过程需要放在后台。」❞2.2 授权流程 ----------| Resource || Owner || |----------^|(B)----|----- Client Identifier ---------------| -----(A)-- Redirection URI ----| || User- | | Authorization || Agent -----(B)-- User authenticates ---| Server || | | || -----(C)-- Authorization Code ---| |-|----|--- ---------------| | ^ v(A) (C) | || | | |^ v | |--------- | || |---(D)-- Authorization Code --------- || Client | Redirection URI || | || |---(E)----- Access Token ---------------------------- (w/ Optional Refresh Token)
「授权码授权流程描述」A「用户访问第三方应用第三方应用将用户导向认证服务器」B「用户选择是否给予第三方应用授权」C「假设用户给予授权认证服务器将用户导向第三方应用事先指定的重定向URI同时带上一个授权码」D「第三方应用收到授权码带上上一步时的重定向URI向认证服务器申请访问令牌。这一步是在第三方应用的后台的服务器上完成的对用户不可见」E「认证服务器核对了授权码和重定向URI确认无误后向第三方应用发送访问令牌(Access Token)和更新令牌(Refresh token)」F「访问令牌过期后刷新访问令牌」2.2.1 过程详解访问令牌请求「1用户访问第三方应用第三方应用将用户导向认证服务器」 (用户的操作用户访问https://client.example.com/cb跳转到登录地址选择授权服务器方式登录)
在授权开始之前它首先生成state参数(随机字符串)。client端将需要存储这个cookie会话或其他方式以便在下一步中使用。GET /authorize?response_typecodeclient_ids6BhdRkqt3statexyzredirect_urihttps%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
HTTP/1.1 Host: server.example.com
生成的授权URL如上所述如上请求这个地址后重定向访问授权服务器其中 response_type参数为code,表示授权类型返回code授权码。参数是否必须含义response_type必需表示授权类型此处的值固定为codeclient_id必需客户端IDredirect_uri可选表示重定向的URIscope可选表示授权范围。state可选表示随机字符串,可指定任意值认证服务器会返回这个值「2假设用户给予授权认证服务器将用户导向第三方应用事先指定的重定向URI同时带上一个授权码」HTTP/1.1 302 Found
Location: https://client.example.com/cb?codeSplxlOBeZQQYbYS6WxSbIAstatexyz
参数含义code表示授权码必选项。该码的有效期应该很短通常设为10分钟客户端只能使用该码一次否则会被授权服务器拒绝。该码与客户端ID和重定向URI是一一对应关系。state如果客户端的请求中包含这个参数认证服务器的回应也必须一模一样包含这个参数。「3第三方应用收到授权码带上上一步时的重定向URI向认证服务器申请访问令牌。这一步是在第三方应用的后台的服务器上完成的对用户不可见」。POST /token HTTP/1.1
Host: server.example.com
Authorization: Bearer czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_typeauthorization_codecodeSplxlOBeZQQYbYS6WxSbIA
redirect_urihttps%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
参数含义grant_type表示使用的授权模式必选项此处的值固定为authorization_code。code表示上一步获得的授权码必选项。redirect_uri表示重定向URI必选项且必须与步骤1中的该参数值保持一致。client_id表示客户端ID必选项。「4认证服务器核对了授权码和重定向URI确认无误后向第三方应用发送访问令牌(Access Token)和更新令牌(Refresh token)」HTTP/1.1 200 OKContent-Type: application/json;charsetUTF-8Cache-Control: no-storePragma: no-cache{access_token:2YotnFZFEjr1zCsicMWpAA,token_type:Bearer,expires_in:3600,refresh_token:tGzv3JOkF0XG5Qx2TlKWIA,example_parameter:example_value}
参数含义access_token表示访问令牌必选项。token_type表示令牌类型该值大小写不敏感必选项可以是Bearer类型或mac类型。expires_in表示过期时间单位为秒。如果省略该参数必须其他方式设置过期时间。refresh_token表示更新令牌用来获取下一次的访问令牌可选项。scope表示权限范围如果与客户端申请的范围一致此项可省略。「(5) 访问令牌过期后刷新访问令牌」POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_typerefresh_tokenrefresh_tokentGzv3JOkF0XG5Qx2TlKWIA
参数含义granttype表示使用的授权模式此处的值固定为refreshtoken必选项。refresh_token表示早前收到的更新令牌必选项。scope表示申请的授权范围不可以超出上一次申请的范围如果省略该参数则表示与上一次一致。三、实践❝在示例实践中我们将创建一个授权访问服务定义一个MVC客户端MVC客户端通过「IdentityServer」上请求访问令牌并使用它来访问API。❞3.1 搭建 Authorization Server 服务❝搭建认证授权服务❞3.1.1 安装Nuget包❝IdentityServer4 程序包❞3.1.2 配置内容建立配置内容文件Config.cspublic static class Config
{public static IEnumerableIdentityResource IdentityResources new IdentityResource[]{new IdentityResources.OpenId(),new IdentityResources.Profile(),};public static IEnumerableApiScope ApiScopes new ApiScope[]{new ApiScope(code_scope1)};public static IEnumerableApiResource ApiResources new ApiResource[]{new ApiResource(api1,api1){Scopes{ code_scope1 },UserClaims{JwtClaimTypes.Role}, //添加Cliam 角色类型ApiSecrets{new Secret(apipwd.Sha256())}}};public static IEnumerableClient Clients new Client[]{new Client{ClientId code_client,ClientName code Auth,AllowedGrantTypes GrantTypes.Code,RedirectUris {http://localhost:5002/signin-oidc, //跳转登录到的客户端的地址},// RedirectUris {http://localhost:5002/auth.html }, //跳转登出到的客户端的地址PostLogoutRedirectUris {http://localhost:5002/signout-callback-oidc,},ClientSecrets { new Secret(511536EF-F270-4058-80CA-1C89C192F69A.Sha256()) },AllowedScopes {IdentityServerConstants.StandardScopes.OpenId,IdentityServerConstants.StandardScopes.Profile,code_scope1},//允许将token通过浏览器传递AllowAccessTokensViaBrowsertrue,// 是否需要同意授权 默认是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();services.ConfigureNonBreakingSameSiteCookies();}
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.Code」为「授权码模式」。所以在本文中我们需要添加一个Client用于支持授权码模式(「Authorization Code」)。❞3.2 搭建API资源❝实现对API资源进行保护❞3.2.1 快速搭建一个API项目3.2.2 安装Nuget包❝IdentityServer4.AccessTokenValidation 包❞3.2.3 注册服务在startup.cs中ConfigureServices方法添加如下代码 public void ConfigureServices(IServiceCollection services){services.AddControllersWithViews();services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)services.AddAuthentication(Bearer).AddIdentityServerAuthentication(options {options.Authority http://localhost:5001;options.RequireHttpsMetadata false;options.ApiName api1;options.ApiSecret apipwd; //对应ApiResources中的密钥});}
AddAuthentication把Bearer配置成默认模式将身份认证服务添加到DI中。AddIdentityServerAuthentication把IdentityServer的access token添加到DI中供身份认证服务使用。3.2.4 配置管道在startup.cs中Configure方法添加如下代码 public void Configure(IApplicationBuilder app, IWebHostEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();} app.UseRouting();app.UseAuthentication();app.UseAuthorization();app.UseEndpoints(endpoints {endpoints.MapDefaultControllerRoute();});}
UseAuthentication将身份验证中间件添加到管道中UseAuthorization 将启动授权中间件添加到管道中以便在每次调用主机时执行身份验证授权功能。3.2.5 添加API资源接口[Route(api/[Controller])]
[ApiController]
public class IdentityController:ControllerBase
{[HttpGet(getUserClaims)][Authorize]public IActionResult GetUserClaims(){return new JsonResult(from c in User.Claims select new { c.Type, c.Value });}
}
在IdentityController 控制器中添加 [Authorize] , 在进行请求资源的时候需进行认证授权通过后才能进行访问。3.3 搭建MVC 客户端❝实现对客户端认证授权访问资源❞3.3.1 快速搭建一个MVC项目3.3.2 安装Nuget包❝IdentityServer4.AccessTokenValidation 包❞3.3.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) //使用Cookie作为验证用户的首选方式.AddOpenIdConnect(oidc, options {options.Authority http://localhost:5001; //授权服务器地址options.RequireHttpsMetadata false; //暂时不用httpsoptions.ClientId code_client;options.ClientSecret 511536EF-F270-4058-80CA-1C89C192F69A;options.ResponseType code; //代表Authorization Codeoptions.Scope.Add(code_scope1); //添加授权资源options.SaveTokens true; //表示把获取的Token存到Cookie中options.GetClaimsFromUserInfoEndpoint true;});services.ConfigureNonBreakingSameSiteCookies();}
❝AddAuthentication注入添加认证授权当需要用户登录时使用 cookie 来本地登录用户通过“Cookies”作为DefaultScheme并将 DefaultChallengeScheme 设置为“oidc”使用 AddCookie 添加可以处理 cookie 的处理程序。在AddOpenIdConnect用于配置执行 OpenID Connect 协议的处理程序和相关参数。Authority表明之前搭建的 IdentityServer 授权服务地址。然后我们通过ClientId、ClientSecret,识别这个客户端。SaveTokens用于保存从IdentityServer获取的token至cookie,「ture」标识ASP.NETCore将会自动存储身份认证session的access和refresh token。❞3.3.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: {controllerHome}/{actionIndex}/{id?});});}
❝UseAuthentication将身份验证中间件添加到管道中UseAuthorization 将启动授权中间件添加到管道中以便在每次调用主机时执行身份验证授权功能。❞3.3.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.6 添加资源访问在HomeController控制器添加对API资源访问的接口方法。在进行请求的时候访问API受保护资源。 /// summary/// 测试请求API资源(api1)/// /summary/// returns/returnspublic async TaskIActionResult getApi(){var client new HttpClient();var accessToken await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);if (string.IsNullOrEmpty(accessToken)){return Json(new { msg accesstoken 获取失败 });}client.DefaultRequestHeaders.Authorization new AuthenticationHeaderValue(Bearer, accessToken);var httpResponse await client.GetAsync(http://localhost:5003/api/identity/GetUserClaims); var result await httpResponse.Content.ReadAsStringAsync();if (!httpResponse.IsSuccessStatusCode){return Json(new { msg 请求 api1 失败。, error result });}return Json(new{msg 成功,data JsonConvert.DeserializeObject(result)});}
❝测试这里通过获取accessToken之后设置client请求头的认证访问API资源受保护的地址获取资源。❞3.4 效果3.4.1 动图3.4.2 过程在用户访问MVC程序时候将用户导向认证服务器在客户端向授权服务器Authorization Endpoint进行验证的时候我们可以发现向授权服务器发送的请求附带的那些参数就是我们之前说到的数据(clientid,redirect_url,type等)继续往下看发现在用户给予授权完成登录之后可以看到在登录后授权服务器向重定向URL地址同时带上一个授权码数据带给MVC程序。随后MVC向授权客户端的Token终结点发送请求从下图可以看到这次请求包含了client_idclient_secretcodegrant_type和redirect_uri向授权服务器申请访问令牌token, 并且在响应中可以看到授权服务器核对了授权码和重定向地址URI确认无误后向第三方应用发送访问令牌(Access Token)和更新令牌(Refresh token)完成获取令牌后访问受保护资源的时候带上令牌请求访问可以成功响应获取用户信息资源。四、总结本篇主要阐述以「授权码授权」编写一个MVC客户端并通过客户端以浏览器的形式请求「IdentityServer」上请求获取访问令牌从而访问受保护的API资源。「授权码模式」解决了简化模式由于token携带在url中安全性方面不能保证问题而通过授权码的模式不直接返回token而是先返回一个授权码然后再根据这个授权码去请求token这个请求token这个过程需要放在后台这种方式也更为安全。适用于有后端的应用。在后续会对这方面进行介绍继续说明数据库持久化问题以及如何应用在API资源服务器中和配置在客户端中会进一步说明。如果有不对的或不理解的地方希望大家可以多多指正提出问题一起讨论,不断学习,共同进步。项目地址https://github.com/i3yuan/Yuan.IdentityServer4.Demo/tree/main/DiffAuthMode/AuthorizationCode五、附加「OpenID Connect」资料「Authorization Code资料」「samesite问题解决」