├── LICENSE ├── README.md ├── 欢迎使用IdentityModel文档.md ├── 第一部分 协议客户端库 ├── 第1章 发现端点(Discovery Endpoint).md ├── 第2章 授权端点(Authorize Endpoint).md ├── 第3章 结束会话端点(EndSession Point).md ├── 第4章 令牌端点(Token Endpoint).md ├── 第5章 令牌自省端点(Token Introspection Endpoint).md ├── 第6章 令牌撤销端点(Token Revocation Endpoint).md ├── 第7章 UserInfo端点(UserInfo Endpoint).md ├── 第8章 动态客户端注册.md └── 第9章 设备授权端点(Device Authorization Endpoint).md └── 第二部分 杂项助手 ├── 第10章 协议和声明类型常量.md ├── 第11章 创建请求URLs.md ├── 第12章 X.509证书库的Fluent API.md ├── 第13章 Base64 URL编码.md ├── 第14章 纪元时间转换.md └── 第15章 时间常数字符串比较.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 thinksjay 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 欢迎使用IdentityModel文档! 2 | IdentityModel是基于声明的身份,OAuth 2.0和OpenID Connect的.NET标准帮助程序库。 3 | 4 | 它具有以下高级功能: 5 | 6 | * 标准OAuth 2.0和OpenID Connect端点的客户端库,如授权,令牌,发现,内省,撤销等。 7 | * 令牌管理的助手 8 | * 标准JWT声明类型和协议值的常量 9 | * 用于访问X509证书库的简化API 10 | * 用于base64 URL编码的misc帮助程序,时间常量字符串比较和纪元时间 11 | 12 | 怎么弄 13 | * github 14 | * nuget 15 | * CI构建 16 | 17 | [博客地址](https://www.cnblogs.com/thinksjay/p/10787595.html) 18 | 19 | | 支付宝支付 | 微信支付 | 20 | | ------ | ------ | 21 | | | | 22 | 23 | **感谢支持!!!** 24 | -------------------------------------------------------------------------------- /欢迎使用IdentityModel文档.md: -------------------------------------------------------------------------------- 1 | # 欢迎使用IdentityModel文档! 2 | IdentityModel是基于声明的身份,OAuth 2.0和OpenID Connect的.NET标准帮助程序库。 3 | 4 | 它具有以下高级功能: 5 | 6 | * 标准OAuth 2.0和OpenID Connect端点的客户端库,如授权,令牌,发现,内省,撤销等。 7 | * 令牌管理的助手 8 | * 标准JWT声明类型和协议值的常量 9 | * 用于访问X509证书库的简化API 10 | * 用于base64 URL编码的misc帮助程序,时间常量字符串比较和纪元时间 11 | 12 | 怎么弄 13 | * github 14 | * nuget 15 | * CI构建 16 | -------------------------------------------------------------------------------- /第一部分 协议客户端库/第1章 发现端点(Discovery Endpoint).md: -------------------------------------------------------------------------------- 1 | # 第1章 发现端点(Discovery Endpoint) 2 | [OpenID Connect发现端点](https://openid.net/specs/openid-connect-discovery-1_0.html)的客户端库作为httpclient的扩展方法提供。该`GetDiscoveryDocumentAsync`方法返回一个`DiscoveryResponse`对象,该对象具有发现文档的各种元素的强类型和弱类型访问器。 3 | 4 | 在访问文档内容之前,应始终检查`IsError`和`Error`属性。 5 | 6 | 例: 7 | 8 | ``` C# 9 | var client = new HttpClient(); 10 | 11 | var disco = await client.GetDiscoveryDocumentAsync("https://demo.identityserver.io"); 12 | if (disco.IsError) throw new Exception(disco.Error); 13 | ``` 14 | 15 | 可以使用以下属性访问标准元素: 16 | 17 | ``` C# 18 | var tokenEndpoint = disco.TokenEndpoint; 19 | var keys = disco.KeySet.Keys; 20 | ``` 21 | 22 | 自定义元素(或标准属性未涵盖的元素)可以像这样访问: 23 | 24 | ``` C# 25 | // returns string or null 26 | var stringValue = disco.TryGetString("some_string_element"); 27 | 28 | // return a nullable boolean 29 | var boolValue = disco.TryGetBoolean("some_boolean_element"); 30 | 31 | // return array (maybe empty) 32 | var arrayValue = disco.TryGetStringArray("some_array_element"); 33 | 34 | // returns JToken 35 | var rawJson = disco.TryGetValue("some_element); 36 | ``` 37 | 38 | ## 1.1 发现政策 39 | 默认情况下,发现响应在返回到客户端之前已经过验证,验证包括: 40 | 41 | * 强制使用HTTPS(localhost地址除外) 42 | * 强制发行人与当局匹配 43 | * 强制协议端点与权限位于同一DNS名称上 44 | * 强制执行密钥集的存在 45 | 46 | 可以使用`DiscoveryPolicy`类修改所有标准验证规则,例如禁用颁发者名称检查: 47 | 48 | ``` C# 49 | var disco = await client.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest 50 | { 51 | Address = "https://demo.identityserver.io", 52 | Policy = 53 | { 54 | ValidateIssuerName = false 55 | } 56 | }); 57 | ``` 58 | 59 | 策略冲突错误会将`DiscoveryResponse`上的`ErrorType`属性设置为`PolicyViolation`。 60 | 61 | ## 1.2 缓存发现文档 62 | 您应该定期更新发现文档的本地副本,以便能够对服务器上的配置更改作出响应。这对于使用自动旋转键进行良好的播放尤其重要。 63 | 64 | 该`DiscoveryCache`类可以帮助你。 65 | 66 | 以下代码将设置缓存,在第一次需要时检索文档,然后将其缓存24小时: 67 | 68 | ``` C# 69 | var cache = new DiscoveryCache("https://demo.identityserver.io"); 70 | ``` 71 | 72 | 然后,您可以像这样访问文档: 73 | 74 | ``` C# 75 | var disco = await cache.GetAsync(); 76 | if (disco.IsError) throw new Exception(disco.Error); 77 | ``` 78 | 79 | 您可以使用该`CacheDuration`属性指定缓存持续时间,也可以通过将`DiscoveryPolicy`传递给构造函数来指定自定义发现策略。 80 | 81 | ### 1.2.1 缓存和HttpClient实例 82 | 默认情况下,发现缓存将在`HttpClient`每次访问发现端点时创建新实例。您可以通过两种方式修改此行为,方法是将预先创建的实例传入构造函数,或者通过提供将`HttpClient`在需要时返回的函数。 83 | 84 | 以下代码将在DI中设置发现缓存,并将使用`HttpClientFactory`以创建客户端: 85 | 86 | ``` C# 87 | services.AddSingleton(r => 88 | { 89 | var factory = r.GetRequiredService(); 90 | return new DiscoveryCache(Constants.Authority, () => factory.CreateClient()); 91 | }); 92 | ``` -------------------------------------------------------------------------------- /第一部分 协议客户端库/第2章 授权端点(Authorize Endpoint).md: -------------------------------------------------------------------------------- 1 | # 第2章 授权端点(Authorize Endpoint) 2 | 对于大多数情况,[OAuth 2.0](https://tools.ietf.org/html/rfc6749#section-3.1)和[OpenID Connect](https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint)授权端点的GET请求需要具有许多查询字符串参数。 3 | 4 | 虽然您可以使用任何方法创建带参数的URL来创建正确的字符串,但[RequestUrl](https://github.com/thinksjay/IdentityModel/blob/master/%E7%AC%AC%E4%BA%8C%E9%83%A8%E5%88%86%20%E6%9D%82%E9%A1%B9%E5%8A%A9%E6%89%8B/%E7%AC%AC11%E7%AB%A0%20%E5%88%9B%E5%BB%BA%E8%AF%B7%E6%B1%82URLs.md)类是完成此任务的简单帮助程序。 5 | 6 | 特别是,您可以使用`CreateAuthorizeUrl`扩展方法为授权端点创建URL - 它支持最常用的参数: 7 | 8 | ``` C# 9 | /// 10 | /// Creates an authorize URL. 11 | /// 12 | /// The request. 13 | /// The client identifier. 14 | /// The response type. 15 | /// The scope. 16 | /// The redirect URI. 17 | /// The state. 18 | /// The nonce. 19 | /// The login hint. 20 | /// The acr values. 21 | /// The prompt. 22 | /// The response mode. 23 | /// The code challenge. 24 | /// The code challenge method. 25 | /// The display option. 26 | /// The max age. 27 | /// The ui locales. 28 | /// The id_token hint. 29 | /// Extra parameters. 30 | /// 31 | public static string CreateAuthorizeUrl(this RequestUrl request, 32 | string clientId, 33 | string responseType, 34 | string scope = null, 35 | string redirectUri = null, 36 | string state = null, 37 | string nonce = null, 38 | string loginHint = null, 39 | string acrValues = null, 40 | string prompt = null, 41 | string responseMode = null, 42 | string codeChallenge = null, 43 | string codeChallengeMethod = null, 44 | string display = null, 45 | int? maxAge = null, 46 | string uiLocales = null, 47 | string idTokenHint = null, 48 | object extra = null) 49 | { ... } 50 | ``` 51 | 52 | 例: 53 | 54 | ``` C# 55 | var ru = new RequestUrl("https://demo.identityserver.io/connect/authorize"); 56 | 57 | var url = ru.CreateAuthorizeUrl( 58 | clientId: "client", 59 | responseType: "implicit", 60 | redirectUri: "https://app.com/callback", 61 | nonce: "xyz", 62 | scope: "openid"); 63 | 64 | ``` 65 | 66 | > **注意** 67 | 该`extra`参数可以是一个串字典或任意其它具有属性的类型。在这两种情况下,值都将序列化为键/值。 -------------------------------------------------------------------------------- /第一部分 协议客户端库/第3章 结束会话端点(EndSession Point).md: -------------------------------------------------------------------------------- 1 | # 第3章 结束会话端点(EndSession Point) 2 | 该[RequestUrl](https://github.com/thinksjay/IdentityModel/blob/master/%E7%AC%AC%E4%BA%8C%E9%83%A8%E5%88%86%20%E6%9D%82%E9%A1%B9%E5%8A%A9%E6%89%8B/%E7%AC%AC11%E7%AB%A0%20%E5%88%9B%E5%BB%BA%E8%AF%B7%E6%B1%82URLs.md)类可用于构造URL发送到OpenID Connect EndSession endpoint。 3 | 4 | 该`CreateEndSessionUrl`扩展方法支持最常用的参数: 5 | 6 | ``` C# 7 | /// 8 | /// Creates a end_session URL. 9 | /// 10 | /// The request. 11 | /// The id_token hint. 12 | /// The post logout redirect URI. 13 | /// The state. 14 | /// The extra parameters. 15 | /// 16 | public static string CreateEndSessionUrl(this RequestUrl request, 17 | string idTokenHint = null, 18 | string postLogoutRedirectUri = null, 19 | string state = null, 20 | object extra = null) 21 | { ... } 22 | ``` 23 | 24 | > **注意** 25 | 该`extra`参数可以是一个串字典或任意其它类型的具有属性。在这两种情况下,值都将序列化为键/值。 -------------------------------------------------------------------------------- /第一部分 协议客户端库/第4章 令牌端点(Token Endpoint).md: -------------------------------------------------------------------------------- 1 | # 第4章 令牌端点(Token Endpoint) 2 | 令牌端点的客户端库([OAuth 2.0](https://tools.ietf.org/html/rfc6749#section-3.2)和[OpenID Connect](https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint))作为`HttpClient`一组扩展方法提供。这允许`HttpClient`以您喜欢的方式创建和管理生命周期- 例如静态或通过像Microsoft这样的工厂`HttpClientFactory`。 3 | 4 | ## 4.1 请求令牌 5 | 调用主扩展方法`RequestTokenAsync`- 它直接支持标准参数,如客户端ID /机密(或断言)和授权类型,但它也允许通过字典设置任意其他参数。所有其他扩展方法最终在内部调用此方法: 6 | 7 | ``` C# 8 | var client = new HttpClient(); 9 | 10 | var response = await client.RequestTokenAsync(new TokenRequest 11 | { 12 | Address = "https://demo.identityserver.io/connect/token", 13 | GrantType = "custom", 14 | 15 | ClientId = "client", 16 | ClientSecret = "secret", 17 | 18 | Parameters = 19 | { 20 | { "custom_parameter", "custom value"}, 21 | { "scope", "api1" } 22 | } 23 | }); 24 | ``` 25 | 26 | 响应属于`TokenResponse`类型并且具有用于标准令牌响应参数等属性`access_token`,`expires_in`等等。你也可以访问原始响应以及对已解析JSON的文档(通过`Raw`和`Json`属性)。 27 | 28 | 在使用响应之前,您应该始终检查`IsError`属性以确保请求成功: 29 | 30 | ``` C# 31 | if (response.IsError) throw new Exception(response.Error); 32 | 33 | var token = response.AccessToken; 34 | var custom = response.Json.TryGetString("custom_parameter"); 35 | ``` 36 | 37 | ## 4.2 使用`client_credentials`授权类型请求令牌 38 | 该方法具有方便`requestclientcredentialstoken`扩展属性的`client_credentials`类型: 39 | 40 | ``` C# 41 | var response = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest 42 | { 43 | Address = "https://demo.identityserver.io/connect/token", 44 | 45 | ClientId = "client", 46 | ClientSecret = "secret", 47 | Scope = "api1" 48 | }); 49 | ``` 50 | 51 | ## 4.3 使用`password`授权类型请求令牌 52 | 该方法具有方便`requestclientcredentialstoken`扩展属性的`password`类型: 53 | 54 | ``` C# 55 | var response = await client.RequestPasswordTokenAsync(new PasswordTokenRequest 56 | { 57 | Address = "https://demo.identityserver.io/connect/token", 58 | 59 | ClientId = "client", 60 | ClientSecret = "secret", 61 | Scope = "api1", 62 | 63 | UserName = "bob", 64 | Password = "bob" 65 | }); 66 | ``` 67 | 68 | ## 4.4 使用`authorization_code`授权类型请求令牌 69 | 该方法具有方便`requestclientcredentialstoken`扩展属性的`authorization_code`类型和PKCE: 70 | 71 | ``` C# 72 | var response = await client.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest 73 | { 74 | Address = IdentityServerPipeline.TokenEndpoint, 75 | 76 | ClientId = "client", 77 | ClientSecret = "secret", 78 | 79 | Code = code, 80 | RedirectUri = "https://app.com/callback", 81 | 82 | // optional PKCE parameter 83 | CodeVerifier = "xyz" 84 | }); 85 | ``` 86 | 87 | ## 4.5 使用`refresh_token`授权类型请求令牌 88 | 该方法具有方便`requestclientcredentialstoken`扩展属性的`refresh_token`类型: 89 | 90 | ``` C# 91 | var response = await _client.RequestRefreshTokenAsync(new RefreshTokenRequest 92 | { 93 | Address = TokenEndpoint, 94 | 95 | ClientId = "client", 96 | ClientSecret = "secret", 97 | 98 | RefreshToken = "xyz" 99 | }); 100 | ``` 101 | 102 | ## 4.6 请求设备令牌 103 | 该方法具有方便`requestclientcredentialstoken`扩展属性的`urn:ietf:params:oauth:grant-type:device_code`类型 104 | 105 | ``` C# 106 | var response = await client.RequestDeviceTokenAsync(new DeviceTokenRequest 107 | { 108 | Address = disco.TokenEndpoint, 109 | 110 | ClientId = "device", 111 | DeviceCode = authorizeResponse.DeviceCode 112 | }); 113 | ``` -------------------------------------------------------------------------------- /第一部分 协议客户端库/第5章 令牌自省端点(Token Introspection Endpoint).md: -------------------------------------------------------------------------------- 1 | # 第5章 令牌自省端点(Token Introspection Endpoint) 2 | [OAuth 2.0令牌自省](https://tools.ietf.org/html/rfc7662)的客户端库是作为`HttpClient`扩展方法提供的。 3 | 4 | 以下代码将引用令牌发送到内省端点: 5 | 6 | ``` C# 7 | var client = new HttpClient(); 8 | 9 | var response = await client.IntrospectTokenAsync(new TokenIntrospectionRequest 10 | { 11 | Address = "https://demo.identityserver.io/connect/introspect", 12 | ClientId = "api1", 13 | ClientSecret = "secret", 14 | 15 | Token = accessToken 16 | }); 17 | ``` 18 | 19 | 响应属于`IntrospectionResponse`类型并具有标准响应参数的属性。您还可以访问原始响应以及解析的JSON文档(通过`Raw`和`Json`属性)。 20 | 21 | 在使用响应之前,您应该始终检查`IsError`属性以确保请求成功: 22 | 23 | ``` C# 24 | if (response.IsError) throw new Exception(response.Error); 25 | 26 | var isActive = response.IsActive; 27 | var claims = response.Claims; 28 | ``` -------------------------------------------------------------------------------- /第一部分 协议客户端库/第6章 令牌撤销端点(Token Revocation Endpoint).md: -------------------------------------------------------------------------------- 1 | # 第6章 令牌撤销端点(Token Revocation Endpoint) 2 | OAuth 2.0令牌撤销的客户端库是作为扩展方法提供的HttpClient。 3 | 4 | 以下代码撤消撤销端点处的访问令牌令牌: 5 | 6 | ``` C# 7 | var client = new HttpClient(); 8 | 9 | var result = await client.RevokeTokenAsync(new TokenRevocationRequest 10 | { 11 | Address = "https://demo.identityserver.io/connect/revocation", 12 | ClientId = "client", 13 | ClientSecret = "secret", 14 | 15 | Token = accessToken 16 | }); 17 | ``` 18 | 19 | 响应属于`TokenRevocationResponse`类型使您可以访问原始响应以及解析的JSON文档(通过`Raw`和`Json`属性)。 20 | 21 | 在使用响应之前,您应该始终检查`IsError`属性以确保请求成功: 22 | 23 | ``` C# 24 | if (response.IsError) throw new Exception(response.Error); 25 | ``` -------------------------------------------------------------------------------- /第一部分 协议客户端库/第7章 UserInfo端点(UserInfo Endpoint).md: -------------------------------------------------------------------------------- 1 | # 第7章 UserInfo端点(UserInfo Endpoint) 2 | OpenID Connect UserInfo端点的客户端库是作为扩展`HttpClient`方法提供的。 3 | 4 | 以下代码将访问令牌发送到UserInfo端点: 5 | 6 | ``` C# 7 | var client = new HttpClient(); 8 | 9 | var response = await client.GetUserInfoAsync(new UserInfoRequest 10 | { 11 | Address = disco.UserInfoEndpoint, 12 | Token = token 13 | }); 14 | ``` 15 | 16 | 响应属于`UserInfoResponse`类型并具有标准响应参数的属性。您还可以访问原始响应以及解析的JSON文档(通过`Raw`和`Json`属性)。 17 | 18 | 在使用响应之前,您应该始终检查IsError属性以确保请求成功: 19 | 20 | ``` C# 21 | if (response.IsError) throw new Exception(response.Error); 22 | 23 | var claims = response.Claims; 24 | ``` -------------------------------------------------------------------------------- /第一部分 协议客户端库/第8章 动态客户端注册.md: -------------------------------------------------------------------------------- 1 | # 第8章 动态客户端注册 2 | [OpenID Connect动态客户端注册](https://openid.net/specs/openid-connect-registration-1_0.html)的客户端库是作为`HttpClient`扩展方法提供的。 3 | 4 | 以下代码发送注册请求: 5 | 6 | ``` C# 7 | var client = new HttpClient(); 8 | 9 | var response = await client.RegisterClientAsync(new DynamicClientRegistrationRequest 10 | { 11 | Address = Endpoint, 12 | RegistrationRequest = new RegistrationRequest 13 | { 14 | RedirectUris = { redirectUri }, 15 | ApplicationType = "native" 16 | } 17 | }); 18 | ``` 19 | 20 | > **注意** 21 | RegistrationRequest类已经强类型用于所有标准登记参数性质如由规范定义。如果要添加自定义参数,建议从此类派生并添加自己的属性。 22 | 23 | 响应属于`RegistrationResponse`类型并具有标准响应参数的属性。您还可以访问原始响应以及解析的JSON文档(通过`Raw`和`Json`属性)。 24 | 25 | 在使用响应之前,您应该始终检查`IsError`属性以确保请求成功: 26 | 27 | ``` C# 28 | if (response.IsError) throw new Exception(response.Error); 29 | 30 | var clientId = response.ClientId; 31 | var secret = resopnse.ClientSecret; 32 | ``` -------------------------------------------------------------------------------- /第一部分 协议客户端库/第9章 设备授权端点(Device Authorization Endpoint).md: -------------------------------------------------------------------------------- 1 | # 第9章 设备授权端点(Device Authorization Endpoint) 2 | [OAuth 2.0设备流](https://tools.ietf.org/html/rfc7662)设备授权的客户端库是作为`HttpClient`扩展方法提供的。 3 | 4 | 以下代码发送设备授权请求: 5 | 6 | ``` C# 7 | var client = new HttpClient(); 8 | 9 | var response = await client.RequestDeviceAuthorizationAsync(new DeviceAuthorizationRequest 10 | { 11 | Address = "https://demo.identityserver.io/connect/device_authorize", 12 | ClientId = "device" 13 | }); 14 | ``` 15 | 16 | 响应属于`DeviceAuthorizationResponse`类型并具有标准响应参数的属性。您还可以访问原始响应以及解析的JSON文档(通过`Raw`和`Json`属性)。 17 | 18 | 在使用响应之前,您应该始终检查IsError属性以确保请求成功: 19 | 20 | ``` C# 21 | if (response.IsError) throw new Exception(response.Error); 22 | 23 | var userCode = response.UserCode; 24 | var deviceCode = response.DeviceCode; 25 | var verificationUrl = response.VerificationUri; 26 | var verificationUrlComplete = response.VerificationUriComplete; 27 | ``` -------------------------------------------------------------------------------- /第二部分 杂项助手/第10章 协议和声明类型常量.md: -------------------------------------------------------------------------------- 1 | # 第10章 协议和声明类型常量 2 | 使用OAuth 2.0,OpenID Connect和声明时,声明类型和protocoal值有很多“魔术字符串”。IdentityModel提供了几个常量字符串类来帮助它。 3 | 4 | ## 10.1 OAuth 2.0和OpenID Connect协议值 5 | 本`OidcConstants`类有补助的类型,参数名称,错误名称等所有的值 6 | 7 | ## 10.2 JWT声明类型 8 | 本`JwtClaimTypes`类在OpenID Connect,JWT和OAuth 2.0规范中的所有标准要求的类型-其中许多人也都聚集在IANA。 -------------------------------------------------------------------------------- /第二部分 杂项助手/第11章 创建请求URLs.md: -------------------------------------------------------------------------------- 1 | # 第11章 创建请求URLs 2 | 该`RequestUrl`是创建与查询字符串参数,例如URL的帮手: 3 | 4 | ``` C# 5 | var ru = new RequestUrl("https://server/endpoint"); 6 | 7 | // produces https://server/endpoint?foo=foo&bar=bar 8 | var url = ru.Create(new 9 | { 10 | foo: "foo", 11 | bar: "bar" 12 | }); 13 | ``` 14 | 15 | 作为`Create`方法的参数,您可以传入对象或字符串字典。在这两种情况下,属性/值都将序列化为键/值对。 16 | 17 | > **注意** 18 | 所有值都将进行URL编码。 19 | 20 | `RequestUrl`在创建用于建模特定于域的URL结构的扩展方法时,它非常有用。有关示例,请参阅[Authorize Endpoint](https://github.com/thinksjay/IdentityModel/blob/master/%E7%AC%AC%E4%B8%80%E9%83%A8%E5%88%86%20%E5%8D%8F%E8%AE%AE%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%BA%93/%E7%AC%AC2%E7%AB%A0%20%E6%8E%88%E6%9D%83%E7%AB%AF%E7%82%B9(Authorize%20Endpoint).md)和[EndSession Endpoint](https://github.com/thinksjay/IdentityModel/blob/master/%E7%AC%AC%E4%B8%80%E9%83%A8%E5%88%86%20%E5%8D%8F%E8%AE%AE%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%BA%93/%E7%AC%AC3%E7%AB%A0%20%E7%BB%93%E6%9D%9F%E4%BC%9A%E8%AF%9D%E7%AB%AF%E7%82%B9(EndSession%20Point).md)。 -------------------------------------------------------------------------------- /第二部分 杂项助手/第12章 X.509证书库的Fluent API.md: -------------------------------------------------------------------------------- 1 | # 第12章 X.509证书库的Fluent API 2 | 存储X.509证书的常见位置是Windows X.509证书存储区。商店的原始API有点神秘(在.NET Framework和.NET Core之间也略有变化)。 3 | 4 | `X509`类是一个简化的API从所述存储区加载证书。以下代码从个人计算机存储按名称加载证书: 5 | ``` C# 6 | var cert = X509 7 | .LocalMachine 8 | .My 9 | .SubjectDistinguishedName 10 | .Find("CN=sts") 11 | .FirstOrDefault(); 12 | ``` 13 | 14 | 您可以从计算机或用户存储和`My`,`AddressBook`,`TrustedPeople`, `CertificateAuthority`和`TrustedPublisher`分别加载证书。您可以搜索主题名称,指纹,发行人名称或序列号。 15 | 16 | -------------------------------------------------------------------------------- /第二部分 杂项助手/第13章 Base64 URL编码.md: -------------------------------------------------------------------------------- 1 | # 第13章 Base64 URL编码 2 | JWT令牌使用Base64 URL编码进行序列化。 3 | 4 | IdentityModel包括`Base64Url`帮助编码/解码的类: 5 | 6 | ``` C# 7 | var text = "hello"; 8 | var b64url = Base64Url.Encode(text); 9 | 10 | text = Base64Url.Decode(b64url); 11 | ``` 12 | 13 | > **注意** 14 | ASP.NET Core通过[WebEncoders.Base64UrlEncode](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.webutilities.webencoders.base64urlencode)和[WebEncoders.Base64UrlDecode](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.webutilities.webencoders.base64urldecode)提供内置支持。 -------------------------------------------------------------------------------- /第二部分 杂项助手/第14章 纪元时间转换.md: -------------------------------------------------------------------------------- 1 | # 第14章 纪元时间转换 2 | JWT令牌使用所谓的[Epoch或Unix](https://en.wikipedia.org/wiki/Unix_time)时间来表示日期/时间。 3 | 4 | IdentityModel包含用于`DateTime`和`DateTimeOffset`转换到/来自Unix时间的扩展方法: 5 | 6 | ``` C# 7 | var dt = DateTime.UtcNow; 8 | var unix = dt.ToEpochTime(); 9 | ``` 10 | 11 | > **注意** 12 | 从.NET Framework 4.6和.NET Core 1.0开始,此功能通过[DateTimeOffset.FromUnixTimeSeconds](https://docs.microsoft.com/en-us/dotnet/api/system.datetimeoffset.fromunixtimeseconds)和[DateTimeOffset.ToUnixTimeSeconds](https://docs.microsoft.com/en-us/dotnet/api/system.datetimeoffset.tounixtimeseconds)内置。 -------------------------------------------------------------------------------- /第二部分 杂项助手/第15章 时间常数字符串比较.md: -------------------------------------------------------------------------------- 1 | # 第15章 时间常数字符串比较 2 | 在比较安全上下文中的字符串(例如比较键)时,应尽量避免泄漏时序信息。 3 | 4 | 该`TimeConstantComparer`类可以帮助: 5 | 6 | ``` C# 7 | var isEqual = TimeConstantComparer.IsEqual(key1, key2); 8 | ``` 9 | 10 | > **注意** 11 | 从.NET Core 2.1开始,此功能通过[CryptographicOperations.FixedTimeEquals](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicoperations.fixedtimeequals?view=netcore-2.1)内置 --------------------------------------------------------------------------------