<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>身份验证与授权 &#8211; 岁月细碎点滴快查</title>
	<atom:link href="https://blog.mutadecheng.com/category/%e8%ba%ab%e4%bb%bd%e9%aa%8c%e8%af%81%e4%b8%8e%e6%8e%88%e6%9d%83/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.mutadecheng.com</link>
	<description></description>
	<lastBuildDate>Wed, 18 Jun 2025 01:45:47 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9</generator>
	<item>
		<title>C# WebAPI Token 生成与账号密码登录实现</title>
		<link>https://blog.mutadecheng.com/2025/06/18/c-webapi-token-%e7%94%9f%e6%88%90%e4%b8%8e%e8%b4%a6%e5%8f%b7%e5%af%86%e7%a0%81%e7%99%bb%e5%bd%95%e5%ae%9e%e7%8e%b0/</link>
		
		<dc:creator><![CDATA[木它]]></dc:creator>
		<pubDate>Wed, 18 Jun 2025 01:45:45 +0000</pubDate>
				<category><![CDATA[c#]]></category>
		<category><![CDATA[身份验证与授权]]></category>
		<guid isPermaLink="false">https://blog.mutadecheng.com/?p=336</guid>

					<description><![CDATA[此为一个简单的且完整的 C# WebAPI 实现，用于基于账号密码的 Token 生成和验证系统。这个实现使用 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>此为一个简单的且完整的 C# WebAPI 实现，用于基于账号密码的 Token 生成和验证系统。这个实现使用 JWT (JSON Web Token) 作为认证机制。</p>



<h2 class="wp-block-heading">1. 创建项目</h2>



<p>首先创建一个 ASP.NET Core WebAPI 项目。</p>



<h2 class="wp-block-heading">2. 安装必要 NuGet 包</h2>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="Microsoft.AspNetCore.Authentication.JwtBearer
System.IdentityModel.Tokens.Jwt
Microsoft.EntityFrameworkCore (如果使用数据库)
Microsoft.EntityFrameworkCore.SqlServer (如果使用SQL Server)" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #9CDCFE">Microsoft</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">AspNetCore</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Authentication</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">JwtBearer</span></span>
<span class="line"><span style="color: #9CDCFE">System</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">IdentityModel</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Tokens</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Jwt</span></span>
<span class="line"><span style="color: #9CDCFE">Microsoft</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">EntityFrameworkCore</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">如果使用数据库</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #9CDCFE">Microsoft</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">EntityFrameworkCore</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">SqlServer</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">如果使用SQL</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Server</span><span style="color: #D4D4D4">)</span></span></code></pre></div>



<h2 class="wp-block-heading">3. 完整实现代码</h2>



<h3 class="wp-block-heading">3.1 配置类 (appsettings.json)</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="{
  &quot;Jwt&quot;: {
    &quot;Key&quot;: &quot;YourSuperSecretKeyHereAtLeast32CharactersLong&quot;,
    &quot;Issuer&quot;: &quot;YourAppIssuer&quot;,
    &quot;Audience&quot;: &quot;YourAppAudience&quot;,
    &quot;ExpireMinutes&quot;: 60
  },
  &quot;Logging&quot;: {
    &quot;LogLevel&quot;: {
      &quot;Default&quot;: &quot;Information&quot;,
      &quot;Microsoft.AspNetCore&quot;: &quot;Warning&quot;
    }
  },
  &quot;AllowedHosts&quot;: &quot;*&quot;
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #CE9178">&quot;Jwt&quot;</span><span style="color: #D4D4D4">: {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;Key&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;YourSuperSecretKeyHereAtLeast32CharactersLong&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;Issuer&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;YourAppIssuer&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;Audience&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;YourAppAudience&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;ExpireMinutes&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #B5CEA8">60</span></span>
<span class="line"><span style="color: #D4D4D4">  },</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #CE9178">&quot;Logging&quot;</span><span style="color: #D4D4D4">: {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;LogLevel&quot;</span><span style="color: #D4D4D4">: {</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #CE9178">&quot;Default&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;Information&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #CE9178">&quot;Microsoft.AspNetCore&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;Warning&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">  },</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #CE9178">&quot;AllowedHosts&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;*&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">3.2 用户模型 (Models/User.cs)</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="public class User
{
    public int Id { get; set; }
    public string Username { get; set; }
    public string Password { get; set; } // 实际应用中应该存储哈希值
    public string Role { get; set; } // 例如 &quot;Admin&quot;, &quot;User&quot; 等
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">User</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">int</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Id</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">get</span><span style="color: #D4D4D4">; </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4">; }</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Username</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">get</span><span style="color: #D4D4D4">; </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4">; }</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Password</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">get</span><span style="color: #D4D4D4">; </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4">; } </span><span style="color: #6A9955">// 实际应用中应该存储哈希值</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Role</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">get</span><span style="color: #D4D4D4">; </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4">; } </span><span style="color: #6A9955">// 例如 &quot;Admin&quot;, &quot;User&quot; 等</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">3.3 Token 响应模型 (Models/TokenResponse.cs)</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="public class TokenResponse
{
    public string Token { get; set; }
    public DateTime Expiration { get; set; }
    public string Username { get; set; }
    public string Role { get; set; }
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TokenResponse</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Token</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">get</span><span style="color: #D4D4D4">; </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4">; }</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">DateTime</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Expiration</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">get</span><span style="color: #D4D4D4">; </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4">; }</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Username</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">get</span><span style="color: #D4D4D4">; </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4">; }</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Role</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">get</span><span style="color: #D4D4D4">; </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4">; }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">3.4 登录请求模型 (Models/LoginRequest.cs)</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="public class LoginRequest
{
    public string Username { get; set; }
    public string Password { get; set; }
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">LoginRequest</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Username</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">get</span><span style="color: #D4D4D4">; </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4">; }</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Password</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">get</span><span style="color: #D4D4D4">; </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4">; }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">3.5 JWT 服务 (Services/JwtService.cs)</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;

public class JwtService
{
    private readonly IConfiguration _configuration;

    public JwtService(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    public string GenerateToken(User user)
    {
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration[&quot;Jwt:Key&quot;]));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        var claims = new[]
        {
            new Claim(ClaimTypes.NameIdentifier, user.Username),
            new Claim(ClaimTypes.Role, user.Role)
        };

        var token = new JwtSecurityToken(
            _configuration[&quot;Jwt:Issuer&quot;],
            _configuration[&quot;Jwt:Audience&quot;],
            claims,
            expires: DateTime.Now.AddMinutes(Convert.ToDouble(_configuration[&quot;Jwt:ExpireMinutes&quot;])),
            signingCredentials: credentials);

        return new JwtSecurityTokenHandler().WriteToken(token);
    }
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C586C0">using</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">System</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">IdentityModel</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">Tokens</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">Jwt</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #C586C0">using</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">System</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">Security</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">Claims</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #C586C0">using</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">System</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">Text</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #C586C0">using</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Microsoft</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">IdentityModel</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">Tokens</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">JwtService</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">readonly</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IConfiguration</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">_configuration</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">JwtService</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">IConfiguration</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">configuration</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">_configuration</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">configuration</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">GenerateToken</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">User</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">securityKey</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">SymmetricSecurityKey</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">Encoding</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">UTF8</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetBytes</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">_configuration</span><span style="color: #D4D4D4">[</span><span style="color: #CE9178">&quot;Jwt:Key&quot;</span><span style="color: #D4D4D4">]));</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">credentials</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">SigningCredentials</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">securityKey</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">SecurityAlgorithms</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">HmacSha256</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">claims</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4">[]</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Claim</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">ClaimTypes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">NameIdentifier</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Username</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Claim</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">ClaimTypes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Role</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Role</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">        };</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">token</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">JwtSecurityToken</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">_configuration</span><span style="color: #D4D4D4">[</span><span style="color: #CE9178">&quot;Jwt:Issuer&quot;</span><span style="color: #D4D4D4">],</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">_configuration</span><span style="color: #D4D4D4">[</span><span style="color: #CE9178">&quot;Jwt:Audience&quot;</span><span style="color: #D4D4D4">],</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">claims</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">expires</span><span style="color: #D4D4D4">: </span><span style="color: #9CDCFE">DateTime</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Now</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddMinutes</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">Convert</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">ToDouble</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">_configuration</span><span style="color: #D4D4D4">[</span><span style="color: #CE9178">&quot;Jwt:ExpireMinutes&quot;</span><span style="color: #D4D4D4">])),</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">signingCredentials</span><span style="color: #D4D4D4">: </span><span style="color: #9CDCFE">credentials</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">JwtSecurityTokenHandler</span><span style="color: #D4D4D4">().</span><span style="color: #DCDCAA">WriteToken</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">token</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">3.6 用户服务 (Services/UserService.cs)</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="public class UserService
{
    // 实际应用中应该从数据库获取用户
    private readonly List<User&gt; _users = new List<User&gt;
    {
        new User { Id = 1, Username = &quot;admin&quot;, Password = &quot;admin123&quot;, Role = &quot;Admin&quot; },
        new User { Id = 2, Username = &quot;user&quot;, Password = &quot;user123&quot;, Role = &quot;User&quot; }
    };

    public User Authenticate(string username, string password)
    {
        // 实际应用中应该验证密码哈希
        var user = _users.SingleOrDefault(x =&gt; x.Username == username &amp;&amp; x.Password == password);

        // 如果用户不存在或密码错误返回null
        if (user == null)
            return null;

        // 认证成功返回用户详情 (不包含密码)
        return user;
    }
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">UserService</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #6A9955">    // 实际应用中应该从数据库获取用户</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">readonly</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">List</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">User</span><span style="color: #D4D4D4">&gt; </span><span style="color: #9CDCFE">_users</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">List</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">User</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">User</span><span style="color: #D4D4D4"> { </span><span style="color: #9CDCFE">Id</span><span style="color: #D4D4D4"> = </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">Username</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;admin&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">Password</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;admin123&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">Role</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;Admin&quot;</span><span style="color: #D4D4D4"> },</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">User</span><span style="color: #D4D4D4"> { </span><span style="color: #9CDCFE">Id</span><span style="color: #D4D4D4"> = </span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">Username</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;user&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">Password</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;user123&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">Role</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;User&quot;</span><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">User</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Authenticate</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">username</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">password</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #6A9955">        // 实际应用中应该验证密码哈希</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">_users</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">SingleOrDefault</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">x</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #9CDCFE">x</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Username</span><span style="color: #D4D4D4"> == </span><span style="color: #9CDCFE">username</span><span style="color: #D4D4D4"> &amp;&amp; </span><span style="color: #9CDCFE">x</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Password</span><span style="color: #D4D4D4"> == </span><span style="color: #9CDCFE">password</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">        // 如果用户不存在或密码错误返回null</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4"> == </span><span style="color: #569CD6">null</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">null</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">        // 认证成功返回用户详情 (不包含密码)</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">3.7 认证控制器 (Controllers/AuthController.cs)</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="using Microsoft.AspNetCore.Mvc;

[Route(&quot;api/[controller]&quot;)]
[ApiController]
public class AuthController : ControllerBase
{
    private readonly UserService _userService;
    private readonly JwtService _jwtService;

    public AuthController(UserService userService, JwtService jwtService)
    {
        _userService = userService;
        _jwtService = jwtService;
    }

    [HttpPost(&quot;login&quot;)]
    public IActionResult Login([FromBody] LoginRequest request)
    {
        var user = _userService.Authenticate(request.Username, request.Password);

        if (user == null)
            return Unauthorized(new { message = &quot;用户名或密码错误&quot; });

        var token = _jwtService.GenerateToken(user);

        return Ok(new TokenResponse
        {
            Token = token,
            Expiration = DateTime.Now.AddMinutes(Convert.ToDouble(_configuration[&quot;Jwt:ExpireMinutes&quot;])),
            Username = user.Username,
            Role = user.Role
        });
    }
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C586C0">using</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Microsoft</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">AspNetCore</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">Mvc</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">[</span><span style="color: #4EC9B0">Route</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;api/[controller]&quot;</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #D4D4D4">[</span><span style="color: #4EC9B0">ApiController</span><span style="color: #D4D4D4">]</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">AuthController</span><span style="color: #D4D4D4"> : </span><span style="color: #4EC9B0">ControllerBase</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">readonly</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">UserService</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">_userService</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">readonly</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">JwtService</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">_jwtService</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">AuthController</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">UserService</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">userService</span><span style="color: #D4D4D4">, </span><span style="color: #4EC9B0">JwtService</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">jwtService</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">_userService</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">userService</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">_jwtService</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">jwtService</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    [</span><span style="color: #4EC9B0">HttpPost</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;login&quot;</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IActionResult</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Login</span><span style="color: #D4D4D4">([</span><span style="color: #4EC9B0">FromBody</span><span style="color: #D4D4D4">] </span><span style="color: #4EC9B0">LoginRequest</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">request</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">_userService</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Authenticate</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">request</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Username</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">request</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Password</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4"> == </span><span style="color: #569CD6">null</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Unauthorized</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> { </span><span style="color: #9CDCFE">message</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;用户名或密码错误&quot;</span><span style="color: #D4D4D4"> });</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">token</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">_jwtService</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GenerateToken</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Ok</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TokenResponse</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">Token</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">token</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">Expiration</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">DateTime</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Now</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddMinutes</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">Convert</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">ToDouble</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">_configuration</span><span style="color: #D4D4D4">[</span><span style="color: #CE9178">&quot;Jwt:ExpireMinutes&quot;</span><span style="color: #D4D4D4">])),</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">Username</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Username</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">Role</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Role</span></span>
<span class="line"><span style="color: #D4D4D4">        });</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">3.8 受保护资源控制器 (Controllers/ProtectedController.cs)</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="using Microsoft.AspNetCore.Authorization;

[Authorize]
[Route(&quot;api/[controller]&quot;)]
[ApiController]
public class ProtectedController : ControllerBase
{
    [HttpGet]
    public IActionResult Get()
    {
        // 可以从HttpContext.User获取用户信息
        var username = User.Identity.Name;
        var role = User.FindFirst(ClaimTypes.Role)?.Value;

        return Ok(new { 
            Message = $&quot;你好 {username}, 你的角色是 {role}&quot;,
            Data = &quot;这是受保护的数据&quot; 
        });
    }

    [Authorize(Roles = &quot;Admin&quot;)]
    [HttpGet(&quot;admin&quot;)]
    public IActionResult AdminOnly()
    {
        return Ok(new { Message = &quot;只有管理员能看到这条消息&quot; });
    }
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C586C0">using</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Microsoft</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">AspNetCore</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">Authorization</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">[</span><span style="color: #4EC9B0">Authorize</span><span style="color: #D4D4D4">]</span></span>
<span class="line"><span style="color: #D4D4D4">[</span><span style="color: #4EC9B0">Route</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;api/[controller]&quot;</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #D4D4D4">[</span><span style="color: #4EC9B0">ApiController</span><span style="color: #D4D4D4">]</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">ProtectedController</span><span style="color: #D4D4D4"> : </span><span style="color: #4EC9B0">ControllerBase</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    [</span><span style="color: #4EC9B0">HttpGet</span><span style="color: #D4D4D4">]</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IActionResult</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Get</span><span style="color: #D4D4D4">()</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #6A9955">        // 可以从HttpContext.User获取用户信息</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">username</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">User</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Identity</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Name</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">role</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">User</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">FindFirst</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">ClaimTypes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Role</span><span style="color: #D4D4D4">)?.</span><span style="color: #9CDCFE">Value</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Ok</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> { </span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">Message</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">$&quot;你好 {</span><span style="color: #9CDCFE">username</span><span style="color: #CE9178">}, 你的角色是 {</span><span style="color: #9CDCFE">role</span><span style="color: #CE9178">}&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">Data</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;这是受保护的数据&quot;</span><span style="color: #D4D4D4"> </span></span>
<span class="line"><span style="color: #D4D4D4">        });</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    [</span><span style="color: #4EC9B0">Authorize</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">Roles</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;Admin&quot;</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #D4D4D4">    [</span><span style="color: #4EC9B0">HttpGet</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;admin&quot;</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IActionResult</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">AdminOnly</span><span style="color: #D4D4D4">()</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Ok</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> { </span><span style="color: #9CDCFE">Message</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;只有管理员能看到这条消息&quot;</span><span style="color: #D4D4D4"> });</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">3.9 Program.cs 配置</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

// 添加服务到容器
builder.Services.AddControllers();

// 配置JWT认证
builder.Services.AddAuthentication(options =&gt;
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =&gt;
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = builder.Configuration[&quot;Jwt:Issuer&quot;],
        ValidAudience = builder.Configuration[&quot;Jwt:Audience&quot;],
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration[&quot;Jwt:Key&quot;]))
    };
});

// 注册自定义服务
builder.Services.AddScoped<UserService&gt;();
builder.Services.AddScoped<JwtService&gt;();

var app = builder.Build();

// 配置HTTP请求管道
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.UseHttpsRedirection();

// 添加认证和授权中间件
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C586C0">using</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Microsoft</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">AspNetCore</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">Authentication</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">JwtBearer</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #C586C0">using</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Microsoft</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">IdentityModel</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">Tokens</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #C586C0">using</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">System</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">Text</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">WebApplication</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">CreateBuilder</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">args</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">// 添加服务到容器</span></span>
<span class="line"><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Services</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddControllers</span><span style="color: #D4D4D4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">// 配置JWT认证</span></span>
<span class="line"><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Services</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddAuthentication</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4"> =&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">DefaultAuthenticateScheme</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">JwtBearerDefaults</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">AuthenticationScheme</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">DefaultChallengeScheme</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">JwtBearerDefaults</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">AuthenticationScheme</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">})</span></span>
<span class="line"><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddJwtBearer</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4"> =&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">TokenValidationParameters</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TokenValidationParameters</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">ValidateIssuer</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">ValidateAudience</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">ValidateLifetime</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">ValidateIssuerSigningKey</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">ValidIssuer</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Configuration</span><span style="color: #D4D4D4">[</span><span style="color: #CE9178">&quot;Jwt:Issuer&quot;</span><span style="color: #D4D4D4">],</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">ValidAudience</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Configuration</span><span style="color: #D4D4D4">[</span><span style="color: #CE9178">&quot;Jwt:Audience&quot;</span><span style="color: #D4D4D4">],</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">IssuerSigningKey</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">SymmetricSecurityKey</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">Encoding</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">UTF8</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetBytes</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Configuration</span><span style="color: #D4D4D4">[</span><span style="color: #CE9178">&quot;Jwt:Key&quot;</span><span style="color: #D4D4D4">]))</span></span>
<span class="line"><span style="color: #D4D4D4">    };</span></span>
<span class="line"><span style="color: #D4D4D4">});</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">// 注册自定义服务</span></span>
<span class="line"><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Services</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddScoped</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">UserService</span><span style="color: #D4D4D4">&gt;();</span></span>
<span class="line"><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Services</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddScoped</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">JwtService</span><span style="color: #D4D4D4">&gt;();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Build</span><span style="color: #D4D4D4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">// 配置HTTP请求管道</span></span>
<span class="line"><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Environment</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">IsDevelopment</span><span style="color: #D4D4D4">())</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseDeveloperExceptionPage</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseHttpsRedirection</span><span style="color: #D4D4D4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">// 添加认证和授权中间件</span></span>
<span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseAuthentication</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseAuthorization</span><span style="color: #D4D4D4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">MapControllers</span><span style="color: #D4D4D4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Run</span><span style="color: #D4D4D4">();</span></span></code></pre></div>



<h2 class="wp-block-heading">4. 使用说明</h2>



<h3 class="wp-block-heading">4.1 获取Token</h3>



<p>发送POST请求到 <code>/api/auth/login</code>，请求体为：</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="{
    &quot;username&quot;: &quot;admin&quot;,
    &quot;password&quot;: &quot;admin123&quot;
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;username&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;admin&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;password&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;admin123&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p>成功响应示例：</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="{
    &quot;token&quot;: &quot;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...&quot;,
    &quot;expiration&quot;: &quot;2023-05-01T12:00:00&quot;,
    &quot;username&quot;: &quot;admin&quot;,
    &quot;role&quot;: &quot;Admin&quot;
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;token&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;expiration&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;2023-05-01T12:00:00&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;username&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;admin&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;role&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;Admin&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">4.2 访问受保护资源</h3>



<p>在请求头中添加：</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="Authorization: Bearer <your_token&gt;" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C8C8C8">Authorization</span><span style="color: #D4D4D4">: </span><span style="color: #9CDCFE">Bearer</span><span style="color: #D4D4D4"> &lt;</span><span style="color: #9CDCFE">your_token</span><span style="color: #D4D4D4">&gt;</span></span></code></pre></div>



<p>然后访问 <code>/api/protected</code></p>



<h3 class="wp-block-heading">4.3 访问管理员资源</h3>



<p>在请求头中添加：</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="Authorization: Bearer <your_token&gt;" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C8C8C8">Authorization</span><span style="color: #D4D4D4">: </span><span style="color: #9CDCFE">Bearer</span><span style="color: #D4D4D4"> &lt;</span><span style="color: #9CDCFE">your_token</span><span style="color: #D4D4D4">&gt;</span></span></code></pre></div>



<p>然后访问 <code>/api/protected/admin</code> (需要管理员角色)</p>



<h2 class="wp-block-heading">5. 安全建议</h2>



<ol class="wp-block-list">
<li><strong>密码存储</strong>：实际应用中不要明文存储密码，应该使用密码哈希加盐</li>



<li><strong>HTTPS</strong>：确保所有认证请求都通过HTTPS</li>



<li><strong>Token过期</strong>：设置合理的Token过期时间</li>



<li><strong>密钥保护</strong>：保护JWT密钥，不要硬编码在代码中</li>



<li><strong>刷新Token</strong>：考虑实现刷新Token机制</li>
</ol>



<h2 class="wp-block-heading">6. 扩展功能</h2>



<ol class="wp-block-list">
<li>添加用户注册功能</li>



<li>实现密码重置</li>



<li>添加多因素认证</li>



<li>实现Token刷新机制</li>



<li>添加日志记录和监控</li>
</ol>



<p> </p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Microsoft.IdentityModel.Tokens 命名空间中的 TokenValidationParameters 类</title>
		<link>https://blog.mutadecheng.com/2025/04/23/microsoft-identitymodel-tokens-%e5%91%bd%e5%90%8d%e7%a9%ba%e9%97%b4%e4%b8%ad%e7%9a%84-tokenvalidationparameters-%e7%b1%bb/</link>
		
		<dc:creator><![CDATA[木它]]></dc:creator>
		<pubDate>Wed, 23 Apr 2025 01:38:55 +0000</pubDate>
				<category><![CDATA[c#]]></category>
		<category><![CDATA[身份验证与授权]]></category>
		<guid isPermaLink="false">https://blog.mutadecheng.com/?p=316</guid>

					<description><![CDATA[Microsoft.IdentityModel.Tokens 命名空间中的 TokenValidationPa [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p><code>Microsoft.IdentityModel.Tokens</code> 命名空间中的 <code>TokenValidationParameters</code> 类，用于配置和控制令牌验证的行为。以下是每个参数的含义和作用, 包括其在 JWT 令牌验证过程中的具体作用和可能的使用场景： </p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Token Validation Parameters</h3>



<h4 class="wp-block-heading">1. AlgorithmValidator</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>Func&lt;string, SecurityToken, TokenValidationParameters, bool></code></li>



<li><strong>作用</strong>: 验证令牌签名算法是否符合预期。</li>



<li><strong>使用场景</strong>: 限制使用不安全的算法，确保符合安全策略。</li>
</ul>



<h4 class="wp-block-heading">2. ActorValidationParameters</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>TokenValidationParameters</code></li>



<li><strong>作用</strong>: 提供用于验证嵌套 actor token 的参数。</li>



<li><strong>使用场景</strong>: 在 OAuth 2.0 委托场景中验证代表用户的服务。</li>
</ul>



<h4 class="wp-block-heading">3. AudienceValidator</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>Func&lt;IEnumerable&lt;string>, SecurityToken, TokenValidationParameters, bool></code></li>



<li><strong>作用</strong>: 自定义受众验证逻辑。</li>



<li><strong>使用场景</strong>: 实现复杂的受众验证规则。</li>
</ul>



<h4 class="wp-block-heading">4. _authenticationType</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>string</code></li>



<li><strong>作用</strong>: 标识身份验证类型。</li>



<li><strong>使用场景</strong>: 用于标记或区分不同的身份验证过程。</li>
</ul>



<h4 class="wp-block-heading">5. ClockSkew</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>TimeSpan</code></li>



<li><strong>作用</strong>: 定义允许的时间偏移量。</li>



<li><strong>使用场景</strong>: 解决服务器间时钟不同步的问题。</li>
</ul>



<h4 class="wp-block-heading">6. ConfigurationManager</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>IConfigurationManager&lt;OpenIdConnectConfiguration></code></li>



<li><strong>作用</strong>: 管理和获取动态配置。</li>



<li><strong>使用场景</strong>: 动态获取远程服务器的配置。</li>
</ul>



<h4 class="wp-block-heading">7. CryptoProviderFactory</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>CryptoProviderFactory</code></li>



<li><strong>作用</strong>: 提供加密操作的工厂类。</li>



<li><strong>使用场景</strong>: 创建加密器、解密器、签名器等。</li>
</ul>



<h4 class="wp-block-heading">8. DebugId</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>string</code></li>



<li><strong>作用</strong>: 用于调试的标识符。</li>



<li><strong>使用场景</strong>: 在调试或日志记录过程中，帮助识别和追踪特定的验证请求。</li>
</ul>



<h4 class="wp-block-heading">9. IncludeTokenOnFailedValidation</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>bool</code></li>



<li><strong>作用</strong>: 指示在验证失败时是否包含令牌。</li>



<li><strong>使用场景</strong>: 调试和记录，但需注意安全性。</li>
</ul>



<h4 class="wp-block-heading">10. IgnoreTrailingSlashWhenValidatingAudience</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>bool</code></li>



<li><strong>作用</strong>: 指示在验证受众时是否忽略 URL 的尾部斜杠。</li>



<li><strong>使用场景</strong>: 允许在验证受众时忽略 URL 尾部斜杠的差异。</li>
</ul>



<h4 class="wp-block-heading">11. IssuerSigningKey</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>SecurityKey</code></li>



<li><strong>作用</strong>: 用于验证令牌签名的单个密钥。</li>



<li><strong>使用场景</strong>: 提供签名密钥进行验证。</li>
</ul>



<h4 class="wp-block-heading">12. IssuerSigningKeyResolver</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>Func&lt;string, SecurityToken, string, TokenValidationParameters, IEnumerable&lt;SecurityKey>></code></li>



<li><strong>作用</strong>: 动态解析签名密钥的委托。</li>



<li><strong>使用场景</strong>: 签名密钥动态变化时解析正确的密钥。</li>
</ul>



<h4 class="wp-block-heading">13. IssuerSigningKeyResolverUsingConfiguration</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>Func&lt;string, SecurityToken, string, TokenValidationParameters, IEnumerable&lt;SecurityKey>></code></li>



<li><strong>作用</strong>: 使用配置解析签名密钥的委托。</li>



<li><strong>使用场景</strong>: 依赖配置管理器的动态变化来解析签名密钥。</li>
</ul>



<h4 class="wp-block-heading">14. IssuerSigningKeys</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>IEnumerable&lt;SecurityKey></code></li>



<li><strong>作用</strong>: 用于验证令牌签名的密钥集合。</li>



<li><strong>使用场景</strong>: 当支持多个签名密钥时，提供一个密钥列表供选择。</li>
</ul>



<h4 class="wp-block-heading">15. IssuerSigningKeyValidator</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>Func&lt;SecurityKey, SecurityToken, TokenValidationParameters, bool></code></li>



<li><strong>作用</strong>: 自定义签名密钥验证逻辑。</li>



<li><strong>使用场景</strong>: 对签名密钥进行额外验证（如检查密钥来源）。</li>
</ul>



<h4 class="wp-block-heading">16. IssuerSigningKeyValidatorUsingConfiguration</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>Func&lt;SecurityKey, SecurityToken, TokenValidationParameters, bool></code></li>



<li><strong>作用</strong>: 使用配置的签名密钥验证委托。</li>



<li><strong>使用场景</strong>: 依赖配置进行密钥验证。</li>
</ul>



<h4 class="wp-block-heading">17. IssuerValidator</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>Func&lt;string, SecurityToken, TokenValidationParameters, string></code></li>



<li><strong>作用</strong>: 自定义颁发者验证逻辑。</li>



<li><strong>使用场景</strong>: 实现复杂的颁发者验证规则。</li>
</ul>



<h4 class="wp-block-heading">18. IssuerValidatorAsync</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>Func&lt;string, SecurityToken, TokenValidationParameters, Task&lt;string>></code></li>



<li><strong>作用</strong>: 异步自定义颁发者验证逻辑。</li>



<li><strong>使用场景</strong>: 当颁发者验证涉及异步操作时，使用此委托。</li>
</ul>



<h4 class="wp-block-heading">19. IssuerValidatorUsingConfiguration</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>Func&lt;string, SecurityToken, TokenValidationParameters, string></code></li>



<li><strong>作用</strong>: 使用配置的颁发者验证委托。</li>



<li><strong>使用场景</strong>: 依赖配置的动态变化来验证颁发者。</li>
</ul>



<h4 class="wp-block-heading">20. LifetimeValidator</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>Func&lt;DateTime?, DateTime?, SecurityToken, TokenValidationParameters, bool></code></li>



<li><strong>作用</strong>: 自定义令牌有效期验证逻辑。</li>



<li><strong>使用场景</strong>: 实现特定的有效期验证规则。</li>
</ul>



<h4 class="wp-block-heading">21. LogTokenId</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>bool</code></li>



<li><strong>作用</strong>: 指示是否记录令牌 ID。</li>



<li><strong>使用场景</strong>: 用于审计和日志记录，以便在日志中跟踪特定令牌。</li>
</ul>



<h4 class="wp-block-heading">22. LogValidationExceptions</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>bool</code></li>



<li><strong>作用</strong>: 指示是否记录验证异常。</li>



<li><strong>使用场景</strong>: 用于调试和监控，记录验证过程中发生的异常。</li>
</ul>



<h4 class="wp-block-heading">23. NameClaimType</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>string</code></li>



<li><strong>作用</strong>: 指定从令牌中提取用户名称的声明类型。</li>



<li><strong>使用场景</strong>: 指定哪个声明用于标识用户名称。</li>
</ul>



<h4 class="wp-block-heading">24. NameClaimTypeRetriever</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>Func&lt;IEnumerable&lt;Claim>, string></code></li>



<li><strong>作用</strong>: 自定义名称声明类型的检索逻辑。</li>



<li><strong>使用场景</strong>: 动态决定使用哪个名称声明时，通过此委托实现。</li>
</ul>



<h4 class="wp-block-heading">25. PropertyBag</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>IDictionary&lt;string, object></code></li>



<li><strong>作用</strong>: 用于存储额外属性的字典。</li>



<li><strong>使用场景</strong>: 在验证过程中传递和存储自定义数据，如上下文信息。</li>
</ul>



<h4 class="wp-block-heading">26. RefreshBeforeValidation</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>bool</code></li>



<li><strong>作用</strong>: 指示在验证令牌之前是否刷新配置。</li>



<li><strong>使用场景</strong>: 在配置可能更新的环境中，确保使用最新的配置进行验证。</li>
</ul>



<h4 class="wp-block-heading">27. RequireAudience</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>bool</code></li>



<li><strong>作用</strong>: 指示验证过程中是否需要受众声明。</li>



<li><strong>使用场景</strong>: 确保令牌有明确的受众，防止令牌被误用。</li>
</ul>



<h4 class="wp-block-heading">28. RequireExpirationTime</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>bool</code></li>



<li><strong>作用</strong>: 指示验证过程中是否需要过期时间声明。</li>



<li><strong>使用场景</strong>: 确保令牌有明确的有效期，防止无期限令牌被长期使用。</li>
</ul>



<h4 class="wp-block-heading">29. RequireSignedTokens</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>bool</code></li>



<li><strong>作用</strong>: 指示验证过程中是否需要令牌是签名的。</li>



<li><strong>使用场景</strong>: 确保令牌的完整性和真实性。</li>
</ul>



<h4 class="wp-block-heading">30. RoleClaimType</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>string</code></li>



<li><strong>作用</strong>: 指定从令牌中提取用户角色的声明类型。</li>



<li><strong>使用场景</strong>: 指定哪个声明用于标识用户角色。</li>
</ul>



<h4 class="wp-block-heading">31. RoleClaimTypeRetriever</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>Func&lt;IEnumerable&lt;Claim>, string></code></li>



<li><strong>作用</strong>: 自定义角色声明类型的检索逻辑。</li>



<li><strong>使用场景</strong>: 动态决定使用哪个角色声明时，通过此委托实现。</li>
</ul>



<h4 class="wp-block-heading">32. SaveSigninToken</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>bool</code></li>



<li><strong>作用</strong>: 指示在身份验证成功后是否保存令牌。</li>



<li><strong>使用场景</strong>: 用于后续处理或审计目的，保存成功验证的令牌。</li>
</ul>



<h4 class="wp-block-heading">33. SignatureValidator</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>Func&lt;string, SecurityToken, TokenValidationParameters, SecurityToken></code></li>



<li><strong>作用</strong>: 自定义签名验证逻辑。</li>



<li><strong>使用场景</strong>: 实现特定的签名验证策略。</li>
</ul>



<h4 class="wp-block-heading">34. SignatureValidatorUsingConfiguration</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>Func&lt;string, SecurityToken, TokenValidationParameters, SecurityToken></code></li>



<li><strong>作用</strong>: 使用配置的签名验证委托。</li>



<li><strong>使用场景</strong>: 依赖配置的动态变化来进行签名验证。</li>
</ul>



<h4 class="wp-block-heading">35. TokenDecryptionKey</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>SecurityKey</code></li>



<li><strong>作用</strong>: 用于解密令牌的单个密钥。</li>



<li><strong>使用场景</strong>: 提供解密密钥进行验证。</li>
</ul>



<h4 class="wp-block-heading">36. TokenDecryptionKeyResolver</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>Func&lt;string, SecurityToken, string, TokenValidationParameters, IEnumerable&lt;SecurityKey>></code></li>



<li><strong>作用</strong>: 动态解析解密密钥的委托。</li>



<li><strong>使用场景</strong>: 解密密钥动态变化时，通过此委托解析正确的密钥。</li>
</ul>



<h4 class="wp-block-heading">37. TokenDecryptionKeys</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>IEnumerable&lt;SecurityKey></code></li>



<li><strong>作用</strong>: 用于解密令牌的密钥集合。</li>



<li><strong>使用场景</strong>: 当支持多个解密密钥时，提供一个密钥列表供选择。</li>
</ul>



<h4 class="wp-block-heading">38. TokenReader</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>Func&lt;string, SecurityToken></code></li>



<li><strong>作用</strong>: 自定义令牌读取逻辑。</li>



<li><strong>使用场景</strong>: 在解析和处理令牌时需要特定的读取逻辑。</li>
</ul>



<h4 class="wp-block-heading">39. TokenReplayCache</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>ITokenReplayCache</code></li>



<li><strong>作用</strong>: 用于存储令牌重放信息的缓存。</li>



<li><strong>使用场景</strong>: 防止令牌重放攻击，通过缓存已使用的令牌 ID。</li>
</ul>



<h4 class="wp-block-heading">40. TokenReplayValidator</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>Func&lt;string, DateTime, TokenValidationParameters, bool></code></li>



<li><strong>作用</strong>: 自定义令牌重放验证逻辑。</li>



<li><strong>使用场景</strong>: 需要特定的重放攻击防护策略时，通过此委托实现。</li>
</ul>



<h4 class="wp-block-heading">41. TransformBeforeSignatureValidation</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>Func&lt;string, TokenValidationParameters, string></code></li>



<li><strong>作用</strong>: 在签名验证之前对令牌进行转换。</li>



<li><strong>使用场景</strong>: 需要对令牌进行预处理以便验证时使用。</li>
</ul>



<h4 class="wp-block-heading">42. TryAllIssuerSigningKeys</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>bool</code></li>



<li><strong>作用</strong>: 指示是否尝试使用所有提供的签名密钥进行验证。</li>



<li><strong>使用场景</strong>: 当有多个潜在的签名密钥时，尝试所有可能的密钥进行验证。</li>
</ul>



<h4 class="wp-block-heading">43. TypeValidator</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>Func&lt;string, SecurityToken, TokenValidationParameters, bool></code></li>



<li><strong>作用</strong>: 自定义令牌类型验证逻辑。</li>



<li><strong>使用场景</strong>: 确保令牌类型符合预期，防止不正确类型的令牌被使用。</li>
</ul>



<h4 class="wp-block-heading">44. ValidateActor</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>bool</code></li>



<li><strong>作用</strong>: 指示是否应该验证嵌套令牌。</li>



<li><strong>使用场景</strong>: 在处理嵌套令牌时，确保其有效性和真实性。</li>
</ul>



<h4 class="wp-block-heading">45. ValidateAudience</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>bool</code></li>



<li><strong>作用</strong>: 指示是否应该验证令牌的受众声明。</li>



<li><strong>使用场景</strong>: 确保令牌的受众与预期一致，防止令牌被不当使用。</li>
</ul>



<h4 class="wp-block-heading">46. ValidateIssuer</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>bool</code></li>



<li><strong>作用</strong>: 指示是否应该验证令牌的颁发者声明。</li>



<li><strong>使用场景</strong>: 确保令牌的颁发者是可信的，防止伪造的令牌。</li>
</ul>



<h4 class="wp-block-heading">47. ValidateIssuerSigningKey</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>bool</code></li>



<li><strong>作用</strong>: 指示是否应该验证令牌的签名密钥。</li>



<li><strong>使用场景</strong>: 确保使用正确的密钥进行签名验证，防止签名被伪造。</li>
</ul>



<h4 class="wp-block-heading">48. ValidateLifetime</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>bool</code></li>



<li><strong>作用</strong>: 指示是否应该验证令牌的有效期。</li>



<li><strong>使用场景</strong>: 确保令牌在有效期内使用，防止过期令牌被使用。</li>
</ul>



<h4 class="wp-block-heading">49. ValidateSignatureLast</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>bool</code></li>



<li><strong>作用</strong>: 指示是否应该在最后进行签名验证。</li>



<li><strong>使用场景</strong>: 当有特定的验证顺序需求时，确保签名验证在其他验证之后进行。</li>
</ul>



<h4 class="wp-block-heading">50. ValidateTokenReplay</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>bool</code></li>



<li><strong>作用</strong>: 指示是否应该验证令牌是否被重放。</li>



<li><strong>使用场景</strong>: 防止令牌重放攻击，确保每个令牌仅被使用一次。</li>
</ul>



<h4 class="wp-block-heading">51. ValidateWithLKG</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>bool</code></li>



<li><strong>作用</strong>: 指示是否应该使用最后已知良好的配置进行验证。</li>



<li><strong>使用场景</strong>: 在配置更新失败的情况下，使用最后已知的有效配置进行验证。</li>
</ul>



<h4 class="wp-block-heading">52. ValidAlgorithms</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>IEnumerable&lt;string></code></li>



<li><strong>作用</strong>: 指定允许的签名算法。</li>



<li><strong>使用场景</strong>: 限制令牌签名算法，以确保使用符合安全策略的算法。</li>
</ul>



<h4 class="wp-block-heading">53. ValidAudience</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>string</code></li>



<li><strong>作用</strong>: 指定允许的单个受众。</li>



<li><strong>使用场景</strong>: 确保令牌的受众与预期的单个受众匹配。</li>
</ul>



<h4 class="wp-block-heading">54. ValidAudiences</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>IEnumerable&lt;string></code></li>



<li><strong>作用</strong>: 指定允许的受众集合。</li>



<li><strong>使用场景</strong>: 当令牌可能有多个受众时，提供一个受众列表进行匹配。</li>
</ul>



<h4 class="wp-block-heading">55. ValidIssuer</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>string</code></li>



<li><strong>作用</strong>: 指定允许的单个颁发者。</li>



<li><strong>使用场景</strong>: 确保令牌的颁发者与预期的单个颁发者匹配。</li>
</ul>



<h4 class="wp-block-heading">56. ValidIssuers</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>IEnumerable&lt;string></code></li>



<li><strong>作用</strong>: 指定允许的颁发者集合。</li>



<li><strong>使用场景</strong>: 当令牌可能来自多个颁发者时，提供一个颁发者列表进行匹配。</li>
</ul>



<h4 class="wp-block-heading">57. ValidTypes</h4>



<ul class="wp-block-list">
<li><strong>属性类型</strong>: <code>IEnumerable&lt;string></code></li>



<li><strong>作用</strong>: 指定允许的令牌类型。</li>



<li><strong>使用场景</strong>: 确保令牌类型符合预期，防止不符合要求的令牌被使用。</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>这些参数用于配置和控制令牌验证过程，确保应用程序的安全性和可靠性。通过合理配置这些参数，可以应对不同的业务需求和安全要求。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>身份验证模型  （RBAC）（ABAC）对比</title>
		<link>https://blog.mutadecheng.com/2024/10/25/%e8%ba%ab%e4%bb%bd%e9%aa%8c%e8%af%81%e6%a8%a1%e5%9e%8b-%ef%bc%88rbac%ef%bc%89%ef%bc%88abac%ef%bc%89%e5%af%b9%e6%af%94/</link>
		
		<dc:creator><![CDATA[木它]]></dc:creator>
		<pubDate>Fri, 25 Oct 2024 13:25:28 +0000</pubDate>
				<category><![CDATA[身份验证与授权]]></category>
		<guid isPermaLink="false">https://blog.mutadecheng.com/?p=259</guid>

					<description><![CDATA[RBAC（基于角色的访问控制）和 ABAC（基于属性的访问控制）是两种常见的访问控制模型，它们在定义和管理用户 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>RBAC（基于角色的访问控制）和 ABAC（基于属性的访问控制）是两种常见的访问控制模型，它们在定义和管理用户权限方面具有不同的方法。</p>



<h3 class="wp-block-heading">RBAC（基于角色的访问控制）</h3>



<p><strong>定义</strong>：<br>RBAC通过将权限与角色关联起来，并将这些角色分配给用户，从而控制对资源的访问。角色通常是基于用户在组织中的职责和职能来定义的。</p>



<p><strong>特点</strong>：</p>



<ul class="wp-block-list">
<li><strong>角色</strong>：一组权限的集合。</li>



<li><strong>用户</strong>：可以被分配一个或多个角色。</li>



<li><strong>权限</strong>：与角色关联，而不是直接与用户关联。</li>
</ul>



<p><strong>示例</strong>：<br>假设在一个公司内有三个角色：管理员、编辑和查看者。</p>



<ul class="wp-block-list">
<li><strong>管理员</strong>：可以创建、修改、删除和查看所有文档。</li>



<li><strong>编辑</strong>：可以修改和查看文档，但不能删除。</li>



<li><strong>查看者</strong>：只能查看文档。</li>
</ul>



<p>在这种情况下，用户Alice被分配为“管理员”，用户Bob被分配为“编辑”，而用户Charlie被分配为“查看者”。每个用户根据其角色获得相应的权限。</p>



<h3 class="wp-block-heading">ABAC（基于属性的访问控制）</h3>



<p><strong>定义</strong>：<br>ABAC通过评估一组属性来决定是否授予访问权限。这些属性可以是关于用户、资源、环境或操作的详细信息。</p>



<p><strong>特点</strong>：</p>



<ul class="wp-block-list">
<li><strong>属性</strong>：包括用户属性（如角色、部门）、资源属性（如文件类型、文件所有者）、环境属性（如访问时间、地点）。</li>



<li><strong>策略</strong>：使用这些属性定义复杂的访问控制规则。</li>



<li><strong>灵活性</strong>：可以处理更复杂和动态的访问控制需求。</li>
</ul>



<p><strong>示例</strong>：<br>假设我们有一个系统，使用以下属性来定义访问权限：</p>



<ul class="wp-block-list">
<li>用户属性：角色（如员工、经理）、部门（如销售、财务）</li>



<li>资源属性：文档类型（如机密、公开）</li>



<li>环境属性：访问时间（如工作时间）</li>
</ul>



<p>策略示例：</p>



<ul class="wp-block-list">
<li>只有在工作时间内，销售部门的员工可以访问销售报告。</li>



<li>只有经理可以在任何时间访问机密文件。</li>
</ul>



<p>在这种情况下，用户Alice是销售部门的员工，她只能在工作时间内访问销售报告。用户Bob是经理，他可以在任何时间访问机密文件。</p>



<h3 class="wp-block-heading">比较</h3>



<ul class="wp-block-list">
<li><strong>RBAC</strong>适用于系统角色明确且权限变化不频繁的场景，管理相对简单。</li>



<li><strong>ABAC</strong>适用于需要精细化控制和动态权限管理的场景，灵活性更高，但复杂度也更高。</li>
</ul>



<p>选择使用哪种模型通常取决于组织的需求和复杂性。许多现代系统会结合使用RBAC和ABAC，以便在不同的场景中提供最佳的访问控制解决方案。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Blazor Server 前后端不分离项目简单身份验证</title>
		<link>https://blog.mutadecheng.com/2024/10/25/blazor-server-%e5%89%8d%e5%90%8e%e7%ab%af%e4%b8%8d%e5%88%86%e7%a6%bb%e9%a1%b9%e7%9b%ae%e7%ae%80%e5%8d%95%e8%ba%ab%e4%bb%bd%e9%aa%8c%e8%af%81/</link>
		
		<dc:creator><![CDATA[木它]]></dc:creator>
		<pubDate>Fri, 25 Oct 2024 03:37:17 +0000</pubDate>
				<category><![CDATA[Blazor]]></category>
		<category><![CDATA[c#]]></category>
		<category><![CDATA[身份验证与授权]]></category>
		<guid isPermaLink="false">https://blog.mutadecheng.com/?p=239</guid>

					<description><![CDATA[此基于Blazor Server 应用通过使用 SignalR 创建的实时连接运行。建立连接时，将处理基于 S [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>此基于Blazor Server 应用通过使用 SignalR 创建的实时连接运行。建立连接时，将处理基于 SignalR 的应用中的身份验证。身份验证可以基于 Cookie 或其他一些不记名令牌。Blazor Server 应用的安全性配置方式与 ASP.NET Core 应用相同。</p>



<p>Blazor Server 应用的内置 AuthenticationStateProvider 服务从 ASP.NET Core 的 HttpContext.User 获取身份验证状态数据。这就是身份验证状态与现有 ASP.NET Core 身份验证机制集成的方式</p>



<h3 class="wp-block-heading">项目结构</h3>



<ol class="wp-block-list">
<li><strong>解决方案</strong>
<ul class="wp-block-list">
<li><code>MyBlazorApp.sln</code></li>
</ul>
</li>



<li><strong>Blazor Server项目</strong>
<ul class="wp-block-list">
<li><code>MyBlazorApp</code> (Blazor Server项目)</li>



<li><code>Pages/</code> &#8211; 包含所有的页面组件（<code>*.razor</code>）</li>



<li><code>Components/</code> &#8211; 可复用的UI组件</li>



<li><code>Authentication/</code> &#8211; 身份验证服务相关文件夹
<ul class="wp-block-list">
<li><code>CustomAuthenticationStateProvider.cs/</code> &#8211; 自定义的认证状态提供器类</li>



<li><code>UserAccount.cs/</code> &#8211; 用户Dto</li>



<li><code>UserAccountService.cs/</code> &#8211; 用户服务</li>



<li><code>UserSession.cs/</code> &#8211; 用户的会话信息</li>
</ul>
</li>



<li><code>Shared/</code> &#8211; 前后端共享的组件或代码</li>



<li><code>Data/</code> &#8211; 数据访问层和业务逻辑</li>



<li><code>Models/</code> &#8211; 数据模型</li>



<li><code>wwwroot/</code> &#8211; 静态文件，如CSS、JavaScript和图像</li>



<li><code>Program.cs</code> &#8211; 配置Blazor Server应用程序的启动</li>



<li><code>Startup.cs</code> &#8211; 配置服务和中间件（如果使用Startup类）</li>
</ul>
</li>
</ol>



<h3 class="wp-block-heading">详细说明</h3>



<ul class="wp-block-list">
<li><strong>Pages</strong>: 包含Blazor应用的页面，每个页面都由一个<code>.razor</code>文件组成。这些页面负责用户界面的展示和用户交互。</li>



<li><strong>Components</strong>: 存放可复用的UI组件，组件可以在多个页面中使用，帮助减少代码重复。</li>



<li><strong>Shared</strong>: 用于存放前后端共享的代码，如共享的组件或帮助类。</li>



<li><strong>Data</strong>: 负责数据访问和业务逻辑。可以在这里实现与数据库的交互逻辑（如使用Entity Framework Core），以及任何必要的业务规则。</li>



<li><strong>Models</strong>: 定义应用程序使用的数据模型。这些模型通常用于表示数据库中的实体或其他数据结构。</li>



<li><strong>wwwroot</strong>: 包含所有的静态资源，如CSS样式表、JavaScript文件和图像文件。Blazor Server会将这些文件直接提供给浏览器。</li>



<li><strong>Program.cs</strong>: 定义应用程序的入口点，配置服务（如依赖注入）和应用程序生命周期。</li>



<li><strong>Startup.cs</strong>: 配置应用程序的请求管道和服务（如果使用Startup类）。包括中间件配置、路由设置等。</li>
</ul>



<h3 class="wp-block-heading">身份验证详解</h3>



<p>Authentication/<code><strong>CustomAuthenticationStateProvider.cs</strong></code></p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="// 定义一个自定义的认证状态提供器类，继承自AuthenticationStateProvider
public class CustomAuthenticationStateProvider : AuthenticationStateProvider
{
    // 使用ProtectedSessionStorage来安全地存储用户会话信息
    private readonly ProtectedSessionStorage _sessionStorage;
    // 定义一个匿名用户的ClaimsPrincipal对象，表示未认证的用户
    private ClaimsPrincipal _anonymous = new ClaimsPrincipal(new ClaimsIdentity());

    // 构造函数，初始化会话存储对象
    public CustomAuthenticationStateProvider(ProtectedSessionStorage sessionStorage)
    {
        _sessionStorage = sessionStorage;
    }

    // 重写基类的GetAuthenticationStateAsync方法，用于获取当前用户的认证状态
    public override async Task&lt;AuthenticationState&gt; GetAuthenticationStateAsync()
    {
        try
        {
            // 从会话存储中异步获取用户会话信息
            var userSessionStorageResult = await _sessionStorage.GetAsync&lt;UserSession&gt;(&quot;UserSession&quot;);
            // 检查获取结果是否成功，如果成功则获取用户会话信息，否则为null
            var userSession = userSessionStorageResult.Success ? userSessionStorageResult.Value : null;

            // 如果用户会话信息为null，返回匿名用户的认证状态
            if (userSession == null)
                return await Task.FromResult(new AuthenticationState(_anonymous));

            // 如果用户会话信息存在，创建一个包含用户名和角色的ClaimsPrincipal对象
            var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new List&lt;Claim&gt;
            {
                new Claim(ClaimTypes.Name, userSession.UserName), // 用户名声明
                new Claim(ClaimTypes.Role, userSession.Role) // 角色声明
            }, &quot;CustomAuth&quot;)); // 使用自定义认证类型

            // 返回包含用户信息的认证状态
            return await Task.FromResult(new AuthenticationState(claimsPrincipal));
        }
        catch
        {
            // 如果发生异常，返回匿名用户的认证状态
            return await Task.FromResult(new AuthenticationState(_anonymous));
        }
    }

    // 更新认证状态的方法，接收一个用户会话对象作为参数
    public async Task UpdateAuthenticationState(UserSession userSession)
    {
        ClaimsPrincipal claimsPrincipal;

        if (userSession != null)
        {
            // 如果用户会话信息不为null，将其存储到会话存储中
            await _sessionStorage.SetAsync(&quot;UserSession&quot;, userSession);
            // 创建包含用户名和角色的ClaimsPrincipal对象
            claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new List&lt;Claim&gt;
            {
                new Claim(ClaimTypes.Name, userSession.UserName), // 用户名声明
                new Claim(ClaimTypes.Role, userSession.Role) // 角色声明
            }));
        }
        else
        {
            // 如果用户会话信息为null，删除存储的会话信息
            await _sessionStorage.DeleteAsync(&quot;UserSession&quot;);
            // 设置为匿名用户
            claimsPrincipal = _anonymous;
        }

        // 通知认证状态已更改，传递新的认证状态
        NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(claimsPrincipal)));
    }
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">// 定义一个自定义的认证状态提供器类，继承自AuthenticationStateProvider</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">CustomAuthenticationStateProvider</span><span style="color: #D4D4D4"> : </span><span style="color: #4EC9B0">AuthenticationStateProvider</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #6A9955">    // 使用ProtectedSessionStorage来安全地存储用户会话信息</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">readonly</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">ProtectedSessionStorage</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">_sessionStorage</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #6A9955">    // 定义一个匿名用户的ClaimsPrincipal对象，表示未认证的用户</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">ClaimsPrincipal</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">_anonymous</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">ClaimsPrincipal</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">ClaimsIdentity</span><span style="color: #D4D4D4">());</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">    // 构造函数，初始化会话存储对象</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">CustomAuthenticationStateProvider</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">ProtectedSessionStorage</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sessionStorage</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">_sessionStorage</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">sessionStorage</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">    // 重写基类的GetAuthenticationStateAsync方法，用于获取当前用户的认证状态</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">override</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">async</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Task</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">AuthenticationState</span><span style="color: #D4D4D4">&gt; </span><span style="color: #DCDCAA">GetAuthenticationStateAsync</span><span style="color: #D4D4D4">()</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">try</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #6A9955">            // 从会话存储中异步获取用户会话信息</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">userSessionStorageResult</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">_sessionStorage</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetAsync</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">UserSession</span><span style="color: #D4D4D4">&gt;(</span><span style="color: #CE9178">&quot;UserSession&quot;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #6A9955">            // 检查获取结果是否成功，如果成功则获取用户会话信息，否则为null</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">userSession</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">userSessionStorageResult</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Success</span><span style="color: #D4D4D4"> ? </span><span style="color: #9CDCFE">userSessionStorageResult</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Value</span><span style="color: #D4D4D4"> : </span><span style="color: #569CD6">null</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">            // 如果用户会话信息为null，返回匿名用户的认证状态</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">userSession</span><span style="color: #D4D4D4"> == </span><span style="color: #569CD6">null</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Task</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">FromResult</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">AuthenticationState</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">_anonymous</span><span style="color: #D4D4D4">));</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">            // 如果用户会话信息存在，创建一个包含用户名和角色的ClaimsPrincipal对象</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">claimsPrincipal</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">ClaimsPrincipal</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">ClaimsIdentity</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">List</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">Claim</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            {</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Claim</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">ClaimTypes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Name</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">userSession</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">UserName</span><span style="color: #D4D4D4">), </span><span style="color: #6A9955">// 用户名声明</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Claim</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">ClaimTypes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Role</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">userSession</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Role</span><span style="color: #D4D4D4">) </span><span style="color: #6A9955">// 角色声明</span></span>
<span class="line"><span style="color: #D4D4D4">            }, </span><span style="color: #CE9178">&quot;CustomAuth&quot;</span><span style="color: #D4D4D4">)); </span><span style="color: #6A9955">// 使用自定义认证类型</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">            // 返回包含用户信息的认证状态</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Task</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">FromResult</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">AuthenticationState</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">claimsPrincipal</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">catch</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #6A9955">            // 如果发生异常，返回匿名用户的认证状态</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Task</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">FromResult</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">AuthenticationState</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">_anonymous</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">    // 更新认证状态的方法，接收一个用户会话对象作为参数</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">async</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Task</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">UpdateAuthenticationState</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">UserSession</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">userSession</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #4EC9B0">ClaimsPrincipal</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">claimsPrincipal</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">userSession</span><span style="color: #D4D4D4"> != </span><span style="color: #569CD6">null</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #6A9955">            // 如果用户会话信息不为null，将其存储到会话存储中</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">_sessionStorage</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">SetAsync</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;UserSession&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">userSession</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #6A9955">            // 创建包含用户名和角色的ClaimsPrincipal对象</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">claimsPrincipal</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">ClaimsPrincipal</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">ClaimsIdentity</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">List</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">Claim</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            {</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Claim</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">ClaimTypes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Name</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">userSession</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">UserName</span><span style="color: #D4D4D4">), </span><span style="color: #6A9955">// 用户名声明</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Claim</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">ClaimTypes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Role</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">userSession</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Role</span><span style="color: #D4D4D4">) </span><span style="color: #6A9955">// 角色声明</span></span>
<span class="line"><span style="color: #D4D4D4">            }));</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">else</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #6A9955">            // 如果用户会话信息为null，删除存储的会话信息</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">_sessionStorage</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">DeleteAsync</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;UserSession&quot;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #6A9955">            // 设置为匿名用户</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">claimsPrincipal</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">_anonymous</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">        // 通知认证状态已更改，传递新的认证状态</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #DCDCAA">NotifyAuthenticationStateChanged</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">Task</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">FromResult</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">AuthenticationState</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">claimsPrincipal</span><span style="color: #D4D4D4">)));</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p><code>Authentication</code>/UserAccount.cs</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="public class UserAccount
{
    public string UserName { get; set; }
    public string Password { get; set; }
    public string Role { get; set; }
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">UserAccount</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">UserName</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">get</span><span style="color: #D4D4D4">; </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4">; }</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Password</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">get</span><span style="color: #D4D4D4">; </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4">; }</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Role</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">get</span><span style="color: #D4D4D4">; </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4">; }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p><code>Authentication</code>/UserAccountService.cs</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="public class UserAccountService
{
    private List&lt;UserAccount&gt; _users;

    public UserAccountService()
    {
        _users = new List&lt;UserAccount&gt;
        {
            new UserAccount{ UserName = &quot;admin&quot;, Password = &quot;admin&quot;, Role = &quot;Administrator&quot; },
            new UserAccount{ UserName = &quot;user&quot;, Password = &quot;user&quot;, Role = &quot;User&quot; }
        };
    }

    public UserAccount? GetByUserName(string userName)
    {
        return _users.FirstOrDefault(x =&gt; x.UserName == userName);
    }
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">UserAccountService</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">List</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">UserAccount</span><span style="color: #D4D4D4">&gt; </span><span style="color: #9CDCFE">_users</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">UserAccountService</span><span style="color: #D4D4D4">()</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">_users</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">List</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">UserAccount</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">UserAccount</span><span style="color: #D4D4D4">{ </span><span style="color: #9CDCFE">UserName</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;admin&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">Password</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;admin&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">Role</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;Administrator&quot;</span><span style="color: #D4D4D4"> },</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">UserAccount</span><span style="color: #D4D4D4">{ </span><span style="color: #9CDCFE">UserName</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;user&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">Password</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;user&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">Role</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;User&quot;</span><span style="color: #D4D4D4"> }</span></span>
<span class="line"><span style="color: #D4D4D4">        };</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">UserAccount</span><span style="color: #D4D4D4">? </span><span style="color: #DCDCAA">GetByUserName</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">userName</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">_users</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">FirstOrDefault</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">x</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #9CDCFE">x</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">UserName</span><span style="color: #D4D4D4"> == </span><span style="color: #9CDCFE">userName</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p><code>Authentication</code>/UserSession.cs</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="public class UserSession
{
    public string UserName { get; set; }
    public string Role { get; set; }
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">UserSession</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">UserName</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">get</span><span style="color: #D4D4D4">; </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4">; }</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Role</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">get</span><span style="color: #D4D4D4">; </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4">; }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p>Pages/Login.razor</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="@page &quot;/login&quot;
@using MyBlazorApp.Authentication
@inject UserAccountService userAccountService
@inject IJSRuntime js
@inject AuthenticationStateProvider authStateProvider
@inject NavigationManager navManager

&lt;div class=&quot;row&quot;&gt;
    &lt;div class=&quot;col-lg-4 offset-lg-4 pt-4 pb-4 border&quot;&gt;
        &lt;div class=&quot;mb-3 text-center&quot;&gt;
            &lt;h3&gt;LOGIN&lt;/h3&gt;
        &lt;/div&gt;
        &lt;div class=&quot;mb-3&quot;&gt;
            &lt;label&gt;User Name&lt;/label&gt;
            &lt;input @bind=&quot;model.UserName&quot; class=&quot;form-control&quot; placeholder=&quot;User Name&quot; /&gt;
        &lt;/div&gt;
        &lt;div class=&quot;mb-3&quot;&gt;
            &lt;label&gt;Password&lt;/label&gt;
            &lt;input @bind=&quot;model.Password&quot; type=&quot;password&quot; class=&quot;form-control&quot; placeholder=&quot;Password&quot; /&gt;
        &lt;/div&gt;
        &lt;div class=&quot;mb-3 d-grid gap-2&quot;&gt;
            &lt;button @onclick=&quot;Authenticate&quot; class=&quot;btn btn-primary&quot;&gt;Login&lt;/button&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

@code {
    private class Model
    {
        public string UserName { get; set; } 
        public string Password { get; set; }
    } 
    private Model model = new Model(); 
    private async Task Authenticate()
    {
        var userAccount = userAccountService.GetByUserName(model.UserName); 
        if (userAccount == null || userAccount.Password != model.Password)
        {
            await js.InvokeVoidAsync(&quot;alert&quot;, &quot;Invalid User Name or Password&quot;);
            return;
        } 
        var customAuthStateProvider = (CustomAuthenticationStateProvider)authStateProvider;
        await customAuthStateProvider.UpdateAuthenticationState(new UserSession
            {
                UserName = userAccount.UserName,
                Role = userAccount.Role
            });
        navManager.NavigateTo(&quot;/&quot;, true);
    }
} " style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #9CDCFE">@page</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;/login&quot;</span></span>
<span class="line"><span style="color: #9CDCFE">@using</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">MyBlazorApp</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Authentication</span></span>
<span class="line"><span style="color: #9CDCFE">@inject</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">UserAccountService</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">userAccountService</span></span>
<span class="line"><span style="color: #9CDCFE">@inject</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">IJSRuntime</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">js</span></span>
<span class="line"><span style="color: #9CDCFE">@inject</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">AuthenticationStateProvider</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">authStateProvider</span></span>
<span class="line"><span style="color: #9CDCFE">@inject</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">NavigationManager</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">navManager</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">div</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;row&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;</span><span style="color: #9CDCFE">div</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;col-lg-4 offset-lg-4 pt-4 pb-4 border&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;</span><span style="color: #9CDCFE">div</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;mb-3 text-center&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;</span><span style="color: #9CDCFE">h3</span><span style="color: #D4D4D4">&gt;</span><span style="color: #9CDCFE">LOGIN</span><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">h3</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;/</span><span style="color: #9CDCFE">div</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;</span><span style="color: #9CDCFE">div</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;mb-3&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;</span><span style="color: #9CDCFE">label</span><span style="color: #D4D4D4">&gt;</span><span style="color: #9CDCFE">User</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Name</span><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">label</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;</span><span style="color: #9CDCFE">input</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">@bind</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;model.UserName&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;form-control&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">placeholder</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;User Name&quot;</span><span style="color: #D4D4D4"> /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;/</span><span style="color: #9CDCFE">div</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;</span><span style="color: #9CDCFE">div</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;mb-3&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;</span><span style="color: #9CDCFE">label</span><span style="color: #D4D4D4">&gt;</span><span style="color: #9CDCFE">Password</span><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">label</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;</span><span style="color: #9CDCFE">input</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">@bind</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;model.Password&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;password&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;form-control&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">placeholder</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;Password&quot;</span><span style="color: #D4D4D4"> /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;/</span><span style="color: #9CDCFE">div</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;</span><span style="color: #9CDCFE">div</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;mb-3 d-grid gap-2&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;</span><span style="color: #9CDCFE">button</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">@onclick</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;Authenticate&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;btn btn-primary&quot;</span><span style="color: #D4D4D4">&gt;</span><span style="color: #9CDCFE">Login</span><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">button</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;/</span><span style="color: #9CDCFE">div</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;/</span><span style="color: #9CDCFE">div</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">div</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">@code</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">private</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Model</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">UserName</span><span style="color: #D4D4D4"> { </span><span style="color: #9CDCFE">get</span><span style="color: #D4D4D4">; </span><span style="color: #9CDCFE">set</span><span style="color: #D4D4D4">; } </span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Password</span><span style="color: #D4D4D4"> { </span><span style="color: #9CDCFE">get</span><span style="color: #D4D4D4">; </span><span style="color: #9CDCFE">set</span><span style="color: #D4D4D4">; }</span></span>
<span class="line"><span style="color: #D4D4D4">    } </span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">private</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Model</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">model</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Model</span><span style="color: #D4D4D4">(); </span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">private</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">async</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Task</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Authenticate</span><span style="color: #D4D4D4">()</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">userAccount</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">userAccountService</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetByUserName</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">model</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">UserName</span><span style="color: #D4D4D4">); </span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #DCDCAA">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">userAccount</span><span style="color: #D4D4D4"> == </span><span style="color: #569CD6">null</span><span style="color: #D4D4D4"> || </span><span style="color: #9CDCFE">userAccount</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Password</span><span style="color: #D4D4D4"> != </span><span style="color: #9CDCFE">model</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Password</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">js</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">InvokeVoidAsync</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;alert&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&quot;Invalid User Name or Password&quot;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">return</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        } </span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">customAuthStateProvider</span><span style="color: #D4D4D4"> = (</span><span style="color: #4EC9B0">CustomAuthenticationStateProvider</span><span style="color: #D4D4D4">)</span><span style="color: #9CDCFE">authStateProvider</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">customAuthStateProvider</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UpdateAuthenticationState</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">UserSession</span></span>
<span class="line"><span style="color: #D4D4D4">            {</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">UserName</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">userAccount</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">UserName</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">Role</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">userAccount</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Role</span></span>
<span class="line"><span style="color: #D4D4D4">            });</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">navManager</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">NavigateTo</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;/&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">} </span></span></code></pre></div>



<p>Shared/RedirectToLogin</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="@inject AuthenticationStateProvider AuthStateProvider
@inject NavigationManager Navigation

&lt;p&gt;用户未授权，正在重定向到登录页面...&lt;/p&gt;

@code {
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            var authState = await AuthStateProvider.GetAuthenticationStateAsync();
            var user = authState.User; 
            if (user.Identity == null || !user.Identity.IsAuthenticated)
            {
                // 用户未认证，重定向到登录页面
                Navigation.NavigateTo(&quot;/login&quot;, true);
            }
        } 
    }
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #9CDCFE">@inject</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">AuthenticationStateProvider</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">AuthStateProvider</span></span>
<span class="line"><span style="color: #9CDCFE">@inject</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">NavigationManager</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Navigation</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">&lt;</span><span style="color: #9CDCFE">p</span><span style="color: #D4D4D4">&gt;</span><span style="color: #9CDCFE">用户未授权</span><span style="color: #D4D4D4">，</span><span style="color: #9CDCFE">正在重定向到登录页面</span><span style="color: #D4D4D4">...&lt;/</span><span style="color: #9CDCFE">p</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">@code</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">protected</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">override</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">async</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Task</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">OnAfterRenderAsync</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">bool</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">firstRender</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">firstRender</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">authState</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">AuthStateProvider</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetAuthenticationStateAsync</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">authState</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">User</span><span style="color: #D4D4D4">; </span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Identity</span><span style="color: #D4D4D4"> == </span><span style="color: #569CD6">null</span><span style="color: #D4D4D4"> || !</span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Identity</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">IsAuthenticated</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">            {</span></span>
<span class="line"><span style="color: #6A9955">                // 用户未认证，重定向到登录页面</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">Navigation</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">NavigateTo</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;/login&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"><span style="color: #D4D4D4">        } </span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p>Shared/MainLayout.razor</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="@using MyBlazorApp.Authentication
@inherits LayoutComponentBase
@inject AuthenticationStateProvider authStateProvider
@inject NavigationManager navManager

&lt;PageTitle&gt;MyBlazorApp&lt;/PageTitle&gt;

&lt;div class=&quot;page&quot;&gt;
    &lt;div class=&quot;sidebar&quot;&gt;
        &lt;NavMenu /&gt;
    &lt;/div&gt;

    &lt;main&gt;
        &lt;div class=&quot;top-row px-4&quot;&gt;
            &lt;a href=&quot;https://docs.microsoft.com/aspnet/&quot; target=&quot;_blank&quot;&gt;About&lt;/a&gt;
            &lt;AuthorizeView&gt;
                &lt;Authorized&gt;
                    &lt;a @onclick=&quot;Logout&quot; href=&quot;javascript:void(0)&quot;&gt;Logout&lt;/a&gt;
                &lt;/Authorized&gt;
                &lt;NotAuthorized&gt;
                    &lt;a href=&quot;/login&quot;&gt;Login&lt;/a&gt;
                &lt;/NotAuthorized&gt;
            &lt;/AuthorizeView&gt;
        &lt;/div&gt;

        &lt;article class=&quot;content px-4&quot;&gt;
            @Body
        &lt;/article&gt;
    &lt;/main&gt;
&lt;/div&gt;

@code{
    private async Task Logout()
    {
        var customAuthStateProvider = (CustomAuthenticationStateProvider)authStateProvider;
        await customAuthStateProvider.UpdateAuthenticationState(null);
        navManager.NavigateTo(&quot;/&quot;, true);
    }
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #9CDCFE">@using</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">MyBlazorApp</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Authentication</span></span>
<span class="line"><span style="color: #9CDCFE">@inherits</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">LayoutComponentBase</span></span>
<span class="line"><span style="color: #9CDCFE">@inject</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">AuthenticationStateProvider</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">authStateProvider</span></span>
<span class="line"><span style="color: #9CDCFE">@inject</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">NavigationManager</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">navManager</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">&lt;</span><span style="color: #9CDCFE">PageTitle</span><span style="color: #D4D4D4">&gt;</span><span style="color: #9CDCFE">MyBlazorApp</span><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">PageTitle</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">div</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;page&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;</span><span style="color: #9CDCFE">div</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;sidebar&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;</span><span style="color: #9CDCFE">NavMenu</span><span style="color: #D4D4D4"> /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;/</span><span style="color: #9CDCFE">div</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    &lt;</span><span style="color: #9CDCFE">main</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;</span><span style="color: #9CDCFE">div</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;top-row px-4&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;</span><span style="color: #9CDCFE">a</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">href</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;https://docs.microsoft.com/aspnet/&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">target</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;_blank&quot;</span><span style="color: #D4D4D4">&gt;</span><span style="color: #9CDCFE">About</span><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">a</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;</span><span style="color: #9CDCFE">AuthorizeView</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;</span><span style="color: #9CDCFE">Authorized</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                    &lt;</span><span style="color: #9CDCFE">a</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">@onclick</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;Logout&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">href</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;javascript:void(0)&quot;</span><span style="color: #D4D4D4">&gt;</span><span style="color: #9CDCFE">Logout</span><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">a</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;/</span><span style="color: #9CDCFE">Authorized</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;</span><span style="color: #9CDCFE">NotAuthorized</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                    &lt;</span><span style="color: #9CDCFE">a</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">href</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;/login&quot;</span><span style="color: #D4D4D4">&gt;</span><span style="color: #9CDCFE">Login</span><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">a</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;/</span><span style="color: #9CDCFE">NotAuthorized</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;/</span><span style="color: #9CDCFE">AuthorizeView</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;/</span><span style="color: #9CDCFE">div</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        &lt;</span><span style="color: #9CDCFE">article</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;content px-4&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">@Body</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;/</span><span style="color: #9CDCFE">article</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;/</span><span style="color: #9CDCFE">main</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">div</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">@code</span><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">private</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">async</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Task</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Logout</span><span style="color: #D4D4D4">()</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">customAuthStateProvider</span><span style="color: #D4D4D4"> = (</span><span style="color: #4EC9B0">CustomAuthenticationStateProvider</span><span style="color: #D4D4D4">)</span><span style="color: #9CDCFE">authStateProvider</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">customAuthStateProvider</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UpdateAuthenticationState</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">null</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">navManager</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">NavigateTo</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;/&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p>App.razor</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="&lt;CascadingAuthenticationState&gt;
    &lt;Router AppAssembly=&quot;@typeof(App).Assembly&quot;&gt;
        &lt;Found Context=&quot;routeData&quot;&gt;
            &lt;AuthorizeRouteView RouteData=&quot;@routeData&quot; DefaultLayout=&quot;@typeof(MainLayout)&quot;&gt;
                &lt;NotAuthorized&gt;
                    &lt;RedirectToLogin /&gt;
                &lt;/NotAuthorized&gt;
                &lt;Authorizing&gt;
                    You are getting authorized  
                &lt;/Authorizing&gt;
            &lt;/AuthorizeRouteView&gt;
            &lt;FocusOnNavigate RouteData=&quot;@routeData&quot; Selector=&quot;h1&quot; /&gt;
        &lt;/Found&gt;
        &lt;NotFound&gt;
            &lt;PageTitle&gt;Not found&lt;/PageTitle&gt;
            &lt;LayoutView Layout=&quot;@typeof(MainLayout)&quot;&gt;
                &lt;p role=&quot;alert&quot;&gt;Sorry, there's nothing at this address.&lt;/p&gt;
            &lt;/LayoutView&gt;
        &lt;/NotFound&gt;
    &lt;/Router&gt;
&lt;/CascadingAuthenticationState&gt;
" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;</span><span style="color: #9CDCFE">CascadingAuthenticationState</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;</span><span style="color: #4EC9B0">Router</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">AppAssembly</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;@typeof(App).Assembly&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;</span><span style="color: #9CDCFE">Found</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Context</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;routeData&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;</span><span style="color: #9CDCFE">AuthorizeRouteView</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">RouteData</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;@routeData&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">DefaultLayout</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;@typeof(MainLayout)&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;</span><span style="color: #9CDCFE">NotAuthorized</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                    &lt;</span><span style="color: #9CDCFE">RedirectToLogin</span><span style="color: #D4D4D4"> /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;/</span><span style="color: #9CDCFE">NotAuthorized</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;</span><span style="color: #9CDCFE">Authorizing</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                    </span><span style="color: #9CDCFE">You</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">are</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">getting</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">authorized</span><span style="color: #D4D4D4">  </span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;/</span><span style="color: #9CDCFE">Authorizing</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;/</span><span style="color: #9CDCFE">AuthorizeRouteView</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;</span><span style="color: #9CDCFE">FocusOnNavigate</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">RouteData</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;@routeData&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Selector</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;h1&quot;</span><span style="color: #D4D4D4"> /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;/</span><span style="color: #9CDCFE">Found</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;</span><span style="color: #9CDCFE">NotFound</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;</span><span style="color: #9CDCFE">PageTitle</span><span style="color: #D4D4D4">&gt;</span><span style="color: #9CDCFE">Not</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">found</span><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">PageTitle</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;</span><span style="color: #9CDCFE">LayoutView</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Layout</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;@typeof(MainLayout)&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;</span><span style="color: #9CDCFE">p</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">role</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;alert&quot;</span><span style="color: #D4D4D4">&gt;</span><span style="color: #9CDCFE">Sorry</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">there</span><span style="color: #D4D4D4">&#39;</span><span style="color: #9CDCFE">s</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">nothing</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">at</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">this</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">address</span><span style="color: #D4D4D4">.&lt;/</span><span style="color: #9CDCFE">p</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;/</span><span style="color: #9CDCFE">LayoutView</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;/</span><span style="color: #9CDCFE">NotFound</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;/</span><span style="color: #9CDCFE">Router</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">CascadingAuthenticationState</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"></span></code></pre></div>



<p>Program.cs</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="var builder = WebApplication.CreateBuilder(args); 
builder.Services.AddAuthenticationCore();
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddScoped&lt;ProtectedSessionStorage&gt;();
builder.Services.AddScoped&lt;AuthenticationStateProvider, CustomAuthenticationStateProvider&gt;();
builder.Services.AddSingleton&lt;UserAccountService&gt;();
builder.Services.AddSingleton&lt;WeatherForecastService&gt;();

var app = builder.Build(); 
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler(&quot;/Error&quot;);
}  
app.UseStaticFiles(); 
app.UseRouting(); 
app.MapBlazorHub();
app.MapFallbackToPage(&quot;/_Host&quot;); 
app.Run();" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">WebApplication</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">CreateBuilder</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">args</span><span style="color: #D4D4D4">); </span></span>
<span class="line"><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Services</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddAuthenticationCore</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Services</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddRazorPages</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Services</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddServerSideBlazor</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Services</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddScoped</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">ProtectedSessionStorage</span><span style="color: #D4D4D4">&gt;();</span></span>
<span class="line"><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Services</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddScoped</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">AuthenticationStateProvider</span><span style="color: #D4D4D4">, </span><span style="color: #4EC9B0">CustomAuthenticationStateProvider</span><span style="color: #D4D4D4">&gt;();</span></span>
<span class="line"><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Services</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddSingleton</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">UserAccountService</span><span style="color: #D4D4D4">&gt;();</span></span>
<span class="line"><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Services</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddSingleton</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">WeatherForecastService</span><span style="color: #D4D4D4">&gt;();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Build</span><span style="color: #D4D4D4">(); </span></span>
<span class="line"><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (!</span><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Environment</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">IsDevelopment</span><span style="color: #D4D4D4">())</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseExceptionHandler</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;/Error&quot;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">}  </span></span>
<span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseStaticFiles</span><span style="color: #D4D4D4">(); </span></span>
<span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseRouting</span><span style="color: #D4D4D4">(); </span></span>
<span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">MapBlazorHub</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">MapFallbackToPage</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;/_Host&quot;</span><span style="color: #D4D4D4">); </span></span>
<span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Run</span><span style="color: #D4D4D4">();</span></span></code></pre></div>



<p></p>



<h3 class="wp-block-heading">使用方式</h3>



<p>在页面中使用</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="@page &quot;/counter&quot;
@attribute [Authorize(Roles = &quot;Administrator,User&quot;)]" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #9CDCFE">@page</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;/counter&quot;</span></span>
<span class="line"><span style="color: #9CDCFE">@attribute</span><span style="color: #D4D4D4"> [</span><span style="color: #DCDCAA">Authorize</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">Roles</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;Administrator,User&quot;</span><span style="color: #D4D4D4">)]</span></span></code></pre></div>



<p>在组件中使用</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="&lt;AuthorizeView Roles=&quot;Administrator,User&quot;&gt;
    &lt;Authorized&gt;
        &lt;div class=&quot;nav-item px-3&quot;&gt;
            &lt;NavLink class=&quot;nav-link&quot; href=&quot;counter&quot;&gt;
                &lt;span class=&quot;oi oi-plus&quot; aria-hidden=&quot;true&quot;&gt;&lt;/span&gt; Counter
            &lt;/NavLink&gt;
        &lt;/div&gt;
    &lt;/Authorized&gt;
&lt;/AuthorizeView&gt;
&lt;AuthorizeView Roles=&quot;Administrator&quot;&gt;
    &lt;Authorized&gt;
        &lt;div class=&quot;nav-item px-3&quot;&gt;
            &lt;NavLink class=&quot;nav-link&quot; href=&quot;fetchdata&quot;&gt;
                &lt;span class=&quot;oi oi-list-rich&quot; aria-hidden=&quot;true&quot;&gt;&lt;/span&gt; Fetch data
            &lt;/NavLink&gt;
        &lt;/div&gt;
    &lt;/Authorized&gt;
&lt;/AuthorizeView&gt;" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">AuthorizeView</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Roles</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;Administrator,User&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;</span><span style="color: #9CDCFE">Authorized</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;</span><span style="color: #9CDCFE">div</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;nav-item px-3&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;</span><span style="color: #9CDCFE">NavLink</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;nav-link&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">href</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;counter&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;</span><span style="color: #9CDCFE">span</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;oi oi-plus&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">aria</span><span style="color: #D4D4D4">-</span><span style="color: #9CDCFE">hidden</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;true&quot;</span><span style="color: #D4D4D4">&gt;&lt;/</span><span style="color: #9CDCFE">span</span><span style="color: #D4D4D4">&gt; </span><span style="color: #9CDCFE">Counter</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;/</span><span style="color: #9CDCFE">NavLink</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;/</span><span style="color: #9CDCFE">div</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;/</span><span style="color: #9CDCFE">Authorized</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">AuthorizeView</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;</span><span style="color: #9CDCFE">AuthorizeView</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Roles</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;Administrator&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;</span><span style="color: #9CDCFE">Authorized</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;</span><span style="color: #9CDCFE">div</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;nav-item px-3&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;</span><span style="color: #9CDCFE">NavLink</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;nav-link&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">href</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;fetchdata&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">                &lt;</span><span style="color: #9CDCFE">span</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;oi oi-list-rich&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">aria</span><span style="color: #D4D4D4">-</span><span style="color: #9CDCFE">hidden</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;true&quot;</span><span style="color: #D4D4D4">&gt;&lt;/</span><span style="color: #9CDCFE">span</span><span style="color: #D4D4D4">&gt; </span><span style="color: #9CDCFE">Fetch</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">data</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;/</span><span style="color: #9CDCFE">NavLink</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;/</span><span style="color: #9CDCFE">div</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;/</span><span style="color: #9CDCFE">Authorized</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">AuthorizeView</span><span style="color: #D4D4D4">&gt;</span></span></code></pre></div>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>OpenIddict身份验证与授权之: 三. 2(授权码模式)客户端</title>
		<link>https://blog.mutadecheng.com/2024/06/07/openiddict%e8%ba%ab%e4%bb%bd%e9%aa%8c%e8%af%81%e4%b8%8e%e6%8e%88%e6%9d%83%e4%b9%8b-%e4%b8%89-1%e6%8e%88%e6%9d%83%e7%a0%81%e6%a8%a1%e5%bc%8f%e5%ae%a2%e6%88%b7%e7%ab%af/</link>
		
		<dc:creator><![CDATA[木它]]></dc:creator>
		<pubDate>Fri, 07 Jun 2024 08:07:37 +0000</pubDate>
				<category><![CDATA[c#]]></category>
		<category><![CDATA[身份验证与授权]]></category>
		<guid isPermaLink="false">https://blog.mutadecheng.com/?p=181</guid>

					<description><![CDATA[]]></description>
										<content:encoded><![CDATA[]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>OpenIddict身份验证与授权之: 三. 1(授权码模式)验证服务器</title>
		<link>https://blog.mutadecheng.com/2024/06/05/openiddict%e8%ba%ab%e4%bb%bd%e9%aa%8c%e8%af%81%e4%b8%8e%e6%8e%88%e6%9d%83%e4%b9%8b-%e4%b8%89-1%e6%8e%88%e6%9d%83%e7%a0%81%e6%a8%a1%e5%bc%8f%e9%aa%8c%e8%af%81%e6%9c%8d%e5%8a%a1%e5%99%a8/</link>
		
		<dc:creator><![CDATA[木它]]></dc:creator>
		<pubDate>Wed, 05 Jun 2024 08:16:46 +0000</pubDate>
				<category><![CDATA[身份验证与授权]]></category>
		<guid isPermaLink="false">https://blog.mutadecheng.com/?p=172</guid>

					<description><![CDATA[OpenIddict 授权码模式流程图 OpenIddict 授权码模式流程图]]></description>
										<content:encoded><![CDATA[
<!DOCTYPE html>
<html>
<head>

<title>OpenIddict 授权码模式流程图</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.19.1/cytoscape.min.js"></script>
<style>
    #cy {
        width: 100%;
        height: 600px;
        display: block;
    }
</style>
</head>
<body>
<div id="cy"></div>
<script>
// 初始化 Cytoscape
var cy = cytoscape({
    container: document.getElementById('cy'), // 容器元素
    elements: [ // 节点和边的定义
        // 节点
        { data: { id: 'client', label: '客户端' } },
        { data: { id: 'auth_server', label: '授权服务器' } },
        { data: { id: 'resource_server', label: '资源服务器' } },
        { data: { id: 'user', label: '用户' } },
        // 边
        { data: { source: 'client', target: 'user', label: '1. 用户重定向到登录页面' } },
        { data: { source: 'user', target: 'auth_server', label: '2. 用户输入凭据登录' } },
        { data: { source: 'auth_server', target: 'user', label: '3. 凭据验证' } },
        { data: { source: 'user', target: 'auth_server', label: '4. 用户授权访问' } },
        { data: { source: 'auth_server', target: 'client', label: '5. 提供授权码' } },
        { data: { source: 'client', target: 'auth_server', label: '6. 请求访问令牌' } },
        { data: { source: 'auth_server', target: 'client', label: '7. 验证授权码和客户端信息' } },
        { data: { source: 'auth_server', target: 'client', label: '8. 颁发访问令牌和刷新令牌' } },
        { data: { source: 'client', target: 'resource_server', label: '9. 请求资源' } },
        { data: { source: 'resource_server', target: 'client', label: '10. 验证访问令牌' } },
        { data: { source: 'resource_server', target: 'client', label: '11. 提供资源' } },
    ],
    style: [ // 样式定义
        {
            selector: 'node',
            style: {
                'background-color': '#666',
                'label': 'data(label)',
                'text-valign': 'center',
                'color': '#fff',
                'text-outline-width': 2,
                'text-outline-color': '#666',
            }
        },
        {
            selector: 'edge',
            style: {
                'width': 3,
                'line-color': '#ccc',
                'target-arrow-color': '#ccc',
                'target-arrow-shape': 'triangle',
                'curve-style': 'bezier',
                'label': 'data(label)',
                'font-size': 10,
                'text-margin-x': 3,
                'text-margin-y': 3,
                'text-rotation': 'autorotate',
                'edge-text-rotation': 'autorotate',
                'text-outline-width': 2,
                'text-outline-color': '#ccc'
            }
        }
    ],
    layout: {
        name: 'cose',
        idealEdgeLength: 100,
        nodeOverlap: 20,
        refresh: 20,
        fit: true,
        padding: 30,
        randomize: false,
        componentSpacing: 100,
        nodeRepulsion: 400000,
        edgeElasticity: 100,
        nestingFactor: 5,
        gravity: 80,
        numIter: 1000,
        initialTemp: 200,
        coolingFactor: 0.95,
        minTemp: 1.0
    }
});

</script>
</body>
</html>



<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>OpenIddict 授权码模式流程图</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.19.1/cytoscape.min.js"></script>
<style>
    #cy {
        width: 100%;
        height: 600px;
        display: block;
    }
</style>
</head>
<body>
<div id="cy"></div>
<script>
// 初始化 Cytoscape
var cy = cytoscape({
    container: document.getElementById('cy'), // 容器元素
    elements: [ // 节点和边的定义
        // 节点
        { data: { id: 'client', label: '客户端' } },
        { data: { id: 'auth_server', label: '授权服务器' } },
        { data: { id: 'resource_server', label: '资源服务器' } },
        { data: { id: 'user', label: '用户' } },
        // 边
        { data: { source: 'client', target: 'user', label: '1. 用户重定向到登录页面' } },
        { data: { source: 'user', target: 'auth_server', label: '2. 用户输入凭据登录' } },
        { data: { source: 'auth_server', target: 'user', label: '3. 凭据验证' } },
        { data: { source: 'user', target: 'auth_server', label: '4. 用户授权访问' } },
        { data: { source: 'auth_server', target: 'client', label: '5. 提供授权码' } },
        { data: { source: 'client', target: 'auth_server', label: '6. 请求访问令牌' } },
        { data: { source: 'auth_server', target: 'client', label: '7. 验证授权码和客户端信息' } },
        { data: { source: 'auth_server', target: 'client', label: '8. 颁发访问令牌和刷新令牌' } },
        { data: { source: 'client', target: 'resource_server', label: '9. 请求资源' } },
        { data: { source: 'resource_server', target: 'client', label: '10. 验证访问令牌' } },
        { data: { source: 'resource_server', target: 'client', label: '11. 提供资源' } },
    ],
    style: [ // 样式定义
        {
            selector: 'node',
            style: {
                'background-color': '#666',
                'label': 'data(label)',
                'text-valign': 'center',
                'color': '#fff',
                'text-outline-width': 2,
                'text-outline-color': '#666',
            }
        },
        {
            selector: 'edge',
            style: {
                'width': 3,
                'line-color': '#ccc',
                'target-arrow-color': '#ccc',
                'target-arrow-shape': 'triangle',
                'curve-style': 'bezier',
                'label': 'data(label)',
                'font-size': 10,
                'text-margin-x': 3,
                'text-margin-y': 3,
                'text-rotation': 'autorotate',
                'edge-text-rotation': 'autorotate',
                'text-outline-width': 2,
                'text-outline-color': '#ccc'
            }
        }
    ],
    layout: {
        name: 'cose',
        idealEdgeLength: 100,
        nodeOverlap: 20,
        refresh: 20,
        fit: true,
        padding: 30,
        randomize: false,
        componentSpacing: 100,
        nodeRepulsion: 400000,
        edgeElasticity: 100,
        nestingFactor: 5,
        gravity: 80,
        numIter: 1000,
        initialTemp: 200,
        coolingFactor: 0.95,
        minTemp: 1.0
    }
});

// 定义颜色变化的步骤
var steps = [
    'edge[source="client"][target="user"]',
    'edge[source="user"][target="auth_server"]',
    'edge[source="auth_server"][target="user"]',
    'edge[source="user"][target="auth_server"]',
    'edge[source="auth_server"][target="client"]',
    'edge[source="client"][target="auth_server"]',
    'edge[source="auth_server"][target="client"]',
    'edge[source="auth_server"][target="client"]',
    'edge[source="client"][target="resource_server"]',
    'edge[source="resource_server"][target="client"]',
    'edge[source="resource_server"][target="client"]'
];

var currentIndex = 0;

// 动态改变颜色的函数
function highlightStep() {
    // 重置所有边的颜色
    cy.elements('edge').style({
        'line-color': '#ccc',
        'target-arrow-color': '#ccc',
        'text-outline-color': '#ccc'
    });

    // 高亮当前步骤
    cy.elements(steps[currentIndex]).style({
        'line-color': '#FF5733', // 高亮颜色
        'target-arrow-color': '#FF5733',
        'text-outline-color': '#FF5733'
    });

    currentIndex++;

    // 如果已经完成一轮，则重置索引
    if (currentIndex === steps.length) {
        currentIndex = 0;
        setTimeout(highlightStep, 1000); // 延迟重新开始
    } else {
        setTimeout(highlightStep, 1000); // 每步骤之间的时间间隔
    }
}

// 开始执行高亮流程
highlightStep();

</script>
</body>
</html>

]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>OpenIddict身份验证与授权之: 二. 2(密码模式)资源服务器</title>
		<link>https://blog.mutadecheng.com/2024/06/04/openiddict%e8%ba%ab%e4%bb%bd%e9%aa%8c%e8%af%81%e4%b8%8e%e6%8e%88%e6%9d%83%e4%b9%8b-%e4%ba%8c-2%e5%af%86%e7%a0%81%e6%a8%a1%e5%bc%8f%e8%b5%84%e6%ba%90%e6%9c%8d%e5%8a%a1%e5%99%a8/</link>
		
		<dc:creator><![CDATA[木它]]></dc:creator>
		<pubDate>Tue, 04 Jun 2024 10:09:55 +0000</pubDate>
				<category><![CDATA[身份验证与授权]]></category>
		<guid isPermaLink="false">https://blog.mutadecheng.com/?p=151</guid>

					<description><![CDATA[密码模式(最简化示例) 为了简化步骤便于理解, 在密码模式下,我只生成两个项目,一个是验证服务器,一个是资源服 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h1 class="wp-block-heading">密码模式(最简化示例)</h1>



<p>为了简化步骤便于理解, 在密码模式下,我只生成两个项目,一个是验证服务器,一个是资源服务器,客户端用postman或者自带的swagger, 详情请看系列文章,此文章为密码模式中的资源服务器篇</p>



<h2 class="wp-block-heading">一. Nuget</h2>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="&lt;ItemGroup&gt;
  &lt;PackageReference Include=&quot;Microsoft.AspNetCore.Authentication.JwtBearer&quot; Version=&quot;6.0.31&quot; /&gt;
  &lt;PackageReference Include=&quot;Swashbuckle.AspNetCore&quot; Version=&quot;6.6.2&quot; /&gt;
&lt;/ItemGroup&gt;" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #808080">&lt;</span><span style="color: #569CD6">ItemGroup</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">PackageReference</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Include</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;Microsoft.AspNetCore.Authentication.JwtBearer&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Version</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;6.0.31&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #808080">/&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">PackageReference</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Include</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;Swashbuckle.AspNetCore&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Version</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;6.6.2&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #808080">/&gt;</span></span>
<span class="line"><span style="color: #808080">&lt;/</span><span style="color: #569CD6">ItemGroup</span><span style="color: #808080">&gt;</span></span></code></pre></div>



<h2 class="wp-block-heading">二. 配置</h2>



<h3 class="wp-block-heading">1.授权</h3>



<h4 class="wp-block-heading">1.1 未加密Jwt</h4>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code=" builder.Services.AddAuthentication(options =&gt;
 {
     options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
     options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
 })
.AddJwtBearer(options =&gt;
{
    options.Authority = &quot;https://localhost:7097/&quot;;
    options.Audience = &quot;resource_api_1&quot;;
    options.RequireHttpsMetadata = false; // 仅限开发环境
});" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Services</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddAuthentication</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4"> =&gt;</span></span>
<span class="line"><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">     </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">DefaultAuthenticateScheme</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">JwtBearerDefaults</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">AuthenticationScheme</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">     </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">DefaultChallengeScheme</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">JwtBearerDefaults</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">AuthenticationScheme</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> })</span></span>
<span class="line"><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddJwtBearer</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4"> =&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Authority</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;https://localhost:7097/&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Audience</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;resource_api_1&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">RequireHttpsMetadata</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">false</span><span style="color: #D4D4D4">; </span><span style="color: #6A9955">// 仅限开发环境</span></span>
<span class="line"><span style="color: #D4D4D4">});</span></span></code></pre></div>



<h4 class="wp-block-heading">1.2 加密Jwt</h4>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code=" builder.Services.AddAuthentication(options =&gt;
 {
     options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
     options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
 })
.AddJwtBearer(options =&gt;
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        // 配置解密密钥
        TokenDecryptionKey = new X509SecurityKey(new X509Certificate2(
             builder.Configuration.GetValue&lt;string&gt;(&quot;Certificate:Path&quot;),
             builder.Configuration.GetValue&lt;string&gt;(&quot;Certificate:PassWord&quot;))
         ),
        // 配置证书的公钥
        IssuerSigningKey = new X509SecurityKey(new X509Certificate2(
            builder.Configuration.GetValue&lt;string&gt;(&quot;Certificate:Path&quot;), 
            builder.Configuration.GetValue&lt;string&gt;(&quot;Certificate:PassWord&quot;))
        ),
        // 验证令牌发行者
        ValidateIssuer = true,
        ValidIssuer = &quot;https://localhost:7097/&quot;,
        // 验证令牌受众
        ValidateAudience = true,
        ValidAudience = &quot;resource_api_1&quot;,
        // 验证令牌签名
        ValidateIssuerSigningKey = true,
        // 验证令牌有效期
        ValidateLifetime = true,
        // 如果需要时钟偏差，可以设置ClockSkew
        // ClockSkew = TimeSpan.FromMinutes(5)
    };
    // 是否要求HTTPS
    options.RequireHttpsMetadata = false; // 仅限开发环境
});" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Services</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddAuthentication</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4"> =&gt;</span></span>
<span class="line"><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">     </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">DefaultAuthenticateScheme</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">JwtBearerDefaults</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">AuthenticationScheme</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">     </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">DefaultChallengeScheme</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">JwtBearerDefaults</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">AuthenticationScheme</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4"> })</span></span>
<span class="line"><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddJwtBearer</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4"> =&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">TokenValidationParameters</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TokenValidationParameters</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #6A9955">        // 配置解密密钥</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">TokenDecryptionKey</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">X509SecurityKey</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">X509Certificate2</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">             </span><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Configuration</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetValue</span><span style="color: #D4D4D4">&lt;</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4">&gt;(</span><span style="color: #CE9178">&quot;Certificate:Path&quot;</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4">             </span><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Configuration</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetValue</span><span style="color: #D4D4D4">&lt;</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4">&gt;(</span><span style="color: #CE9178">&quot;Certificate:PassWord&quot;</span><span style="color: #D4D4D4">))</span></span>
<span class="line"><span style="color: #D4D4D4">         ),</span></span>
<span class="line"><span style="color: #6A9955">        // 配置证书的公钥</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">IssuerSigningKey</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">X509SecurityKey</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">X509Certificate2</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Configuration</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetValue</span><span style="color: #D4D4D4">&lt;</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4">&gt;(</span><span style="color: #CE9178">&quot;Certificate:Path&quot;</span><span style="color: #D4D4D4">), </span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Configuration</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetValue</span><span style="color: #D4D4D4">&lt;</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4">&gt;(</span><span style="color: #CE9178">&quot;Certificate:PassWord&quot;</span><span style="color: #D4D4D4">))</span></span>
<span class="line"><span style="color: #D4D4D4">        ),</span></span>
<span class="line"><span style="color: #6A9955">        // 验证令牌发行者</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">ValidateIssuer</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">ValidIssuer</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;https://localhost:7097/&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #6A9955">        // 验证令牌受众</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">ValidateAudience</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">ValidAudience</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;resource_api_1&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #6A9955">        // 验证令牌签名</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">ValidateIssuerSigningKey</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #6A9955">        // 验证令牌有效期</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">ValidateLifetime</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #6A9955">        // 如果需要时钟偏差，可以设置ClockSkew</span></span>
<span class="line"><span style="color: #6A9955">        // ClockSkew = TimeSpan.FromMinutes(5)</span></span>
<span class="line"><span style="color: #D4D4D4">    };</span></span>
<span class="line"><span style="color: #6A9955">    // 是否要求HTTPS</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">RequireHttpsMetadata</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">false</span><span style="color: #D4D4D4">; </span><span style="color: #6A9955">// 仅限开发环境</span></span>
<span class="line"><span style="color: #D4D4D4">});</span></span></code></pre></div>



<h3 class="wp-block-heading">2.身份验证(根据自己的控制器中的角色来添加)</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="builder.Services.AddAuthorization(options =&gt;
{
    options.AddPolicy(&quot;Admin&quot;, policy =&gt;
    {
        policy.RequireRole(&quot;Admin&quot;);
    });
    options.AddPolicy(&quot;User&quot;, policy =&gt;
    {
        policy.RequireRole(&quot;User&quot;);
    });
});" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Services</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddAuthorization</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4"> =&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddPolicy</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Admin&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">policy</span><span style="color: #D4D4D4"> =&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">policy</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">RequireRole</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Admin&quot;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    });</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddPolicy</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;User&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">policy</span><span style="color: #D4D4D4"> =&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">policy</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">RequireRole</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;User&quot;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    });</span></span>
<span class="line"><span style="color: #D4D4D4">});</span></span></code></pre></div>



<h3 class="wp-block-heading">3.路由相关</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="app.UseSwagger();
app.UseSwaggerUI();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.UseEndpoints(endpoints =&gt;
{
    endpoints.MapControllers();
});" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseSwagger</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseSwaggerUI</span><span style="color: #D4D4D4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseRouting</span><span style="color: #D4D4D4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseAuthentication</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseAuthorization</span><span style="color: #D4D4D4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseEndpoints</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">endpoints</span><span style="color: #D4D4D4"> =&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">endpoints</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">MapControllers</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">});</span></span></code></pre></div>



<h2 class="wp-block-heading">三. 控制器</h2>



<p>以下是示例,示例四种情况</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="[ApiController]
[Route(&quot;[controller]&quot;)]
public class WeatherForecastController : ControllerBase
{
    private static readonly string[] Summaries = new[]
    {
        &quot;Freezing&quot;, &quot;Bracing&quot;, &quot;Chilly&quot;, &quot;Cool&quot;, &quot;Mild&quot;, &quot;Warm&quot;, &quot;Balmy&quot;, &quot;Hot&quot;, &quot;Sweltering&quot;, &quot;Scorching&quot;
    };

    private readonly ILogger&lt;WeatherForecastController&gt; _logger;

    public WeatherForecastController(ILogger&lt;WeatherForecastController&gt; logger)
    {
        _logger = logger;
    }

    //无需授权
    [HttpGet(&quot;GetWeatherForecastV1&quot;)]
    public IEnumerable&lt;WeatherForecast&gt; GetV1()
    {
        return Enumerable.Range(1, 5).Select(index =&gt; new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray();
    }

    //通用授权
    [Authorize]
    [HttpGet(&quot;GetWeatherForecastV2&quot;)]
    public IEnumerable&lt;WeatherForecast&gt; GetV2()
    {
        return Enumerable.Range(1, 5).Select(index =&gt; new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray();
    }

    //特定角色授权
    [Authorize(&quot;Admin&quot;)]
    [HttpGet(&quot;GetWeatherForecastV3&quot;)]
    public IEnumerable&lt;WeatherForecast&gt; GetV3()
    {
        return Enumerable.Range(1, 5).Select(index =&gt; new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray();
    }

    //特定角色授权
    [Authorize(&quot;User&quot;)]
    [HttpGet(&quot;GetWeatherForecastV4&quot;)]
    public IEnumerable&lt;WeatherForecast&gt; GetV4()
    {
        return Enumerable.Range(1, 5).Select(index =&gt; new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray();
    }

}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">[</span><span style="color: #4EC9B0">ApiController</span><span style="color: #D4D4D4">]</span></span>
<span class="line"><span style="color: #D4D4D4">[</span><span style="color: #4EC9B0">Route</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;[controller]&quot;</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">WeatherForecastController</span><span style="color: #D4D4D4"> : </span><span style="color: #4EC9B0">ControllerBase</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">static</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">readonly</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4">[] </span><span style="color: #9CDCFE">Summaries</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4">[]</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #CE9178">&quot;Freezing&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&quot;Bracing&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&quot;Chilly&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&quot;Cool&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&quot;Mild&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&quot;Warm&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&quot;Balmy&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&quot;Hot&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&quot;Sweltering&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&quot;Scorching&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">readonly</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">ILogger</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">WeatherForecastController</span><span style="color: #D4D4D4">&gt; </span><span style="color: #9CDCFE">_logger</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">WeatherForecastController</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">ILogger</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">WeatherForecastController</span><span style="color: #D4D4D4">&gt; </span><span style="color: #9CDCFE">logger</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">_logger</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">logger</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">    //无需授权</span></span>
<span class="line"><span style="color: #D4D4D4">    [</span><span style="color: #4EC9B0">HttpGet</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;GetWeatherForecastV1&quot;</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IEnumerable</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">WeatherForecast</span><span style="color: #D4D4D4">&gt; </span><span style="color: #DCDCAA">GetV1</span><span style="color: #D4D4D4">()</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Enumerable</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Range</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">5</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">Select</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">WeatherForecast</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">Date</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">DateTime</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Now</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddDays</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">TemperatureC</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">Random</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Shared</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Next</span><span style="color: #D4D4D4">(-</span><span style="color: #B5CEA8">20</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">55</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">Summary</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">Summaries</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">Random</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Shared</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Next</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">Summaries</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Length</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #D4D4D4">        })</span></span>
<span class="line"><span style="color: #D4D4D4">        .</span><span style="color: #DCDCAA">ToArray</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">    //通用授权</span></span>
<span class="line"><span style="color: #D4D4D4">    [</span><span style="color: #4EC9B0">Authorize</span><span style="color: #D4D4D4">]</span></span>
<span class="line"><span style="color: #D4D4D4">    [</span><span style="color: #4EC9B0">HttpGet</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;GetWeatherForecastV2&quot;</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IEnumerable</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">WeatherForecast</span><span style="color: #D4D4D4">&gt; </span><span style="color: #DCDCAA">GetV2</span><span style="color: #D4D4D4">()</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Enumerable</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Range</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">5</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">Select</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">WeatherForecast</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">Date</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">DateTime</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Now</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddDays</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">TemperatureC</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">Random</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Shared</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Next</span><span style="color: #D4D4D4">(-</span><span style="color: #B5CEA8">20</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">55</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">Summary</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">Summaries</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">Random</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Shared</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Next</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">Summaries</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Length</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #D4D4D4">        })</span></span>
<span class="line"><span style="color: #D4D4D4">        .</span><span style="color: #DCDCAA">ToArray</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">    //特定角色授权</span></span>
<span class="line"><span style="color: #D4D4D4">    [</span><span style="color: #4EC9B0">Authorize</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Admin&quot;</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #D4D4D4">    [</span><span style="color: #4EC9B0">HttpGet</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;GetWeatherForecastV3&quot;</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IEnumerable</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">WeatherForecast</span><span style="color: #D4D4D4">&gt; </span><span style="color: #DCDCAA">GetV3</span><span style="color: #D4D4D4">()</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Enumerable</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Range</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">5</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">Select</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">WeatherForecast</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">Date</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">DateTime</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Now</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddDays</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">TemperatureC</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">Random</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Shared</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Next</span><span style="color: #D4D4D4">(-</span><span style="color: #B5CEA8">20</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">55</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">Summary</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">Summaries</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">Random</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Shared</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Next</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">Summaries</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Length</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #D4D4D4">        })</span></span>
<span class="line"><span style="color: #D4D4D4">        .</span><span style="color: #DCDCAA">ToArray</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">    //特定角色授权</span></span>
<span class="line"><span style="color: #D4D4D4">    [</span><span style="color: #4EC9B0">Authorize</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;User&quot;</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #D4D4D4">    [</span><span style="color: #4EC9B0">HttpGet</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;GetWeatherForecastV4&quot;</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IEnumerable</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">WeatherForecast</span><span style="color: #D4D4D4">&gt; </span><span style="color: #DCDCAA">GetV4</span><span style="color: #D4D4D4">()</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Enumerable</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Range</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">5</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">Select</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">WeatherForecast</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">Date</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">DateTime</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Now</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddDays</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">TemperatureC</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">Random</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Shared</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Next</span><span style="color: #D4D4D4">(-</span><span style="color: #B5CEA8">20</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">55</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">Summary</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">Summaries</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">Random</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Shared</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Next</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">Summaries</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Length</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #D4D4D4">        })</span></span>
<span class="line"><span style="color: #D4D4D4">        .</span><span style="color: #DCDCAA">ToArray</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p>appsettings</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="{
  &quot;Logging&quot;: {
    &quot;LogLevel&quot;: {
      &quot;Default&quot;: &quot;Information&quot;,
      &quot;Microsoft.AspNetCore&quot;: &quot;Warning&quot;
    }, 
  },  
  &quot;Certificate&quot;: {
    &quot;Path&quot;: &quot;Certificate/SelfSignedCertificate.pfx&quot;,
    &quot;PassWord&quot;: &quot;12345678&quot; 
  },
  &quot;AllowedHosts&quot;: &quot;*&quot;
}
" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #CE9178">&quot;Logging&quot;</span><span style="color: #D4D4D4">: {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;LogLevel&quot;</span><span style="color: #D4D4D4">: {</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #CE9178">&quot;Default&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;Information&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #CE9178">&quot;Microsoft.AspNetCore&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;Warning&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">    }, </span></span>
<span class="line"><span style="color: #D4D4D4">  },  </span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #CE9178">&quot;Certificate&quot;</span><span style="color: #D4D4D4">: {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;Path&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;Certificate/SelfSignedCertificate.pfx&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;PassWord&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;12345678&quot;</span><span style="color: #D4D4D4"> </span></span>
<span class="line"><span style="color: #D4D4D4">  },</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #CE9178">&quot;AllowedHosts&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;*&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre></div>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>OpenIddict身份验证与授权之: 二. 1(密码模式)验证服务器</title>
		<link>https://blog.mutadecheng.com/2024/06/04/openiddict%e8%ba%ab%e4%bb%bd%e9%aa%8c%e8%af%81%e4%b8%8e%e6%8e%88%e6%9d%83%e4%b9%8b-%e4%ba%8c-%e9%aa%8c%e8%af%81%e6%9c%8d%e5%8a%a1%e5%99%a8demo-identityprovider/</link>
		
		<dc:creator><![CDATA[木它]]></dc:creator>
		<pubDate>Tue, 04 Jun 2024 01:56:57 +0000</pubDate>
				<category><![CDATA[身份验证与授权]]></category>
		<guid isPermaLink="false">https://blog.mutadecheng.com/?p=131</guid>

					<description><![CDATA[密码模式(最简化示例) 为了简化步骤便于理解, 在密码模式下,我只生成两个项目,一个是验证服务器,一个是资源服 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h1 class="wp-block-heading">密码模式(最简化示例)</h1>



<p>为了简化步骤便于理解, 在密码模式下,我只生成两个项目,一个是验证服务器,一个是资源服务器,客户端用postman或者自带的swagger, 详情请看系列文章,此文章为密码模式中的验证服务器篇</p>



<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>OpenIddict 密码模式流程图</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.19.1/cytoscape.min.js"></script>
<style>
    #cy {
        width: 100%;
        height: 600px;
        display: block;
    }
</style>
</head>
<body>
<div id="cy"></div>
<script>
// 初始化 Cytoscape
var cy = cytoscape({
    container: document.getElementById('cy'), // 容器元素
    elements: [ // 节点和边的定义
        // 节点
        { data: { id: 'client', label: '客户端' } },
        { data: { id: 'auth_server', label: '授权服务器' } },
        { data: { id: 'resource_server', label: '资源服务器' } },
        // 边
        { data: { id: 'collect', source: 'client', target: 'auth_server', label: '1. 提交用户名和密码' } },
        { data: { id: 'validate_user', source: 'auth_server', target: 'auth_server', label: '2. 验证用户名和密码' } },
        { data: { id: 'validate_client', source: 'auth_server', target: 'auth_server', label: '3. 验证客户端' } },
        { data: { id: 'issue_token', source: 'auth_server', target: 'client', label: '4. 颁发令牌' } },
        { data: { id: 'access_resource', source: 'client', target: 'resource_server', label: '5. 使用访问令牌访问资源' } },
        { data: { id: 'refresh_token', source: 'client', target: 'auth_server', label: '6. 使用刷新令牌获取新的访问令牌' } },
    ],
    style: [ // 样式定义
        {
            selector: 'node',
            style: {
                'background-color': '#666',
                'label': 'data(label)',
                'text-valign': 'center',
                'color': '#fff',
                'text-outline-width': 2,
                'text-outline-color': '#666',
            }
        },
        {
            selector: 'edge',
            style: {
                'width': 3,
                'line-color': '#ccc',
                'target-arrow-color': '#ccc',
                'target-arrow-shape': 'triangle',
                'curve-style': 'bezier',
                'label': 'data(label)',
                'font-size': 10,
                'text-margin-x': 3,
                'text-margin-y': 3,
                'text-rotation': 'autorotate',
                'edge-text-rotation': 'autorotate',
                'text-outline-width': 2,
                'text-outline-color': '#ccc'
            }
        }
    ],
    layout: {
        name: 'cose',
        idealEdgeLength: 100,
        nodeOverlap: 20,
        refresh: 20,
        fit: true,
        padding: 30,
        randomize: false,
        componentSpacing: 100,
        nodeRepulsion: 400000,
        edgeElasticity: 100,
        nestingFactor: 5,
        gravity: 80,
        numIter: 1000,
        initialTemp: 200,
        coolingFactor: 0.95,
        minTemp: 1.0
    }
});

</script>
</body>
</html>




<h2 class="wp-block-heading">一. Nuget</h2>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code=" &lt;ItemGroup&gt;
   &lt;PackageReference Include=&quot;Microsoft.AspNetCore.Authentication.JwtBearer&quot; Version=&quot;6.0.31&quot; /&gt;
   &lt;PackageReference Include=&quot;Microsoft.AspNetCore.Identity.EntityFrameworkCore&quot; Version=&quot;6.0.31&quot; /&gt;
   &lt;PackageReference Include=&quot;Microsoft.EntityFrameworkCore.InMemory&quot; Version=&quot;6.0.31&quot; /&gt;
   &lt;PackageReference Include=&quot;Microsoft.EntityFrameworkCore.Sqlite&quot; Version=&quot;6.0.31&quot; /&gt;
   &lt;PackageReference Include=&quot;Microsoft.IdentityModel.Protocols.OpenIdConnect&quot; Version=&quot;7.6.0&quot; /&gt;
   &lt;PackageReference Include=&quot;OpenIddict.EntityFrameworkCore&quot; Version=&quot;5.6.0&quot; /&gt;
   &lt;PackageReference Include=&quot;OpenIddict.Server.AspNetCore&quot; Version=&quot;5.6.0&quot; /&gt;
   &lt;PackageReference Include=&quot;OpenIddict.Validation.AspNetCore&quot; Version=&quot;5.6.0&quot; /&gt;
   &lt;PackageReference Include=&quot;Swashbuckle.AspNetCore&quot; Version=&quot;6.6.2&quot; /&gt;
   &lt;PackageReference Include=&quot;System.Linq.Async&quot; Version=&quot;6.0.1&quot; /&gt;
 &lt;/ItemGroup&gt;" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">ItemGroup</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">PackageReference</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Include</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;Microsoft.AspNetCore.Authentication.JwtBearer&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Version</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;6.0.31&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #808080">/&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">PackageReference</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Include</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;Microsoft.AspNetCore.Identity.EntityFrameworkCore&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Version</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;6.0.31&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #808080">/&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">PackageReference</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Include</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;Microsoft.EntityFrameworkCore.InMemory&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Version</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;6.0.31&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #808080">/&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">PackageReference</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Include</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;Microsoft.EntityFrameworkCore.Sqlite&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Version</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;6.0.31&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #808080">/&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">PackageReference</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Include</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;Microsoft.IdentityModel.Protocols.OpenIdConnect&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Version</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;7.6.0&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #808080">/&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">PackageReference</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Include</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;OpenIddict.EntityFrameworkCore&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Version</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;5.6.0&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #808080">/&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">PackageReference</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Include</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;OpenIddict.Server.AspNetCore&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Version</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;5.6.0&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #808080">/&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">PackageReference</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Include</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;OpenIddict.Validation.AspNetCore&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Version</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;5.6.0&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #808080">/&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">PackageReference</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Include</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;Swashbuckle.AspNetCore&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Version</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;6.6.2&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #808080">/&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">PackageReference</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Include</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;System.Linq.Async&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Version</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;6.0.1&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #808080">/&gt;</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">ItemGroup</span><span style="color: #808080">&gt;</span></span></code></pre></div>



<h2 class="wp-block-heading">二. 实体类与数据库上下文</h2>



<h3 class="wp-block-heading">1.自定义的用户实体类，继承自IdentityUser</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="public class ApplicationUser : IdentityUser
{
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">ApplicationUser</span><span style="color: #D4D4D4"> : </span><span style="color: #4EC9B0">IdentityUser</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">2.自定义的角色实体类，继承自IdentityRole</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code=" public class ApplicationRole : IdentityRole
 {
     public ApplicationRole() : base() { }
     public ApplicationRole(string roleName) : base(roleName)
     {
     }
 }" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">ApplicationRole</span><span style="color: #D4D4D4"> : </span><span style="color: #4EC9B0">IdentityRole</span></span>
<span class="line"><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">     </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">ApplicationRole</span><span style="color: #D4D4D4">() : </span><span style="color: #569CD6">base</span><span style="color: #D4D4D4">() { }</span></span>
<span class="line"><span style="color: #D4D4D4">     </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">ApplicationRole</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">roleName</span><span style="color: #D4D4D4">) : </span><span style="color: #569CD6">base</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">roleName</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">     {</span></span>
<span class="line"><span style="color: #D4D4D4">     }</span></span>
<span class="line"><span style="color: #D4D4D4"> }</span></span></code></pre></div>



<h3 class="wp-block-heading">3.应用程序数据库上下文</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="//string：指定了用户ID和角色ID的数据类型。在这个例子中，ID被指定为字符串类型。
public class ApplicationDbContext : IdentityDbContext&lt;ApplicationUser, ApplicationRole, string&gt;
{
    public DbSet&lt;ApplicationRole&gt;? ApplicationRoles { get; set; }
    public ApplicationDbContext(DbContextOptions&lt;ApplicationDbContext&gt; options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        // 配置OpenIddict实体
        builder.UseOpenIddict();
    }
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">//string：指定了用户ID和角色ID的数据类型。在这个例子中，ID被指定为字符串类型。</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">ApplicationDbContext</span><span style="color: #D4D4D4"> : </span><span style="color: #4EC9B0">IdentityDbContext</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">ApplicationUser</span><span style="color: #D4D4D4">, </span><span style="color: #4EC9B0">ApplicationRole</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">DbSet</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">ApplicationRole</span><span style="color: #D4D4D4">&gt;? </span><span style="color: #9CDCFE">ApplicationRoles</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">get</span><span style="color: #D4D4D4">; </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4">; }</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">ApplicationDbContext</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">DbContextOptions</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">ApplicationDbContext</span><span style="color: #D4D4D4">&gt; </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">        : </span><span style="color: #569CD6">base</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">protected</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">override</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">void</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">OnModelCreating</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">ModelBuilder</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">base</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">OnModelCreating</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">        // 配置OpenIddict实体</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseOpenIddict</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<h2 class="wp-block-heading">三. OpenIddict拓展</h2>



<h3 class="wp-block-heading">拓展源码</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="public static class OpenIddictExtension
{
    public static IServiceCollection AddOpenIddictServices(this IServiceCollection services, IConfiguration Configuration)
    {
        services.AddDbContext&lt;ApplicationDbContext&gt;(options =&gt;
        {
            // 配置数据库提供程序
            options.UseSqlite(Configuration.GetConnectionString(&quot;DefaultConnection&quot;));
            options.UseOpenIddict();
        });

        // 添加ASP.NET Core Identity配置
        services.AddIdentity&lt;ApplicationUser, ApplicationRole&gt;()
            .AddEntityFrameworkStores&lt;ApplicationDbContext&gt;()
            .AddDefaultTokenProviders();

        // 配置OpenIddict
        services.AddOpenIddict()
            // 配置 OpenIddict 核心组件
            .AddCore(options =&gt;
            {
                options.UseEntityFrameworkCore()
                       .UseDbContext&lt;ApplicationDbContext&gt;();
            })
            // 配置 OpenIddict 服务器
            .AddServer(options =&gt;
            {
                #region 模式
                //1.授权码模式
                options.AllowAuthorizationCodeFlow();
                //2. 简化模式
                //     适用于无法进行客户端认证的客户端，如纯前端应用。由于安全性较低，这种模式已不再推荐使用。
                //3. 密码模式
                options.AllowPasswordFlow();
                //4. 客户端凭据模式
                //options.AllowClientCredentialsFlow();
                #endregion
                
                #region 证书
                // 签名证书, 仅限开发环境使用
                //options.AddDevelopmentSigningCertificate();
                //options.AddDevelopmentEncryptionCertificate();

                // 使用自己的证书进行签名
                options.AddSigningCertificate(new X509Certificate2(Configuration.GetValue&lt;string&gt;(&quot;Certificate:Path&quot;), Configuration.GetValue&lt;string&gt;(&quot;Certificate:PassWord&quot;)));
                options.AddEncryptionCertificate(new X509Certificate2(Configuration.GetValue&lt;string&gt;(&quot;Certificate:Path&quot;), Configuration.GetValue&lt;string&gt;(&quot;Certificate:PassWord&quot;)));

                // 禁用 访问令牌加密
                //options.DisableAccessTokenEncryption();
                #endregion
                
                #region 其他必要的配置
                //token过期时间
                options.SetAccessTokenLifetime(TimeSpan.FromHours(6));

                // 设置令牌和授权端点
                options.SetTokenEndpointUris(&quot;/connect/token&quot;)
                       .SetAuthorizationEndpointUris(&quot;/connect/authorize&quot;)
                       .SetUserinfoEndpointUris(&quot;/connect/userinfo&quot;)
                       ;

                // 重要：配置与 ASP.NET Core 的集成
                options.UseAspNetCore()
                       .EnableAuthorizationEndpointPassthrough()//这个调用配置 OpenIddict 对于授权请求不要自己处理，而是直接传递给 ASP.NET Core。这意味着你需要在你的 ASP.NET Core 应用中自己实现授权端点的逻辑。
                       .EnableTokenEndpointPassthrough()//允许 OpenIddict 将令牌请求直接传递给 ASP.NET Core，你需要自己处理这些请求。
                       //.EnableLogoutEndpointPassthrough()//启用此选项可以让 OpenIddict 将注销请求直接传递给 ASP.NET Core，需要你自己实现注销逻辑。
                       ;
                #endregion
            })
            .AddValidation(options =&gt;
            {
                // 配置受众验证
                options.AddAudiences(&quot;api&quot;);
            });
        return services;
    }

    //自动创建表
    public static IApplicationBuilder UseOpenIddict(this IApplicationBuilder app)
    {
        using (IServiceScope serviceScope = app.ApplicationServices.CreateScope())
        {
            //自动创建表
            var context = serviceScope.ServiceProvider.GetRequiredService&lt;ApplicationDbContext&gt;();

            // 先删除数据库
            context.Database.EnsureDeleted();

            // 再重新创建数据库
            context.Database.EnsureCreated();

            // 调用种子数据方法
            SeedEssentialsAsync(serviceScope.ServiceProvider).Wait();

            // 调用OpenIddict种子数据方法
            SeedOpenIddictAsync(serviceScope.ServiceProvider).Wait();
        }
        return app;
    }

    // 生成种子数据
    private static async Task SeedEssentialsAsync(IServiceProvider serviceProvider)
    {
        // 获取数据库上下文
        var context = serviceProvider.GetRequiredService&lt;ApplicationDbContext&gt;();
        // 获取用户和角色管理器
        var userManager = serviceProvider.GetRequiredService&lt;UserManager&lt;ApplicationUser&gt;&gt;();
        var roleManager = serviceProvider.GetRequiredService&lt;RoleManager&lt;ApplicationRole&gt;&gt;();

        // 添加角色种子数据
        if (!await roleManager.RoleExistsAsync(&quot;Admin&quot;))
        {
            await roleManager.CreateAsync(new ApplicationRole { Name = &quot;Admin&quot; });
        }

        // 添加用户种子数据
        if (await userManager.FindByNameAsync(&quot;admin&quot;) == null)
        {
            var user = new ApplicationUser { UserName = &quot;admin&quot;, Email = &quot;123@qq.com&quot; };
            await userManager.CreateAsync(user, &quot;QWE@qwe123&quot;);
            await userManager.AddToRoleAsync(user, &quot;Admin&quot;);
        }
        
    }

    // 生成种子api
    public static async Task SeedOpenIddictAsync(IServiceProvider serviceProvider)
    {
        // 获取OpenIddict的应用、作用域和授权管理器
        var applicationManager = serviceProvider.GetRequiredService&lt;IOpenIddictApplicationManager&gt;();
        var scopeManager = serviceProvider.GetRequiredService&lt;IOpenIddictScopeManager&gt;();


        // 检查并添加作用域
        if (await scopeManager.FindByNameAsync(&quot;api&quot;) == null)
        {
            await scopeManager.CreateAsync(new OpenIddictScopeDescriptor
            {
                Name = &quot;api&quot;,
                DisplayName = &quot;Access to API&quot;,
                Resources = { &quot;resource_api_1&quot; },
            });
        }

        #region 检查并添加API资源 (只需要token就可访问的客户端)
        if (await applicationManager.FindByClientIdAsync(&quot;clientid&quot;) == null)
        {
            await applicationManager.CreateAsync(new OpenIddictApplicationDescriptor
            {
                ClientId = &quot;clientid&quot;,
                DisplayName = &quot;Demo.Resources.WebApi&quot;,
                Permissions =
                {
                    OpenIddictConstants.Permissions.Endpoints.Token,
                    OpenIddictConstants.Permissions.GrantTypes.Password,
                    OpenIddictConstants.Permissions.GrantTypes.RefreshToken,

                    // 这里添加允许的作用域
                    OpenIddictConstants.Permissions.Prefixes.Scope + &quot;api&quot;,
                    // 添加offline_access作用域
                    OpenIddictConstants.Permissions.Prefixes.Scope + OpenIddictConstants.Scopes.OfflineAccess, 
                },

            });
        }
        #endregion
    }

}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">static</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">OpenIddictExtension</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">static</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IServiceCollection</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">AddOpenIddictServices</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">this</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IServiceCollection</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">services</span><span style="color: #D4D4D4">, </span><span style="color: #4EC9B0">IConfiguration</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Configuration</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">services</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddDbContext</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">ApplicationDbContext</span><span style="color: #D4D4D4">&gt;(</span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4"> =&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #6A9955">            // 配置数据库提供程序</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseSqlite</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">Configuration</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetConnectionString</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;DefaultConnection&quot;</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseOpenIddict</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">        // 添加ASP.NET Core Identity配置</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">services</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddIdentity</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">ApplicationUser</span><span style="color: #D4D4D4">, </span><span style="color: #4EC9B0">ApplicationRole</span><span style="color: #D4D4D4">&gt;()</span></span>
<span class="line"><span style="color: #D4D4D4">            .</span><span style="color: #DCDCAA">AddEntityFrameworkStores</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">ApplicationDbContext</span><span style="color: #D4D4D4">&gt;()</span></span>
<span class="line"><span style="color: #D4D4D4">            .</span><span style="color: #DCDCAA">AddDefaultTokenProviders</span><span style="color: #D4D4D4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">        // 配置OpenIddict</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">services</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddOpenIddict</span><span style="color: #D4D4D4">()</span></span>
<span class="line"><span style="color: #6A9955">            // 配置 OpenIddict 核心组件</span></span>
<span class="line"><span style="color: #D4D4D4">            .</span><span style="color: #DCDCAA">AddCore</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4"> =&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            {</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseEntityFrameworkCore</span><span style="color: #D4D4D4">()</span></span>
<span class="line"><span style="color: #D4D4D4">                       .</span><span style="color: #DCDCAA">UseDbContext</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">ApplicationDbContext</span><span style="color: #D4D4D4">&gt;();</span></span>
<span class="line"><span style="color: #D4D4D4">            })</span></span>
<span class="line"><span style="color: #6A9955">            // 配置 OpenIddict 服务器</span></span>
<span class="line"><span style="color: #D4D4D4">            .</span><span style="color: #DCDCAA">AddServer</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4"> =&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            {</span></span>
<span class="line"><span style="color: #569CD6">                #region </span><span style="color: #CE9178">模式</span></span>
<span class="line"><span style="color: #6A9955">                //1.授权码模式</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AllowAuthorizationCodeFlow</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #6A9955">                //2. 简化模式</span></span>
<span class="line"><span style="color: #6A9955">                //     适用于无法进行客户端认证的客户端，如纯前端应用。由于安全性较低，这种模式已不再推荐使用。</span></span>
<span class="line"><span style="color: #6A9955">                //3. 密码模式</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AllowPasswordFlow</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #6A9955">                //4. 客户端凭据模式</span></span>
<span class="line"><span style="color: #6A9955">                //options.AllowClientCredentialsFlow();</span></span>
<span class="line"><span style="color: #569CD6">                #endregion</span></span>
<span class="line"><span style="color: #D4D4D4">                </span></span>
<span class="line"><span style="color: #569CD6">                #region </span><span style="color: #CE9178">证书</span></span>
<span class="line"><span style="color: #6A9955">                // 签名证书, 仅限开发环境使用</span></span>
<span class="line"><span style="color: #6A9955">                //options.AddDevelopmentSigningCertificate();</span></span>
<span class="line"><span style="color: #6A9955">                //options.AddDevelopmentEncryptionCertificate();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">                // 使用自己的证书进行签名</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddSigningCertificate</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">X509Certificate2</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">Configuration</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetValue</span><span style="color: #D4D4D4">&lt;</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4">&gt;(</span><span style="color: #CE9178">&quot;Certificate:Path&quot;</span><span style="color: #D4D4D4">), </span><span style="color: #9CDCFE">Configuration</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetValue</span><span style="color: #D4D4D4">&lt;</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4">&gt;(</span><span style="color: #CE9178">&quot;Certificate:PassWord&quot;</span><span style="color: #D4D4D4">)));</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddEncryptionCertificate</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">X509Certificate2</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">Configuration</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetValue</span><span style="color: #D4D4D4">&lt;</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4">&gt;(</span><span style="color: #CE9178">&quot;Certificate:Path&quot;</span><span style="color: #D4D4D4">), </span><span style="color: #9CDCFE">Configuration</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetValue</span><span style="color: #D4D4D4">&lt;</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4">&gt;(</span><span style="color: #CE9178">&quot;Certificate:PassWord&quot;</span><span style="color: #D4D4D4">)));</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">                // 禁用 访问令牌加密</span></span>
<span class="line"><span style="color: #6A9955">                //options.DisableAccessTokenEncryption();</span></span>
<span class="line"><span style="color: #569CD6">                #endregion</span></span>
<span class="line"><span style="color: #D4D4D4">                </span></span>
<span class="line"><span style="color: #569CD6">                #region </span><span style="color: #CE9178">其他必要的配置</span></span>
<span class="line"><span style="color: #6A9955">                //token过期时间</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">SetAccessTokenLifetime</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">TimeSpan</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">FromHours</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">6</span><span style="color: #D4D4D4">));</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">                // 设置令牌和授权端点</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">SetTokenEndpointUris</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;/connect/token&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">                       .</span><span style="color: #DCDCAA">SetAuthorizationEndpointUris</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;/connect/authorize&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">                       .</span><span style="color: #DCDCAA">SetUserinfoEndpointUris</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;/connect/userinfo&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">                       ;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">                // 重要：配置与 ASP.NET Core 的集成</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseAspNetCore</span><span style="color: #D4D4D4">()</span></span>
<span class="line"><span style="color: #D4D4D4">                       .</span><span style="color: #DCDCAA">EnableAuthorizationEndpointPassthrough</span><span style="color: #D4D4D4">()</span><span style="color: #6A9955">//这个调用配置 OpenIddict 对于授权请求不要自己处理，而是直接传递给 ASP.NET Core。这意味着你需要在你的 ASP.NET Core 应用中自己实现授权端点的逻辑。</span></span>
<span class="line"><span style="color: #D4D4D4">                       .</span><span style="color: #DCDCAA">EnableTokenEndpointPassthrough</span><span style="color: #D4D4D4">()</span><span style="color: #6A9955">//允许 OpenIddict 将令牌请求直接传递给 ASP.NET Core，你需要自己处理这些请求。</span></span>
<span class="line"><span style="color: #6A9955">                       //.EnableLogoutEndpointPassthrough()//启用此选项可以让 OpenIddict 将注销请求直接传递给 ASP.NET Core，需要你自己实现注销逻辑。</span></span>
<span class="line"><span style="color: #D4D4D4">                       ;</span></span>
<span class="line"><span style="color: #569CD6">                #endregion</span></span>
<span class="line"><span style="color: #D4D4D4">            })</span></span>
<span class="line"><span style="color: #D4D4D4">            .</span><span style="color: #DCDCAA">AddValidation</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4"> =&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            {</span></span>
<span class="line"><span style="color: #6A9955">                // 配置受众验证</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddAudiences</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;api&quot;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">            });</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">services</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">    //自动创建表</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">static</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IApplicationBuilder</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">UseOpenIddict</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">this</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IApplicationBuilder</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">using</span><span style="color: #D4D4D4"> (</span><span style="color: #4EC9B0">IServiceScope</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">serviceScope</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">ApplicationServices</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">CreateScope</span><span style="color: #D4D4D4">())</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #6A9955">            //自动创建表</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">context</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">serviceScope</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">ServiceProvider</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetRequiredService</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">ApplicationDbContext</span><span style="color: #D4D4D4">&gt;();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">            // 先删除数据库</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">context</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Database</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">EnsureDeleted</span><span style="color: #D4D4D4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">            // 再重新创建数据库</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">context</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Database</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">EnsureCreated</span><span style="color: #D4D4D4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">            // 调用种子数据方法</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #DCDCAA">SeedEssentialsAsync</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">serviceScope</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">ServiceProvider</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">Wait</span><span style="color: #D4D4D4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">            // 调用OpenIddict种子数据方法</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #DCDCAA">SeedOpenIddictAsync</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">serviceScope</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">ServiceProvider</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">Wait</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">    // 生成种子数据</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">static</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">async</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Task</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">SeedEssentialsAsync</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">IServiceProvider</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">serviceProvider</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #6A9955">        // 获取数据库上下文</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">context</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">serviceProvider</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetRequiredService</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">ApplicationDbContext</span><span style="color: #D4D4D4">&gt;();</span></span>
<span class="line"><span style="color: #6A9955">        // 获取用户和角色管理器</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">userManager</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">serviceProvider</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetRequiredService</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">UserManager</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">ApplicationUser</span><span style="color: #D4D4D4">&gt;&gt;();</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">roleManager</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">serviceProvider</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetRequiredService</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">RoleManager</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">ApplicationRole</span><span style="color: #D4D4D4">&gt;&gt;();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">        // 添加角色种子数据</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (!</span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">roleManager</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">RoleExistsAsync</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Admin&quot;</span><span style="color: #D4D4D4">))</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">roleManager</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">CreateAsync</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">ApplicationRole</span><span style="color: #D4D4D4"> { </span><span style="color: #9CDCFE">Name</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;Admin&quot;</span><span style="color: #D4D4D4"> });</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">        // 添加用户种子数据</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">userManager</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">FindByNameAsync</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;admin&quot;</span><span style="color: #D4D4D4">) == </span><span style="color: #569CD6">null</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">ApplicationUser</span><span style="color: #D4D4D4"> { </span><span style="color: #9CDCFE">UserName</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;admin&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">Email</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;123@qq.com&quot;</span><span style="color: #D4D4D4"> };</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">userManager</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">CreateAsync</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&quot;QWE@qwe123&quot;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">userManager</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddToRoleAsync</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&quot;Admin&quot;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">        </span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">    // 生成种子api</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">static</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">async</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Task</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">SeedOpenIddictAsync</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">IServiceProvider</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">serviceProvider</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #6A9955">        // 获取OpenIddict的应用、作用域和授权管理器</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">applicationManager</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">serviceProvider</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetRequiredService</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">IOpenIddictApplicationManager</span><span style="color: #D4D4D4">&gt;();</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">scopeManager</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">serviceProvider</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetRequiredService</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">IOpenIddictScopeManager</span><span style="color: #D4D4D4">&gt;();</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">        // 检查并添加作用域</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">scopeManager</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">FindByNameAsync</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;api&quot;</span><span style="color: #D4D4D4">) == </span><span style="color: #569CD6">null</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">scopeManager</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">CreateAsync</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">OpenIddictScopeDescriptor</span></span>
<span class="line"><span style="color: #D4D4D4">            {</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">Name</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;api&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">DisplayName</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;Access to API&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">Resources</span><span style="color: #D4D4D4"> = { </span><span style="color: #CE9178">&quot;resource_api_1&quot;</span><span style="color: #D4D4D4"> },</span></span>
<span class="line"><span style="color: #D4D4D4">            });</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">        #region </span><span style="color: #CE9178">检查并添加API资源 (只需要token就可访问的客户端)</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">applicationManager</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">FindByClientIdAsync</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;clientid&quot;</span><span style="color: #D4D4D4">) == </span><span style="color: #569CD6">null</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">applicationManager</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">CreateAsync</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">OpenIddictApplicationDescriptor</span></span>
<span class="line"><span style="color: #D4D4D4">            {</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">ClientId</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;clientid&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">DisplayName</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;Demo.Resources.WebApi&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">Permissions</span><span style="color: #D4D4D4"> =</span></span>
<span class="line"><span style="color: #D4D4D4">                {</span></span>
<span class="line"><span style="color: #D4D4D4">                    </span><span style="color: #9CDCFE">OpenIddictConstants</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Permissions</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Endpoints</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Token</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">                    </span><span style="color: #9CDCFE">OpenIddictConstants</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Permissions</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">GrantTypes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Password</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">                    </span><span style="color: #9CDCFE">OpenIddictConstants</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Permissions</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">GrantTypes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">RefreshToken</span><span style="color: #D4D4D4">,</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">                    // 这里添加允许的作用域</span></span>
<span class="line"><span style="color: #D4D4D4">                    </span><span style="color: #9CDCFE">OpenIddictConstants</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Permissions</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Prefixes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Scope</span><span style="color: #D4D4D4"> + </span><span style="color: #CE9178">&quot;api&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #6A9955">                    // 添加offline_access作用域</span></span>
<span class="line"><span style="color: #D4D4D4">                    </span><span style="color: #9CDCFE">OpenIddictConstants</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Permissions</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Prefixes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Scope</span><span style="color: #D4D4D4"> + </span><span style="color: #9CDCFE">OpenIddictConstants</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Scopes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">OfflineAccess</span><span style="color: #D4D4D4">, </span></span>
<span class="line"><span style="color: #D4D4D4">                },</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            });</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #569CD6">        #endregion</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">使用拓展</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="builder.Services.AddOpenIddictServices(builder.Configuration);

app.UseHttpsRedirection();

//种子数据
app.UseOpenIddict();

//下面是需要的,以便于访问端点
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =&gt;
{
    endpoints.MapControllers();
});" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Services</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddOpenIddictServices</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">builder</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Configuration</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseHttpsRedirection</span><span style="color: #D4D4D4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">//种子数据</span></span>
<span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseOpenIddict</span><span style="color: #D4D4D4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">//下面是需要的,以便于访问端点</span></span>
<span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseRouting</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseAuthentication</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseAuthorization</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #9CDCFE">app</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">UseEndpoints</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">endpoints</span><span style="color: #D4D4D4"> =&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">endpoints</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">MapControllers</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">});</span></span></code></pre></div>



<h2 class="wp-block-heading">四.控制器</h2>



<h3 class="wp-block-heading">1.登录Dto</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="public class LoginDto
{
    public string username { get; set; } = &quot;admin&quot;;
    public string password { get; set; } = &quot;123546&quot;;
    public string grant_type { get; set; } = &quot;password&quot;;
    public string client_id { get; set; } = &quot;clientid&quot;;
    public string scope { get; set; } = &quot;api&quot;;
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">LoginDto</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">username</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">get</span><span style="color: #D4D4D4">; </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4">; } = </span><span style="color: #CE9178">&quot;admin&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">password</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">get</span><span style="color: #D4D4D4">; </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4">; } = </span><span style="color: #CE9178">&quot;123546&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">grant_type</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">get</span><span style="color: #D4D4D4">; </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4">; } = </span><span style="color: #CE9178">&quot;password&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">client_id</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">get</span><span style="color: #D4D4D4">; </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4">; } = </span><span style="color: #CE9178">&quot;clientid&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">scope</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">get</span><span style="color: #D4D4D4">; </span><span style="color: #569CD6">set</span><span style="color: #D4D4D4">; } = </span><span style="color: #CE9178">&quot;api&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<h3 class="wp-block-heading">2.控制器(以下视情况自主选择二选一)</h3>



<h4 class="wp-block-heading">2.1 token未加密</h4>



<p>资源webapi未启用JWT加密, 在验证服务器的拓展中增加以下</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="// 禁用 访问令牌加密
options.DisableAccessTokenEncryption();" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">// 禁用 访问令牌加密</span></span>
<span class="line"><span style="color: #9CDCFE">options</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">DisableAccessTokenEncryption</span><span style="color: #D4D4D4">();</span></span></code></pre></div>



<p>此种此情况,只能<strong>手动</strong>从数据库中获取 Resources ,详见官网介绍<a href="https://documentation.openiddict.com/configuration/claim-destinations.html">https://documentation.openiddict.com/configuration/claim-destinations.html</a></p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="[ApiController]
[Route(&quot;connect&quot;)]
public class TokenController : ControllerBase
{
    private readonly SignInManager&lt;ApplicationUser&gt; _signInManager;
    private readonly UserManager&lt;ApplicationUser&gt; _userManager;
    private readonly IOpenIddictScopeManager _scopeManager;
    private readonly ApplicationDbContext _context;
    public TokenController( SignInManager&lt;ApplicationUser&gt; signInManager,
        UserManager&lt;ApplicationUser&gt; userManager,
        IOpenIddictScopeManager scopeManager,
        ApplicationDbContext context)
    {
        _signInManager = signInManager;
        _userManager = userManager;
        _scopeManager = scopeManager;
        _context = context;
    }

    // 资源webapi未启用JWT加密,只能手动从数据库中获取 Resources https://documentation.openiddict.com/configuration/claim-destinations.html
    [Consumes(&quot;application/x-www-form-urlencoded&quot;)]
    [HttpPost(&quot;token1&quot;)]
    public async Task&lt;IActionResult&gt; GetToken_NoEncryption([FromForm] LoginDto loginDto)
    {
        var user = await _userManager.FindByNameAsync(loginDto.username);
        if (user == null || !await _userManager.CheckPasswordAsync(user, loginDto.password))
        {
            return BadRequest(new { message = &quot;Username or password is incorrect.&quot; });
        }

        // 创建用户主体
        var principal = await _signInManager.CreateUserPrincipalAsync(user);

        // 确保添加了 sub 声明
        var identity = principal.Identity as ClaimsIdentity;
        identity.AddClaim(new Claim(OpenIddictConstants.Claims.Subject, user.Id));

        var ticket = new AuthenticationTicket(principal,
            new AuthenticationProperties(),
            OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);

        // 设置请求的作用域
        var requestedScopes = new[] { OpenIddictConstants.Scopes.OfflineAccess };
        requestedScopes = requestedScopes.Concat(loginDto.scope.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)).Distinct().ToArray();

        ticket.Principal.SetScopes(requestedScopes);

        // 根据请求的作用域动态设置资源（aud）
        foreach (var scope in requestedScopes)
        {
            var resources = await GetResourcesForScopeAsync(scope);
            foreach (var resource in resources)
            {
                // 这里我们直接将资源作为audience添加到声明中
                identity.AddClaim(new Claim(JwtRegisteredClaimNames.Aud, resource, ClaimValueTypes.String, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme));
            }
        }

        // 为所有声明设置目的地为访问令牌
        foreach (var claim in identity.Claims)
        {
            // 防止ASP.NET Core Identity的角色和策略声明被添加到访问令牌中
            if (claim.Type != ClaimTypes.Name &amp;&amp; claim.Type != ClaimTypes.NameIdentifier)
            {
                claim.SetDestinations(OpenIddictConstants.Destinations.AccessToken);
            }
        }

        return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
    }

    private async Task&lt;IEnumerable&lt;string&gt;&gt; GetResourcesForScopeAsync(string scope)
    {
        // 从数据库查询与指定作用域相关联的资源列表
        var resources = await _context.Set&lt;OpenIddictEntityFrameworkCoreScope&gt;()
            .Where(s =&gt; s.Name == scope)
            .Select(s =&gt; s.Resources)
            .FirstOrDefaultAsync();

        if (!string.IsNullOrEmpty(resources))
        {
            var resourceList = System.Text.Json.JsonSerializer.Deserialize&lt;List&lt;string&gt;&gt;(resources);
            return resourceList ?? Enumerable.Empty&lt;string&gt;();
        }

        return Enumerable.Empty&lt;string&gt;();
    }
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">[</span><span style="color: #4EC9B0">ApiController</span><span style="color: #D4D4D4">]</span></span>
<span class="line"><span style="color: #D4D4D4">[</span><span style="color: #4EC9B0">Route</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;connect&quot;</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TokenController</span><span style="color: #D4D4D4"> : </span><span style="color: #4EC9B0">ControllerBase</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">readonly</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">SignInManager</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">ApplicationUser</span><span style="color: #D4D4D4">&gt; </span><span style="color: #9CDCFE">_signInManager</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">readonly</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">UserManager</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">ApplicationUser</span><span style="color: #D4D4D4">&gt; </span><span style="color: #9CDCFE">_userManager</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">readonly</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IOpenIddictScopeManager</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">_scopeManager</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">readonly</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">ApplicationDbContext</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">_context</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">TokenController</span><span style="color: #D4D4D4">( </span><span style="color: #4EC9B0">SignInManager</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">ApplicationUser</span><span style="color: #D4D4D4">&gt; </span><span style="color: #9CDCFE">signInManager</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #4EC9B0">UserManager</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">ApplicationUser</span><span style="color: #D4D4D4">&gt; </span><span style="color: #9CDCFE">userManager</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #4EC9B0">IOpenIddictScopeManager</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">scopeManager</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #4EC9B0">ApplicationDbContext</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">context</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">_signInManager</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">signInManager</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">_userManager</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">userManager</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">_scopeManager</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">scopeManager</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">_context</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">context</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">    // 资源webapi未启用JWT加密,只能手动从数据库中获取 Resources https://documentation.openiddict.com/configuration/claim-destinations.html</span></span>
<span class="line"><span style="color: #D4D4D4">    [</span><span style="color: #4EC9B0">Consumes</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;application/x-www-form-urlencoded&quot;</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #D4D4D4">    [</span><span style="color: #4EC9B0">HttpPost</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;token1&quot;</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">async</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Task</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">IActionResult</span><span style="color: #D4D4D4">&gt; </span><span style="color: #DCDCAA">GetToken_NoEncryption</span><span style="color: #D4D4D4">([</span><span style="color: #4EC9B0">FromForm</span><span style="color: #D4D4D4">] </span><span style="color: #4EC9B0">LoginDto</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">loginDto</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">_userManager</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">FindByNameAsync</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">loginDto</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">username</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4"> == </span><span style="color: #569CD6">null</span><span style="color: #D4D4D4"> || !</span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">_userManager</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">CheckPasswordAsync</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">loginDto</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">password</span><span style="color: #D4D4D4">))</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">BadRequest</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> { </span><span style="color: #9CDCFE">message</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;Username or password is incorrect.&quot;</span><span style="color: #D4D4D4"> });</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">        // 创建用户主体</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">principal</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">_signInManager</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">CreateUserPrincipalAsync</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">        // 确保添加了 sub 声明</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">identity</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">principal</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Identity</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">as</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">ClaimsIdentity</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">identity</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddClaim</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Claim</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">OpenIddictConstants</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Claims</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Subject</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Id</span><span style="color: #D4D4D4">));</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">ticket</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">AuthenticationTicket</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">principal</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">AuthenticationProperties</span><span style="color: #D4D4D4">(),</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">OpenIddictServerAspNetCoreDefaults</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">AuthenticationScheme</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">        // 设置请求的作用域</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">requestedScopes</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4">[] { </span><span style="color: #9CDCFE">OpenIddictConstants</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Scopes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">OfflineAccess</span><span style="color: #D4D4D4"> };</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">requestedScopes</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">requestedScopes</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Concat</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">loginDto</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">scope</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Split</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4">[] { </span><span style="color: #CE9178">&#39; &#39;</span><span style="color: #D4D4D4"> }, </span><span style="color: #9CDCFE">StringSplitOptions</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">RemoveEmptyEntries</span><span style="color: #D4D4D4">)).</span><span style="color: #DCDCAA">Distinct</span><span style="color: #D4D4D4">().</span><span style="color: #DCDCAA">ToArray</span><span style="color: #D4D4D4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">ticket</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Principal</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">SetScopes</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">requestedScopes</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">        // 根据请求的作用域动态设置资源（aud）</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">foreach</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">scope</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">in</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">requestedScopes</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">resources</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">GetResourcesForScopeAsync</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">scope</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">foreach</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">resource</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">in</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">resources</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">            {</span></span>
<span class="line"><span style="color: #6A9955">                // 这里我们直接将资源作为audience添加到声明中</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">identity</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddClaim</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Claim</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">JwtRegisteredClaimNames</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Aud</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">resource</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">ClaimValueTypes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">String</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">OpenIddictServerAspNetCoreDefaults</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">AuthenticationScheme</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">        // 为所有声明设置目的地为访问令牌</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">foreach</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">claim</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">in</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">identity</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Claims</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #6A9955">            // 防止ASP.NET Core Identity的角色和策略声明被添加到访问令牌中</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">claim</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Type</span><span style="color: #D4D4D4"> != </span><span style="color: #9CDCFE">ClaimTypes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Name</span><span style="color: #D4D4D4"> &amp;&amp; </span><span style="color: #9CDCFE">claim</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Type</span><span style="color: #D4D4D4"> != </span><span style="color: #9CDCFE">ClaimTypes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">NameIdentifier</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">            {</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">claim</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">SetDestinations</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">OpenIddictConstants</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Destinations</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">AccessToken</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">SignIn</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">ticket</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Principal</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">ticket</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Properties</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">ticket</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">AuthenticationScheme</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">async</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Task</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">IEnumerable</span><span style="color: #D4D4D4">&lt;</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4">&gt;&gt; </span><span style="color: #DCDCAA">GetResourcesForScopeAsync</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">scope</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #6A9955">        // 从数据库查询与指定作用域相关联的资源列表</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">resources</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">_context</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Set</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">OpenIddictEntityFrameworkCoreScope</span><span style="color: #D4D4D4">&gt;()</span></span>
<span class="line"><span style="color: #D4D4D4">            .</span><span style="color: #DCDCAA">Where</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">s</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #9CDCFE">s</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Name</span><span style="color: #D4D4D4"> == </span><span style="color: #9CDCFE">scope</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">            .</span><span style="color: #DCDCAA">Select</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">s</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #9CDCFE">s</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Resources</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">            .</span><span style="color: #DCDCAA">FirstOrDefaultAsync</span><span style="color: #D4D4D4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (!</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">IsNullOrEmpty</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">resources</span><span style="color: #D4D4D4">))</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">resourceList</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">System</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Text</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Json</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">JsonSerializer</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Deserialize</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">List</span><span style="color: #D4D4D4">&lt;</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4">&gt;&gt;(</span><span style="color: #9CDCFE">resources</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">resourceList</span><span style="color: #D4D4D4"> ?? </span><span style="color: #9CDCFE">Enumerable</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Empty</span><span style="color: #D4D4D4">&lt;</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4">&gt;();</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Enumerable</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Empty</span><span style="color: #D4D4D4">&lt;</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4">&gt;();</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<h4 class="wp-block-heading">2.2 token加密(证书加密)</h4>



<p>此种情况会自动增加aud</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="[ApiController]
[Route(&quot;connect&quot;)]
public class TokenController : ControllerBase
{
    private readonly SignInManager&lt;ApplicationUser&gt; _signInManager;
    private readonly UserManager&lt;ApplicationUser&gt; _userManager;
    private readonly IOpenIddictScopeManager _scopeManager;
    private readonly ApplicationDbContext _context;
    public TokenController( SignInManager&lt;ApplicationUser&gt; signInManager,
        UserManager&lt;ApplicationUser&gt; userManager,
        IOpenIddictScopeManager scopeManager,
        ApplicationDbContext context)
    {
        _signInManager = signInManager;
        _userManager = userManager;
        _scopeManager = scopeManager;
        _context = context;
    }
    
    [Consumes(&quot;application/x-www-form-urlencoded&quot;)]
    [HttpPost(&quot;token&quot;)]
    public async Task&lt;IActionResult&gt; GetToken_Encryption([FromForm] LoginDto model)
    {
        var user = await _userManager.FindByNameAsync(model.username);
        if (user == null || !await _userManager.CheckPasswordAsync(user, model.password))
        {
            return BadRequest(new { message = &quot;Username or password is incorrect.&quot; });
        }

        // 创建用户主体
        var principal = await _signInManager.CreateUserPrincipalAsync(user);

        // 确保添加了 sub 声明
        var identity = principal.Identity as ClaimsIdentity;
        identity.AddClaim(new Claim(OpenIddictConstants.Claims.Subject, user.Id));

        // 设置作用域
        principal.SetScopes(new[]
        {
            OpenIddictConstants.Scopes.OpenId,
            OpenIddictConstants.Scopes.Profile,
            OpenIddictConstants.Scopes.OfflineAccess,
            &quot;api&quot;
        });

        // 设置资源
        principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync());

        // 设置声明目的地
        principal.SetDestinations(claim =&gt;
        {
            switch (claim.Type)
            {
                case Claims.Name when claim.Subject.HasScope(OpenIddictConstants.Scopes.Profile):
                    return new[] { OpenIddictConstants.Destinations.AccessToken, OpenIddictConstants.Destinations.IdentityToken };

                case &quot;secret_value&quot;:
                    return Array.Empty&lt;string&gt;();

                default:
                    return new[] { OpenIddictConstants.Destinations.AccessToken };
            }
        });

        var ticket = new AuthenticationTicket(principal,
            new AuthenticationProperties(),
            OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);

        return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
    }
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">[</span><span style="color: #4EC9B0">ApiController</span><span style="color: #D4D4D4">]</span></span>
<span class="line"><span style="color: #D4D4D4">[</span><span style="color: #4EC9B0">Route</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;connect&quot;</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TokenController</span><span style="color: #D4D4D4"> : </span><span style="color: #4EC9B0">ControllerBase</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">readonly</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">SignInManager</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">ApplicationUser</span><span style="color: #D4D4D4">&gt; </span><span style="color: #9CDCFE">_signInManager</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">readonly</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">UserManager</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">ApplicationUser</span><span style="color: #D4D4D4">&gt; </span><span style="color: #9CDCFE">_userManager</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">readonly</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IOpenIddictScopeManager</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">_scopeManager</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">readonly</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">ApplicationDbContext</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">_context</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">TokenController</span><span style="color: #D4D4D4">( </span><span style="color: #4EC9B0">SignInManager</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">ApplicationUser</span><span style="color: #D4D4D4">&gt; </span><span style="color: #9CDCFE">signInManager</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #4EC9B0">UserManager</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">ApplicationUser</span><span style="color: #D4D4D4">&gt; </span><span style="color: #9CDCFE">userManager</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #4EC9B0">IOpenIddictScopeManager</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">scopeManager</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #4EC9B0">ApplicationDbContext</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">context</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">_signInManager</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">signInManager</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">_userManager</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">userManager</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">_scopeManager</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">scopeManager</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">_context</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">context</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">    </span></span>
<span class="line"><span style="color: #D4D4D4">    [</span><span style="color: #4EC9B0">Consumes</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;application/x-www-form-urlencoded&quot;</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #D4D4D4">    [</span><span style="color: #4EC9B0">HttpPost</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;token&quot;</span><span style="color: #D4D4D4">)]</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">async</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Task</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">IActionResult</span><span style="color: #D4D4D4">&gt; </span><span style="color: #DCDCAA">GetToken_Encryption</span><span style="color: #D4D4D4">([</span><span style="color: #4EC9B0">FromForm</span><span style="color: #D4D4D4">] </span><span style="color: #4EC9B0">LoginDto</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">model</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">_userManager</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">FindByNameAsync</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">model</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">username</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4"> == </span><span style="color: #569CD6">null</span><span style="color: #D4D4D4"> || !</span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">_userManager</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">CheckPasswordAsync</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">model</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">password</span><span style="color: #D4D4D4">))</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">BadRequest</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> { </span><span style="color: #9CDCFE">message</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;Username or password is incorrect.&quot;</span><span style="color: #D4D4D4"> });</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">        // 创建用户主体</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">principal</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">_signInManager</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">CreateUserPrincipalAsync</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">        // 确保添加了 sub 声明</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">identity</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">principal</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Identity</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">as</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">ClaimsIdentity</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">identity</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">AddClaim</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Claim</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">OpenIddictConstants</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Claims</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Subject</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Id</span><span style="color: #D4D4D4">));</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">        // 设置作用域</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">principal</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">SetScopes</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4">[]</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">OpenIddictConstants</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Scopes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">OpenId</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">OpenIddictConstants</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Scopes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Profile</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">OpenIddictConstants</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Scopes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">OfflineAccess</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #CE9178">&quot;api&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">        // 设置资源</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">principal</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">SetResources</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">_scopeManager</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">ListResourcesAsync</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">principal</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetScopes</span><span style="color: #D4D4D4">()).</span><span style="color: #DCDCAA">ToListAsync</span><span style="color: #D4D4D4">());</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">        // 设置声明目的地</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">principal</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">SetDestinations</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">claim</span><span style="color: #D4D4D4"> =&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">switch</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">claim</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Type</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">            {</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #C586C0">case</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Claims</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">Name</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">when</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">claim</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Subject</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">HasScope</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">OpenIddictConstants</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Scopes</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Profile</span><span style="color: #D4D4D4">):</span></span>
<span class="line"><span style="color: #D4D4D4">                    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4">[] { </span><span style="color: #9CDCFE">OpenIddictConstants</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Destinations</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">AccessToken</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">OpenIddictConstants</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Destinations</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">IdentityToken</span><span style="color: #D4D4D4"> };</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #C586C0">case</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;secret_value&quot;</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">                    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Array</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Empty</span><span style="color: #D4D4D4">&lt;</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4">&gt;();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #C586C0">default</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">                    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4">[] { </span><span style="color: #9CDCFE">OpenIddictConstants</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Destinations</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">AccessToken</span><span style="color: #D4D4D4"> };</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"><span style="color: #D4D4D4">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">ticket</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">AuthenticationTicket</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">principal</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">AuthenticationProperties</span><span style="color: #D4D4D4">(),</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">OpenIddictServerAspNetCoreDefaults</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">AuthenticationScheme</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">SignIn</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">ticket</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Principal</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">ticket</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Properties</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">ticket</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">AuthenticationScheme</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<h2 class="wp-block-heading">五.客户端发送请求</h2>



<p>根据扩展中的种子数据, 根据自身编程语言或者用postman发送如下ajx请求</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="curl -X 'POST' \
  'https://localhost:7097/connect/token' \
  -H 'accept: */*' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'username=admin&amp;password=QWE%40qwe123&amp;grant_type=password&amp;client_id=clientid&amp;scope=api'" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #9CDCFE">curl</span><span style="color: #D4D4D4"> -</span><span style="color: #9CDCFE">X</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&#39;POST&#39;</span><span style="color: #D4D4D4"> \</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #CE9178">&#39;https://localhost:7097/connect/token&#39;</span><span style="color: #D4D4D4"> \</span></span>
<span class="line"><span style="color: #D4D4D4">  -</span><span style="color: #9CDCFE">H</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&#39;accept: */*&#39;</span><span style="color: #D4D4D4"> \</span></span>
<span class="line"><span style="color: #D4D4D4">  -</span><span style="color: #9CDCFE">H</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&#39;Content-Type: application/x-www-form-urlencoded&#39;</span><span style="color: #D4D4D4"> \</span></span>
<span class="line"><span style="color: #D4D4D4">  -</span><span style="color: #9CDCFE">d</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&#39;username=admin&amp;password=QWE%40qwe123&amp;grant_type=password&amp;client_id=clientid&amp;scope=api&#39;</span></span></code></pre></div>



<p>返回结果如下(就是很长&#8230;.很长), 此时就可以用access_token访问资源服务器了</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="{
  &quot;access_token&quot;: &quot;eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJraWQiOiJEODFDODgyQ0RDNTg3RkQ5RjlCQjNGQzg3OEY4RDg0MEZCMTlBMjQ4IiwidHlwIjoiYXQrand0IiwiY3R5IjoiSldUIn0.CijmUD9Wxosv1aaQjgEosG6GliK1s9lgBdZVl60Nx4F4W9HQ_8Wr1P8klQVS-NG9k9P7hcyjSJY64CXnpN57ZvTXdc2F7dKvYeNsKgaQRn7yGPwlcOUOgks02BuizIsw9Pl6_lDVHwJcecqcpRh3ec2MWB-aB21N0UA7fabFi4YHV7pzr_Mr0A8Krdxu8Xe0UC8f51Tk5tawAQA3rqwXm3ZXpDN8EzJxWkeiPmVpYEBKuoI6aouK4GgprMYJqL3-DBPbt5cT8-mPGlEfyQRhTrq5uxd-y6arbVXRbuJIhmTZIixdAC-gab8zSplwcc-IfTEUKHEk66YV7i1AzveQqQ._TEuhLFw7LInL7u6twY8Zg.FOaBEVwRNp3JKkc4rwSu7Ggm_vLkXYYQmWrY6RFBeE5SFDwOxxdcIdx-vBdwmBrIvCBVlKV5DEEhyjuI4p2XHANNn7ObIpvJnKYwZJm5_dM4AGE1-ZdYgL8o0v0O0ZnufU07zav9zcOtsXcujBV_sdYEEdDvW2-x0xc4so_z3dh4Z4PwsvYf2TiTqKPqjKF9lzcjjEzIGEMIDM-lEP7hLwd1Z200-Tqs8PeaFy1yXdx0jN5cOzCO_ehSTKo4IAxpuw5GUMJjjqQI25F5JaHEhctHbLQpc90ghrDYqGTeEDuVC-hgLz58b-ctw-iINtBJKFFQSCjyIFje4nz7dq2Ek_9pjPnC7RVk6MTg7utGEaI3altvUUJHnPznkBaFazOFZbWrVDC0Uy097WrNbk-GS1rij3YjvWQ5780BtpF8O7MDE_gnJRI_fUNQHO9AKU9Pi-GCIT8DRQmeNEBzGXcSh-NdF6rmHEJQuUq57Awan-aYBmVtDZRSm_zb9PbmccqZKcf9y-ahvlEXwnWU6QLU_herm3mDEmknexLw8X8y6M1pCPhqGaUE-flVk3aIU6mmJ2o9wvG_mQsxF7BAPA2AUVfiBCsCgK2rnAfuo-PU4A1qres6GXFU0TXkWvyZkLZ3O0cPW21nQLHwUAH1rfCMwMIg5vptUOts2CpcqKLNcFm3U5lbMGB-o15v7VDW_ioF88VKDzLi0kU_k89GVmmD4tzlhxEqu9fOhOHU49aBR7b7zV0eUlfI0ubNRZuox6ODPYLKor_7zn-JhPz4b_D32RhdyvTiXNzMotdahqjCWAkUKOWhRXQbZIbi0Bo3VnlQsg3HhIToZpLsfQOByynJpY4knH_A2k5BfOIw0CliVGkkM_QETJT5051lyfzixeV2_-bEHRj9MzHJqSlyK5ldZZDtcP8T456da2kp9TAc6s_wUYeiha7NbtbGOutFHdhGxHxiwja0QkVYAtGx3Gq3KXxrTeWb_VPSJz27RQk_hCH2RJQdfdR_UzEvKe-M4ncbDNPjCeJRHV7WGTeQDKYSnSqg6ZYVsXL5OD1QQxx7BYFXh681XAXOwpLAWu4OOa9brF2PTGC86DdyzmgCTsG8teju3N5gOepngEjgBz6XtV7zPUjBoCH6uegExgLgbjFZqbJcaRpzcb5RYGHis0mpdT9VzrExVANNaRqYTBfNdzynTsZmszkrB0SQKp4sPyg_NDupeNl9SGZRsPW8WhvItxAWE5g4fkuU0vVdsO6BxMG-qN473wwOPJVqS0kP0rLp0CLg3adPZtuSja0SItoXhBXiNf94ehDS4n90PrjL93L7huVZ4_NaMHOGk7CJ73H9hLRMKhRdcqFXVWDbxoseo-fBXLqLXMvNmk9_FSZMzaOKvMZmZddMk8nCAxwlAoHntSw_yU7-JUoNszk1qZofHYBqCJDwQFcTEMBPd18csYhXQiumRQ0HMQInVsVFBPNqPeR3sMpqPXW2xxfcbLnwBqTpbQrkvLDwFpfXcZ14RPnyTb-Ho5Pg-DR9p06cvfYq77SENayRaDhgMehhKlF0MIPycw2ob0e6S0cAxMxRzOgvMzm7wR0D4Y-RA9ZrqR8_s2tQPLJM8mdsTGRemsTsYkhwljw8ye-8zIHA3YeMahhCBUgHvDAW8QXDZPa5myxWnWC7gCL3q_fv3QAgFbgFGcWjQqg1dvxgQwHap14BjvWpa_RL3CmP7zr1fNoZdUi8thrSYOc6RG2zd1_wB-SuD_Sv4sofzOND0FG8r4ziq1V-IfS8Irio-HaOUdUYjye01Y0OhlgZDHesL0ZszWO1jReIQF76S9JtrVlAJTZTnWGEquJ7x7loZplD1tuxnvnBE5ZhBnuJEfLlWj8TXeAUQmDzYYIfKyw1jA9fAL1W7s_fQpJtYmrgEbY-kdToK-HOnxKJfJIZBoL7Vf-EQdWGDsDYL0EbULrSGWPmlCEb9dEZxPzffbRzY2Fi1O7DDp46C7fR33U7V5mkHycLWOvoG6_WQFTiA3j8HY_oIGLHE8ikDETGbtEMo0UBX5Cb14HQ.FIagVnKoIoUCO0qWtZf7mYGLOACWbSG2O0OcvR_xF5E&quot;,
  &quot;token_type&quot;: &quot;Bearer&quot;,
  &quot;expires_in&quot;: 21599,
  &quot;scope&quot;: &quot;openid profile offline_access api&quot;,
  &quot;id_token&quot;: &quot;eyJhbGciOiJSUzI1NiIsImtpZCI6IkQ4MUM4ODJDREM1ODdGRDlGOUJCM0ZDODc4RjhEODQwRkIxOUEyNDgiLCJ4NXQiOiIyQnlJTE54WWY5bjV1el9JZVBqWVFQc1pva2ciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2xvY2FsaG9zdDo3MDk3LyIsImV4cCI6MTcxNzQ5NDM2OCwiaWF0IjoxNzE3NDkzMTY4LCJhdWQiOiJjbGllbnRpZCIsInN1YiI6IjQ0ZTE3ZDE2LTgxOGUtNDJlMi04OTllLTAxODIzZWVhZmE0YiIsIm9pX2F1X2lkIjoiOGYwZDliNDEtMTQ3Ny00Zjk0LWIxOGUtOTZhNTdiNDgzYWE3IiwiYXpwIjoiY2xpZW50aWQiLCJhdF9oYXNoIjoiNy14amd0Y3dHM1NwbUhBYXhkNzFXQSIsIm9pX3Rrbl9pZCI6IjU5MjhmNjJkLTE2YzItNGUwNS1hODc2LTM3MDY2NjZlZTIxMSJ9.rH1MATGfGmy-k9l_W4TNIjpbKjkcTON2j_oGWQhaKpCvhP723FZ8b7ahBkX8ITByBadV_WEXb8y04NXNDAELh0e15S5tllKkUNMSkrgcQK3l5bh5LXb9nuKUtO2HrQfg3Wkavgu2caK7nzUj6fmh0vahnBOdoTXny1xFYLJP79UAdLKwNQ1Vpnvk6RttEG69SbAySAelE0XajocshmuAK2TVwVOBBNB4V16evjzD-alzDmr312r8RlCnul6AzSkpMf_TZ4-aYbhpdt6CZA8wqvWT-SioLlGB89PEN6-LKrSD0NtKN3wWMUTv-fVyXtFpJyd-SIGv9hYUHM1qgttTAA&quot;,
  &quot;refresh_token&quot;: &quot;eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJraWQiOiJEODFDODgyQ0RDNTg3RkQ5RjlCQjNGQzg3OEY4RDg0MEZCMTlBMjQ4IiwidHlwIjoib2lfcmVmdCtqd3QiLCJjdHkiOiJKV1QifQ.EUXrtSTDQ3NstYhpAYaugHs73gW2LZeuqRwGftuxVScLMm6u8VL1MSkXgaOYNclyPeeaaopfja8tIv6-ms9K2Kj-8M9EcOfPia_-daLWJM8fgH9_mOb_5AY_khLRWSnT5vV3sTvokDY6glSd7Egqlvlkrt-lMRRBxKJa9zh8Fswp68IYImtjnlHwMqC_BXkHPKMTVNGsRBAGVBd1p_FhPQjes_p9tR_jcf1s3u3ekQhaYo9-qg-WzfImDXbsLoW9o70zgT_I0k605NhxBxiEf_3kLHYogWsg5pdicZy8yPUwuOhtNu1sY-2QWPzmxq7lOWGteclThjGlh4ptzubeAw.vmNtqW7nQ95FuTHrkQnGSQ.jFiSeMwY8GxZjugGo_69H-S63F2zj2iwdvzQd_5MEZZudvRpF3OHV9n3tVcA_FKSXmo3H7Tl7jOdLE6oMWL5EjCM5ik0MU7hBN4IB-HOCJhEBC7VTmEF68HdqC7xbJX55rPNK2F4fna1cFAc0wr0w5eGaIccITpSoeqOPh2rmapjQw53lb_EY15D2XgRHiXX7qiw6-fBUBFjqI3J3eOcNWQC2Jg1JsN1okhvD5AVSRHjhzb4AwUXGTrV2xanm_p0YfQMohaObMQsOCZLazLCCWIYyA9LFJnyy6kV8pOpoyMh2CRiCB0ahBWf9cKYqpBbPMlls8g6_KUA_6TGgXwxxGMYL7RCu_B4NZtne6wnMCSAztO8Fu9swTREJOhygiAlW-eTTICuX3dyNC7QSjCk4ISdZvGXaK5UeBxMvVD85_QikAO2jqDxFk2O_xQEvHAZ9mXRiPtrcGedvUipo49q30abMCKW572lucahUTXQF_v_Y-OQ7Y0OVZr6KtkOpu71Ot-_8OBpKxMscGx3RqugQITLgShjLIrkCwSxykoCADKtu-ySIadjMViYW0sl4Xfdk4a-IFWeYfCYfCbXFzBxfNafuK9kRWZT2eo5poT-o17wmhI747s75FY6E7c0p9HFDbcxZ_U3KYzMlr6WrexVJqqb4qHO9CNPwxGlhP0WM3F0_JF44Drdcm2nDzyHurE2PmhagTaAAt-0qb2vz0jwdEZDj7vY93pShMoIQW3W_e_2NDAnBLMcZgANs5Oh8pHEbsaU7TgghTwqMgwnyS5uYqcEyqfRPCpoHXGbvrmxeiN1ODlsw0To7qk9NfAOPAV7z_CP7MCEHkpTJgI9cIYt9PEK_fxN2Bq8D0tyiah3Sc2TFVqvH5DbcgdaIG5wpV_S6pc55q-iU7lSgBzJziQiURHEJj1v8j_hP0n4HK9IADw6umEqNsz2KlpKWGMubDoh1ZwibNiweqwzH7C09kHMhlHifgjP7oLe5LGLagZyYD6BV9Z0io5vgl7imZrUiIxhA3vWZrrrZGi9j9KQQrkDuZxNuiUSW66-o13mJ_Ki3Z5kwA8gBssL27oAitL3l_rlhOhjcsACIU9AdA2eVu5xeijf_a4QqQEx8l2sbAzgNLJr4mOaYuUFQlPjFUaw30LXhf9VEa8ks8AVMhjRVtrfEekArrCBvUIrza5fzj-ofICtLoPRbd7K3fxT2NVB57zLra4YYchM5Cqgdqg26ETOpvUDQM8_G55Bw-zYWRQe6JmSpjj8_UBE1EE9L7pWy_6xFxk1Hci1G1dITdthPFlanQnzpX71fHLI69C_I71zrr_On5XIyjZHeGB7Fo2GPV_uurKuB_mmjMKuofpJ-fxuNPbDNUVO2qNg_mfC_aJIP8gCvetsrV-Zpv090hBewMnBrg6nh1rfRatFxKgCv7ZF-SWAb7MqHM4ngoZBoUdVy1gLFfQmmMPWsEB3P4A5tnOEornWAeEsYE92HAtD6AY0W6nSyAIdfHQURq_5DgVxtjqmrBDkYhc0DZmdcSSB4CuCQN7EQKrADAyw48jlldbKMZEDnhcUW855Hru37nf53z5JCp13b8extVI9Fb2ZNkxWhm3vvqOrxPjHGlONO7MesGzxYbmK76XtJzOcDStDydYR9nX7V4OR6zbgjTN517FSxcD9VMOc1r-DYo28K5a9sDOvoyasHIsvKDZlx5j472Vtm1q9JvcG-j1djbkfu462ymD0CjVZss_aGHl8Wn2t3ZFk44s5A1ceRY39jfC4iYzBl3ysTbmJF3uPRc-fZEeAMa239hBhWDOgi8f4udbLcHxbz3QEQBDMgsxTLpJKLfXL4ofJZy6BKdHQtMqokRKvTYTTIPtzoKJNgXycdD88o-C-I8MK8orLTN2reLjSevvg8fKR81rlWWoT1JN3pMzbmsns3Bi4AD1sA-5ZqWtyZiG1_KarZCwhvfe3iH-5PQaMVqOZoluwW9Y0CK9khfoB2gGDEUM7fqAB6ES_3oOA_CbXR6xh39BBQS--Qa1sOU_xaE7HTeL0S_QswHgUb8OJzvybaD19jE8eCFlPb1xkHIN7rDeqjWhtYs_RMmJUHkTzOjhCT6icaPZD8Q7aw0E0PW37K8f25lRNvwzlt-MI8K37aypoxwk0q4A2KKd5EWnPG-tNNQtcxRdIzQtZ7s3_G_TVMeJRiv86JKNXk_MrMFMJ6Ys7N_QcO4dtjFQTWdwag-tnm8j1fsB8AZFtUgLzsSyIKj2WfJQ9cw00UJggx14e5EggbvOYnMdMn7EC56dLyP_Ew1r4rciCyx4RPsk8r0djTXzU5HJ-Af3YM1yvn0-NMcsTky73E2LigXwdEIF4ohQjEzJEbuIGhbPCd8S0C2WZc7XRd6-ki-SHMCSbn87OMI5jsVHGX_8gFALaEqT02PHnMHHo5Z3XHjGck2TfI2kpBQ9q3UPomI0R6U7YR1w0CoPwFzKNosFh6y1d6T-ulpMIMOM3LoyZo3AvWb9c0K6Tnixky1S_5ALsIeg01dOVUSPYwTOI8Jw493cGdQ6SA6NvvZMCw20nAS2jRc_0e3QU6EAtN8srnVw7pzXysE939uVxbzhMyzStmWYH_KtV0tXC7uGam6x88V04ee-Um2oejVz6HbTSQQZJlE76ZUB5MC9cggk_DME1r1otWU2NuxPKtZZkeDpA1Xv6pDYarVLrI5QEXJB8xeh-upWepsQlJ3j9HPTC2NK5RQwp64c7DHqfGZ8WYY2N6-d3XLT7X6rpxxUT20ZeAkKyZ6Se9KljJWa4X8l5LsaO3ZidHBI.kQ_u2mD3-p2GUlKi3S4MxylC-tJhj1MmBf-70PPb35I&quot;
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">&quot;access_token&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJraWQiOiJEODFDODgyQ0RDNTg3RkQ5RjlCQjNGQzg3OEY4RDg0MEZCMTlBMjQ4IiwidHlwIjoiYXQrand0IiwiY3R5IjoiSldUIn0.CijmUD9Wxosv1aaQjgEosG6GliK1s9lgBdZVl60Nx4F4W9HQ_8Wr1P8klQVS-NG9k9P7hcyjSJY64CXnpN57ZvTXdc2F7dKvYeNsKgaQRn7yGPwlcOUOgks02BuizIsw9Pl6_lDVHwJcecqcpRh3ec2MWB-aB21N0UA7fabFi4YHV7pzr_Mr0A8Krdxu8Xe0UC8f51Tk5tawAQA3rqwXm3ZXpDN8EzJxWkeiPmVpYEBKuoI6aouK4GgprMYJqL3-DBPbt5cT8-mPGlEfyQRhTrq5uxd-y6arbVXRbuJIhmTZIixdAC-gab8zSplwcc-IfTEUKHEk66YV7i1AzveQqQ._TEuhLFw7LInL7u6twY8Zg.FOaBEVwRNp3JKkc4rwSu7Ggm_vLkXYYQmWrY6RFBeE5SFDwOxxdcIdx-vBdwmBrIvCBVlKV5DEEhyjuI4p2XHANNn7ObIpvJnKYwZJm5_dM4AGE1-ZdYgL8o0v0O0ZnufU07zav9zcOtsXcujBV_sdYEEdDvW2-x0xc4so_z3dh4Z4PwsvYf2TiTqKPqjKF9lzcjjEzIGEMIDM-lEP7hLwd1Z200-Tqs8PeaFy1yXdx0jN5cOzCO_ehSTKo4IAxpuw5GUMJjjqQI25F5JaHEhctHbLQpc90ghrDYqGTeEDuVC-hgLz58b-ctw-iINtBJKFFQSCjyIFje4nz7dq2Ek_9pjPnC7RVk6MTg7utGEaI3altvUUJHnPznkBaFazOFZbWrVDC0Uy097WrNbk-GS1rij3YjvWQ5780BtpF8O7MDE_gnJRI_fUNQHO9AKU9Pi-GCIT8DRQmeNEBzGXcSh-NdF6rmHEJQuUq57Awan-aYBmVtDZRSm_zb9PbmccqZKcf9y-ahvlEXwnWU6QLU_herm3mDEmknexLw8X8y6M1pCPhqGaUE-flVk3aIU6mmJ2o9wvG_mQsxF7BAPA2AUVfiBCsCgK2rnAfuo-PU4A1qres6GXFU0TXkWvyZkLZ3O0cPW21nQLHwUAH1rfCMwMIg5vptUOts2CpcqKLNcFm3U5lbMGB-o15v7VDW_ioF88VKDzLi0kU_k89GVmmD4tzlhxEqu9fOhOHU49aBR7b7zV0eUlfI0ubNRZuox6ODPYLKor_7zn-JhPz4b_D32RhdyvTiXNzMotdahqjCWAkUKOWhRXQbZIbi0Bo3VnlQsg3HhIToZpLsfQOByynJpY4knH_A2k5BfOIw0CliVGkkM_QETJT5051lyfzixeV2_-bEHRj9MzHJqSlyK5ldZZDtcP8T456da2kp9TAc6s_wUYeiha7NbtbGOutFHdhGxHxiwja0QkVYAtGx3Gq3KXxrTeWb_VPSJz27RQk_hCH2RJQdfdR_UzEvKe-M4ncbDNPjCeJRHV7WGTeQDKYSnSqg6ZYVsXL5OD1QQxx7BYFXh681XAXOwpLAWu4OOa9brF2PTGC86DdyzmgCTsG8teju3N5gOepngEjgBz6XtV7zPUjBoCH6uegExgLgbjFZqbJcaRpzcb5RYGHis0mpdT9VzrExVANNaRqYTBfNdzynTsZmszkrB0SQKp4sPyg_NDupeNl9SGZRsPW8WhvItxAWE5g4fkuU0vVdsO6BxMG-qN473wwOPJVqS0kP0rLp0CLg3adPZtuSja0SItoXhBXiNf94ehDS4n90PrjL93L7huVZ4_NaMHOGk7CJ73H9hLRMKhRdcqFXVWDbxoseo-fBXLqLXMvNmk9_FSZMzaOKvMZmZddMk8nCAxwlAoHntSw_yU7-JUoNszk1qZofHYBqCJDwQFcTEMBPd18csYhXQiumRQ0HMQInVsVFBPNqPeR3sMpqPXW2xxfcbLnwBqTpbQrkvLDwFpfXcZ14RPnyTb-Ho5Pg-DR9p06cvfYq77SENayRaDhgMehhKlF0MIPycw2ob0e6S0cAxMxRzOgvMzm7wR0D4Y-RA9ZrqR8_s2tQPLJM8mdsTGRemsTsYkhwljw8ye-8zIHA3YeMahhCBUgHvDAW8QXDZPa5myxWnWC7gCL3q_fv3QAgFbgFGcWjQqg1dvxgQwHap14BjvWpa_RL3CmP7zr1fNoZdUi8thrSYOc6RG2zd1_wB-SuD_Sv4sofzOND0FG8r4ziq1V-IfS8Irio-HaOUdUYjye01Y0OhlgZDHesL0ZszWO1jReIQF76S9JtrVlAJTZTnWGEquJ7x7loZplD1tuxnvnBE5ZhBnuJEfLlWj8TXeAUQmDzYYIfKyw1jA9fAL1W7s_fQpJtYmrgEbY-kdToK-HOnxKJfJIZBoL7Vf-EQdWGDsDYL0EbULrSGWPmlCEb9dEZxPzffbRzY2Fi1O7DDp46C7fR33U7V5mkHycLWOvoG6_WQFTiA3j8HY_oIGLHE8ikDETGbtEMo0UBX5Cb14HQ.FIagVnKoIoUCO0qWtZf7mYGLOACWbSG2O0OcvR_xF5E&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">&quot;token_type&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;Bearer&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">&quot;expires_in&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #B5CEA8">21599</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">&quot;scope&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;openid profile offline_access api&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">&quot;id_token&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;eyJhbGciOiJSUzI1NiIsImtpZCI6IkQ4MUM4ODJDREM1ODdGRDlGOUJCM0ZDODc4RjhEODQwRkIxOUEyNDgiLCJ4NXQiOiIyQnlJTE54WWY5bjV1el9JZVBqWVFQc1pva2ciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2xvY2FsaG9zdDo3MDk3LyIsImV4cCI6MTcxNzQ5NDM2OCwiaWF0IjoxNzE3NDkzMTY4LCJhdWQiOiJjbGllbnRpZCIsInN1YiI6IjQ0ZTE3ZDE2LTgxOGUtNDJlMi04OTllLTAxODIzZWVhZmE0YiIsIm9pX2F1X2lkIjoiOGYwZDliNDEtMTQ3Ny00Zjk0LWIxOGUtOTZhNTdiNDgzYWE3IiwiYXpwIjoiY2xpZW50aWQiLCJhdF9oYXNoIjoiNy14amd0Y3dHM1NwbUhBYXhkNzFXQSIsIm9pX3Rrbl9pZCI6IjU5MjhmNjJkLTE2YzItNGUwNS1hODc2LTM3MDY2NjZlZTIxMSJ9.rH1MATGfGmy-k9l_W4TNIjpbKjkcTON2j_oGWQhaKpCvhP723FZ8b7ahBkX8ITByBadV_WEXb8y04NXNDAELh0e15S5tllKkUNMSkrgcQK3l5bh5LXb9nuKUtO2HrQfg3Wkavgu2caK7nzUj6fmh0vahnBOdoTXny1xFYLJP79UAdLKwNQ1Vpnvk6RttEG69SbAySAelE0XajocshmuAK2TVwVOBBNB4V16evjzD-alzDmr312r8RlCnul6AzSkpMf_TZ4-aYbhpdt6CZA8wqvWT-SioLlGB89PEN6-LKrSD0NtKN3wWMUTv-fVyXtFpJyd-SIGv9hYUHM1qgttTAA&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">&quot;refresh_token&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&quot;eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJraWQiOiJEODFDODgyQ0RDNTg3RkQ5RjlCQjNGQzg3OEY4RDg0MEZCMTlBMjQ4IiwidHlwIjoib2lfcmVmdCtqd3QiLCJjdHkiOiJKV1QifQ.EUXrtSTDQ3NstYhpAYaugHs73gW2LZeuqRwGftuxVScLMm6u8VL1MSkXgaOYNclyPeeaaopfja8tIv6-ms9K2Kj-8M9EcOfPia_-daLWJM8fgH9_mOb_5AY_khLRWSnT5vV3sTvokDY6glSd7Egqlvlkrt-lMRRBxKJa9zh8Fswp68IYImtjnlHwMqC_BXkHPKMTVNGsRBAGVBd1p_FhPQjes_p9tR_jcf1s3u3ekQhaYo9-qg-WzfImDXbsLoW9o70zgT_I0k605NhxBxiEf_3kLHYogWsg5pdicZy8yPUwuOhtNu1sY-2QWPzmxq7lOWGteclThjGlh4ptzubeAw.vmNtqW7nQ95FuTHrkQnGSQ.jFiSeMwY8GxZjugGo_69H-S63F2zj2iwdvzQd_5MEZZudvRpF3OHV9n3tVcA_FKSXmo3H7Tl7jOdLE6oMWL5EjCM5ik0MU7hBN4IB-HOCJhEBC7VTmEF68HdqC7xbJX55rPNK2F4fna1cFAc0wr0w5eGaIccITpSoeqOPh2rmapjQw53lb_EY15D2XgRHiXX7qiw6-fBUBFjqI3J3eOcNWQC2Jg1JsN1okhvD5AVSRHjhzb4AwUXGTrV2xanm_p0YfQMohaObMQsOCZLazLCCWIYyA9LFJnyy6kV8pOpoyMh2CRiCB0ahBWf9cKYqpBbPMlls8g6_KUA_6TGgXwxxGMYL7RCu_B4NZtne6wnMCSAztO8Fu9swTREJOhygiAlW-eTTICuX3dyNC7QSjCk4ISdZvGXaK5UeBxMvVD85_QikAO2jqDxFk2O_xQEvHAZ9mXRiPtrcGedvUipo49q30abMCKW572lucahUTXQF_v_Y-OQ7Y0OVZr6KtkOpu71Ot-_8OBpKxMscGx3RqugQITLgShjLIrkCwSxykoCADKtu-ySIadjMViYW0sl4Xfdk4a-IFWeYfCYfCbXFzBxfNafuK9kRWZT2eo5poT-o17wmhI747s75FY6E7c0p9HFDbcxZ_U3KYzMlr6WrexVJqqb4qHO9CNPwxGlhP0WM3F0_JF44Drdcm2nDzyHurE2PmhagTaAAt-0qb2vz0jwdEZDj7vY93pShMoIQW3W_e_2NDAnBLMcZgANs5Oh8pHEbsaU7TgghTwqMgwnyS5uYqcEyqfRPCpoHXGbvrmxeiN1ODlsw0To7qk9NfAOPAV7z_CP7MCEHkpTJgI9cIYt9PEK_fxN2Bq8D0tyiah3Sc2TFVqvH5DbcgdaIG5wpV_S6pc55q-iU7lSgBzJziQiURHEJj1v8j_hP0n4HK9IADw6umEqNsz2KlpKWGMubDoh1ZwibNiweqwzH7C09kHMhlHifgjP7oLe5LGLagZyYD6BV9Z0io5vgl7imZrUiIxhA3vWZrrrZGi9j9KQQrkDuZxNuiUSW66-o13mJ_Ki3Z5kwA8gBssL27oAitL3l_rlhOhjcsACIU9AdA2eVu5xeijf_a4QqQEx8l2sbAzgNLJr4mOaYuUFQlPjFUaw30LXhf9VEa8ks8AVMhjRVtrfEekArrCBvUIrza5fzj-ofICtLoPRbd7K3fxT2NVB57zLra4YYchM5Cqgdqg26ETOpvUDQM8_G55Bw-zYWRQe6JmSpjj8_UBE1EE9L7pWy_6xFxk1Hci1G1dITdthPFlanQnzpX71fHLI69C_I71zrr_On5XIyjZHeGB7Fo2GPV_uurKuB_mmjMKuofpJ-fxuNPbDNUVO2qNg_mfC_aJIP8gCvetsrV-Zpv090hBewMnBrg6nh1rfRatFxKgCv7ZF-SWAb7MqHM4ngoZBoUdVy1gLFfQmmMPWsEB3P4A5tnOEornWAeEsYE92HAtD6AY0W6nSyAIdfHQURq_5DgVxtjqmrBDkYhc0DZmdcSSB4CuCQN7EQKrADAyw48jlldbKMZEDnhcUW855Hru37nf53z5JCp13b8extVI9Fb2ZNkxWhm3vvqOrxPjHGlONO7MesGzxYbmK76XtJzOcDStDydYR9nX7V4OR6zbgjTN517FSxcD9VMOc1r-DYo28K5a9sDOvoyasHIsvKDZlx5j472Vtm1q9JvcG-j1djbkfu462ymD0CjVZss_aGHl8Wn2t3ZFk44s5A1ceRY39jfC4iYzBl3ysTbmJF3uPRc-fZEeAMa239hBhWDOgi8f4udbLcHxbz3QEQBDMgsxTLpJKLfXL4ofJZy6BKdHQtMqokRKvTYTTIPtzoKJNgXycdD88o-C-I8MK8orLTN2reLjSevvg8fKR81rlWWoT1JN3pMzbmsns3Bi4AD1sA-5ZqWtyZiG1_KarZCwhvfe3iH-5PQaMVqOZoluwW9Y0CK9khfoB2gGDEUM7fqAB6ES_3oOA_CbXR6xh39BBQS--Qa1sOU_xaE7HTeL0S_QswHgUb8OJzvybaD19jE8eCFlPb1xkHIN7rDeqjWhtYs_RMmJUHkTzOjhCT6icaPZD8Q7aw0E0PW37K8f25lRNvwzlt-MI8K37aypoxwk0q4A2KKd5EWnPG-tNNQtcxRdIzQtZ7s3_G_TVMeJRiv86JKNXk_MrMFMJ6Ys7N_QcO4dtjFQTWdwag-tnm8j1fsB8AZFtUgLzsSyIKj2WfJQ9cw00UJggx14e5EggbvOYnMdMn7EC56dLyP_Ew1r4rciCyx4RPsk8r0djTXzU5HJ-Af3YM1yvn0-NMcsTky73E2LigXwdEIF4ohQjEzJEbuIGhbPCd8S0C2WZc7XRd6-ki-SHMCSbn87OMI5jsVHGX_8gFALaEqT02PHnMHHo5Z3XHjGck2TfI2kpBQ9q3UPomI0R6U7YR1w0CoPwFzKNosFh6y1d6T-ulpMIMOM3LoyZo3AvWb9c0K6Tnixky1S_5ALsIeg01dOVUSPYwTOI8Jw493cGdQ6SA6NvvZMCw20nAS2jRc_0e3QU6EAtN8srnVw7pzXysE939uVxbzhMyzStmWYH_KtV0tXC7uGam6x88V04ee-Um2oejVz6HbTSQQZJlE76ZUB5MC9cggk_DME1r1otWU2NuxPKtZZkeDpA1Xv6pDYarVLrI5QEXJB8xeh-upWepsQlJ3j9HPTC2NK5RQwp64c7DHqfGZ8WYY2N6-d3XLT7X6rpxxUT20ZeAkKyZ6Se9KljJWa4X8l5LsaO3ZidHBI.kQ_u2mD3-p2GUlKi3S4MxylC-tJhj1MmBf-70PPb35I&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>OpenIddict身份验证与授权之: 一. 实现结构</title>
		<link>https://blog.mutadecheng.com/2024/06/04/openiddict%e8%ba%ab%e4%bb%bd%e9%aa%8c%e8%af%81%e4%b8%8e%e6%8e%88%e6%9d%83%e4%b9%8b-%e4%b8%80-%e5%ae%9e%e7%8e%b0%e7%bb%93%e6%9e%84/</link>
		
		<dc:creator><![CDATA[木它]]></dc:creator>
		<pubDate>Tue, 04 Jun 2024 01:08:03 +0000</pubDate>
				<category><![CDATA[身份验证与授权]]></category>
		<guid isPermaLink="false">https://blog.mutadecheng.com/?p=127</guid>

					<description><![CDATA[理论知识 OpenIddict 是一个基于 ASP.NET Core 的开放源代码的身份验证和授权框架，它实现 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">理论知识</h2>



<p>OpenIddict 是一个基于 ASP.NET Core 的开放源代码的身份验证和授权框架，它实现了 OpenID Connect 和 OAuth 2.0 协议。OpenIddict 的目的是提供一个简单、易于集成的解决方案，以便开发人员能够在自己的应用程序中快速实现安全的身份验证和授权机制。</p>



<p>在 OpenIddict 的实现结构中，通常会涉及以下几个基础项目：</p>



<ol class="wp-block-list">
<li>验证服务器（Authorization Server）：<br>验证服务器是 OpenIddict 的核心组件，它负责处理客户端的认证请求，发放令牌，以及验证用户的身份。验证服务器会实现 OpenID Connect 和 OAuth 2.0 的相关端点，例如授权端点（/connect/authorize）、令牌端点（/connect/token）和用户信息端点（/connect/userinfo）。</li>



<li>客户端（Client）：<br>客户端是指需要访问受保护资源的应用程序，它可以是 Web 应用程序、移动应用程序或其他类型的服务。客户端会向验证服务器发送请求，以获取授权码、访问令牌或刷新令牌。客户端需要注册到验证服务器，并获取客户端 ID 和客户端密钥（若需要的话），这些信息用于验证客户端的身份。</li>



<li>资源服务器（Resource Server）：<br>资源服务器托管受保护的资源，例如 API 接口。资源服务器需要验证来自客户端的访问令牌，以确保请求是由授权的客户端发起，并且用户已经给予了相应的权限。资源服务器通常会使用 OpenIddict 提供的中间件或库来验证令牌的有效性。</li>
</ol>



<p>具体来说，各个组件的作用如下：</p>



<ul class="wp-block-list">
<li>验证服务器：处理身份验证流程，包括用户登录、授权确认、令牌发放等。验证服务器也会管理客户端的注册信息和用户的授权。</li>



<li>客户端：发起对资源的请求，并通过验证服务器提供的授权机制来获取访问资源所需的令牌。客户端必须按照 OAuth 2.0 和 OpenID Connect 协议来实现请求和处理令牌。</li>



<li>资源服务器：提供受保护资源的服务，验证访问请求中的令牌，确保其有效性和权限范围，然后响应客户端的请求。</li>
</ul>



<p>在实际部署时，验证服务器、资源服务器和客户端可以是独立部署的不同应用，也可以是同一个应用中的不同组件。OpenIddict 提供了灵活的配置和扩展点，使得开发人员可以根据自己的需求来定制身份验证和授权的流程。</p>



<h2 class="wp-block-heading"></h2>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>OAuth 2.0 &#038; OpenID Connect</title>
		<link>https://blog.mutadecheng.com/2024/05/24/oauth-2-0-openid-connect/</link>
		
		<dc:creator><![CDATA[木它]]></dc:creator>
		<pubDate>Fri, 24 May 2024 06:36:26 +0000</pubDate>
				<category><![CDATA[身份验证与授权]]></category>
		<guid isPermaLink="false">https://blog.mutadecheng.com/?p=107</guid>

					<description><![CDATA[OAuth 2.0 和 OpenID Connect 是目前广泛使用的授权（Authorization）和身份 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>OAuth 2.0 和 OpenID Connect 是目前广泛使用的授权（Authorization）和身份验证（Authentication）标准。OAuth 2.0 提供了一种安全的授权方式，而 OpenID Connect 在 OAuth 2.0 的基础上增加了身份验证的功能。</p>



<h3 class="wp-block-heading">OAuth 2.0 授权模式</h3>



<p>OAuth 2.0 定义了四种授权模式，分别适用于不同的应用场景：</p>



<ol class="wp-block-list">
<li><strong>授权码模式（Authorization Code Grant）</strong></li>
</ol>



<ul class="wp-block-list">
<li><strong>流程</strong>：
<ol class="wp-block-list">
<li>客户端将用户重定向到授权服务器。</li>



<li>用户同意授权。</li>



<li>授权服务器将用户重定向回客户端，附带一个授权码。</li>



<li>客户端使用授权码向授权服务器请求访问令牌。</li>



<li>授权服务器验证授权码和客户端信息，如果验证通过，则发放访问令牌。</li>
</ol>
</li>



<li><strong>举例</strong>：用户登录第三方应用（如社交网站应用），应用请求访问用户在社交网站上的信息。</li>
</ul>



<ol class="wp-block-list" start="2">
<li><strong>简化模式（Implicit Grant）</strong></li>
</ol>



<ul class="wp-block-list">
<li><strong>流程</strong>：
<ol class="wp-block-list">
<li>客户端将用户重定向到授权服务器。</li>



<li>用户同意授权。</li>



<li>授权服务器将用户重定向回客户端，URL 中直接附带访问令牌。</li>
</ol>
</li>



<li><strong>举例</strong>：适用于纯前端应用，如单页应用（SPA）。</li>
</ul>



<ol class="wp-block-list" start="3">
<li><strong>密码模式（Resource Owner Password Credentials Grant）</strong></li>
</ol>



<ul class="wp-block-list">
<li><strong>流程</strong>：
<ol class="wp-block-list">
<li>用户向客户端提供用户名和密码。</li>



<li>客户端使用用户名和密码向授权服务器请求访问令牌。</li>



<li>授权服务器验证凭据，如果验证通过，则发放访问令牌。</li>
</ol>
</li>



<li><strong>举例</strong>：用户直接在第三方应用输入登录凭据，适用于用户和客户端高度可信的场景。</li>
</ul>



<ol class="wp-block-list" start="4">
<li><strong>客户端凭据模式（Client Credentials Grant）</strong></li>
</ol>



<ul class="wp-block-list">
<li><strong>流程</strong>：
<ol class="wp-block-list">
<li>客户端向授权服务器提供其自己的凭据。</li>



<li>授权服务器验证客户端凭据，如果验证通过，则发放访问令牌。</li>
</ol>
</li>



<li><strong>举例</strong>：适用于客户端访问自己保护的资源，如内部系统间的API调用。</li>
</ul>



<ol class="wp-block-list" start="5">
<li><strong>扩展: 设备码模式（Device Authorization Grant）</strong></li>
</ol>



<ul class="wp-block-list">
<li><strong>流程</strong>：
<ol class="wp-block-list">
<li><strong>设备请求码</strong>：设备向授权服务器发起请求，授权服务器返回一个用户码（user code）、一个设备码（device code），以及验证用户码的URL。</li>



<li><strong>用户授权</strong>：设备展示给用户一个用户码和验证URL，指导用户使用另外的设备（如智能手机或电脑）访问给定的URL并输入用户码进行授权。</li>



<li><strong>设备轮询</strong>：设备使用设备码定期轮询授权服务器，询问用户是否完成了授权操作。</li>



<li><strong>获取访问令牌</strong>：一旦用户完成授权，授权服务器响应设备的轮询请求，发放访问令牌给设备。</li>
</ol>
</li>



<li><strong>举例</strong>：那些没有浏览器或有限输入能力的设备上，如智能电视、游戏机、打印机等，以便这些设备能够安全地获得用户授权。假设你有一台智能电视，想要观看某个需要授权的视频流服务。电视屏幕上显示一个代码和一个URL，告诉你使用手机或电脑访问这个URL并输入代码。你按照指示操作，登录视频流服务的账号，并授权你的电视访问服务。在这个过程中，你的电视定期向服务的授权服务器发送请求，查询授权状态。一旦你完成了授权操作，电视即获得访问令牌，可以加载并播放视频内容。这就是设备码模式的一个典型应用场景。</li>
</ul>



<h3 class="wp-block-heading">OpenID Connect</h3>



<p>OpenID Connect 在 OAuth 2.0 的基础上增加了身份层。它允许客户端通过验证用户的身份信息（ID Token），来进行身份验证。</p>



<ul class="wp-block-list">
<li><strong>ID Token</strong>：是一个 JWT（JSON Web Token），包含了用户的身份信息。</li>
</ul>



<p>OpenID Connect 主要使用的是 OAuth 2.0 的授权码模式，增加了一些参数和步骤来支持身份验证：</p>



<ol class="wp-block-list">
<li>客户端将用户重定向到授权服务器，请求授权码，并指明需要“openid”作用域（scope）。</li>



<li>用户登录并同意授权。</li>



<li>授权服务器将用户重定向回客户端，附带授权码。</li>



<li>客户端使用授权码向授权服务器请求访问令牌，并同时会返回一个 ID Token。</li>



<li>客户端可以解析 ID Token 来获取用户的身份信息。</li>
</ol>



<p>通过这种方式，OpenID Connect 不仅提供了 OAuth 2.0 的授权机制，还增加了身份验证的能力，使得它成为了构建现代应用中身份验证和授权的重要标准。</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
