ASP.NET Core 3.0 或更高版本通过支持 API 授权在单页面应用(Spa)中提供身份验证。 用于对用户进行身份验证和存储的 ASP.NET Core 标识与用于实现 Open ID Connect 的IdentityServer结合。
身份验证参数已添加到与Web 应用程序(模型-视图-控制器) (MVC)和web 应用程序(Razor Pages)项目模板中的身份验证参数类似的 "角度" 和 "响应" 项目模板。 允许的参数值为None和个体。 目前,反应和 Redux项目模板不支持 authentication 参数。
用户身份验证和授权可用于角度和响应 Spa。 打开命令外壳,并运行以下命令:
角:
dotnet new angular -o <output_directory_name> -au Individual
响应:
dotnet new react -o <output_directory_name> -au Individual
上述命令创建一个 ASP.NET Core 应用,其中包含包含 SPA 的ClientApp目录。
以下各节介绍了在包括身份验证支持的情况下对项目添加的内容:
Startup
类具有以下附加项:
在 Startup.ConfigureServices
方法中:
具有默认 UI 的标识:
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlite(Configuration.GetConnectionString("DefaultConnection"))); services.AddDefaultIdentity<ApplicationUser>() .AddDefaultUI(UIFramework.Bootstrap4) .AddEntityFrameworkStores<ApplicationDbContext>();
使用附加的 AddApiAuthorization
帮助器方法,可在 IdentityServer 上设置某些默认的 ASP.NET Core 约定:
services.AddIdentityServer() .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
使用附加的 AddIdentityServerJwt
帮助器方法进行身份验证,该方法将应用程序配置为验证 IdentityServer 生成的 JWT 令牌:
services.AddAuthentication() .AddIdentityServerJwt();
在 Startup.Configure
方法中:
负责验证请求凭据并在请求上下文上设置用户的身份验证中间件:
app.UseAuthentication();
公开开放 ID 连接终结点的 IdentityServer 中间件:
app.UseIdentityServer();
此帮助器方法将 IdentityServer 配置为使用受支持的配置。 IdentityServer 是一个功能强大且可扩展的框架,用于处理应用安全问题。 同时,对于最常见的方案,这会造成不必要的复杂性。 因此,系统会为您提供一组约定和配置选项,这是一个很好的起点。 身份验证需要更改后,IdentityServer 的全部功能仍可用于自定义身份验证以满足你的需求。
此帮助器方法将应用程序的策略方案配置为默认的身份验证处理程序。 此策略配置为允许标识处理所有路由到标识 URL 空间 "/Identity" 中的子路径的请求。 JwtBearerHandler
处理所有其他请求。 此外,此方法向 IdentityServer 的默认作用 <<ApplicationName>>API
域注册 <<ApplicationName>>API
API 资源,并配置 JWT 持有者令牌中间件来验证 IdentityServer 为应用颁发的令牌。
在Controllers\WeatherForecastController.cs文件中,请注意应用于类的 [Authorize]
属性,该属性指示用户需要根据默认策略进行授权才能访问资源。 默认授权策略将配置为使用默认身份验证方案,该方案通过 AddIdentityServerJwt
设置为上面提到的策略方案,使此类 helper 方法配置的 JwtBearerHandler
成为应用请求的默认处理程序。
在Data\ApplicationDbContext.cs文件中,请注意在标识中使用了相同的 DbContext
,但它会将 ApiAuthorizationDbContext
(从 IdentityDbContext
派生的派生类)扩展为包含 IdentityServer 的架构。
若要完全控制数据库架构,请从某个可用的标识 DbContext
类继承,并通过在 OnModelCreating
方法上调用 builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value)
,将上下文配置为包含标识架构。
在Controllers\OidcConfigurationController.cs文件中,请注意为提供客户端需要使用的 OIDC 参数而设置的终结点。
在项目根的appsettings文件中,有一个新的 IdentityServer
节,其中描述了已配置的客户端的列表。 在下面的示例中,有一个客户端。 客户端名称对应于应用名称,并按约定映射到 OAuth ClientId
参数。 配置文件指示正在配置的应用类型。 它在内部用于驱动约定,以简化服务器的配置过程。 有几个配置文件可用,如 "应用程序配置文件" 部分中所述。
"IdentityServer": { "Clients": { "angularindividualpreview3final": { "Profile": "IdentityServerSPA" } } }
在appsettings 中。 项目根的开发 json 文件,有一个 IdentityServer
部分描述用于对令牌进行签名的密钥。 部署到生产环境时,需要在应用中预配和部署密钥,如 "部署到生产" 一节中所述。
"IdentityServer": { "Key": { "Type": "Development" } }
角度模板中的身份验证和 API 授权支持位于其自身的ClientApp\src\api-authorization目录中。 模块由以下元素组成:
AuthorizeGuard
可以添加到路由,并在访问路由之前要求用户进行身份验证。AuthorizeInterceptor
,它在用户进行身份验证时,将访问令牌附加到针对 API 的传出 HTTP 请求。AuthorizeService
,用于处理身份验证过程的较低级别的详细信息,并向应用程序的其余部分提供有关使用情况的经过身份验证的用户的信息。响应模板中的身份验证和 API 授权支持位于ClientApp\src\components\api-authorization目录中。 它由以下元素组成:
Component
参数中指示的组件。AuthorizeService
的已导出 authService
类的实例,用于处理身份验证过程的较低级别细节,并向应用程序的其余部分公开有关使用情况的已通过身份验证的用户的信息。现在,你已了解解决方案的主要组件,可以更深入地了解应用程序的各个方案。
默认情况下,系统配置为轻松地要求对新 Api 进行授权。 为此,请创建新的控制器,并将 [Authorize]
特性添加到控制器类或控制器中的任何操作。
若要自定义 API 的 JWT 处理程序的配置,请配置其 JwtBearerOptions 实例:
services.AddAuthentication() .AddIdentityServerJwt(); services.Configure<JwtBearerOptions>( IdentityServerJwtConstants.IdentityServerJwtBearerScheme, options => { ... });
API 的 JWT 处理程序会引发事件,这些事件可以使用 JwtBearerEvents
来控制身份验证过程。 若要为 API 授权提供支持,AddIdentityServerJwt
会注册其自己的事件处理程序。
若要自定义事件的处理,请根据需要使用其他逻辑来包装现有的事件处理程序。 例如:
services.Configure<JwtBearerOptions>( IdentityServerJwtConstants.IdentityServerJwtBearerScheme, options => { var onTokenValidated = options.Events.OnTokenValidated; options.Events.OnTokenValidated = async context => { await onTokenValidated(context); ... } });
在前面的代码中,OnTokenValidated
事件处理程序将替换为自定义实现。 此实现:
通过将授权防护添加到在配置路由时要运行的防护列表,来保护客户端路由。 例如,可以在主应用角模块中查看如何配置 fetch-data
路由:
RouterModule.forRoot([ // ... { path: 'fetch-data', component: FetchDataComponent, canActivate: [AuthorizeGuard] }, ])
需要说明的是,保护路由不会保护实际终结点(仍需要应用 [Authorize]
属性),但它只会阻止用户在未通过身份验证时导航到给定的客户端路由。
对与应用程序一起托管的 Api 的请求进行身份验证是通过使用应用定义的 HTTP 客户端侦听器来自动完成的。
使用 AuthorizeRoute
组件而不是纯 Route
组件来保护客户端路由。 例如,请注意在 App
组件中如何配置 fetch-data
路由:
<AuthorizeRoute path='/fetch-data' component={FetchData} />
保护路由:
[Authorize]
特性)。通过首先从 AuthorizeService
导入 authService
实例,对具有反应的请求进行身份验证。 将从 authService
检索访问令牌,并将其附加到请求,如下所示。 在响应组件中,这种工作通常是在 componentDidMount
生命周期方法中完成的,或者作为某些用户交互的结果。
import authService from './api-authorization/AuthorizeService'
async populateWeatherData() { const token = await authService.getAccessToken(); const response = await fetch('api/SampleData/WeatherForecasts', { headers: !token ? {} : { 'Authorization': `Bearer ${token}` } }); const data = await response.json(); this.setState({ forecasts: data, loading: false }); }
若要将应用部署到生产环境,需预配以下资源:
本部分介绍如何使用证书存储中存储的证书将应用部署到 Azure 网站。 若要修改应用以从证书存储区中加载证书,请在稍后的步骤中配置应用服务计划。 在应用的appsettings文件中,修改 IdentityServer
部分以包含关键详细信息:
"IdentityServer": { "Key": { "Type": "Store", "StoreName": "My", "StoreLocation": "CurrentUser", "Name": "CN=MyApplication" } }
CurrentUser
或 LocalMachine
)。若要部署到 Azure 网站,请遵循将应用程序部署到 azure中的步骤部署应用,以创建所需的 Azure 资源,并将该应用部署到生产环境。
按照前面的说明操作后,应用程序将部署到 Azure,但尚不起作用。 仍需设置应用使用的证书。 找到要使用的证书的指纹,并按照加载证书中所述的步骤进行操作。
虽然这些步骤提及 SSL,但在门户上有一个 "私有证书" 部分,你可以在其中上传要与应用一起使用的预配证书。
完成此步骤后,重新启动应用程序并确保它正常运行。
支持 API 授权构建在 IdentityServer 的基础之上,具有一组约定、默认值和增强功能,可简化 Spa 的体验。 不用说,如果 ASP.NET Core 集成不涵盖方案,则 IdentityServer 的全部功能都可在幕后使用。 ASP.NET Core 支持重点介绍 "第一方" 应用,其中的所有应用都是由我们的组织创建和部署的。 因此,不支持许可或联合等。 对于这些方案,请使用 IdentityServer 并按照其文档进行操作。
应用程序配置文件是用于进一步定义其参数的应用的预定义配置。 目前支持以下配置文件:
IdentityServerSPA
:表示与 IdentityServer 一起托管的 SPA 作为单个单元。
redirect_uri
默认为 /authentication/login-callback
。post_logout_redirect_uri
默认为 /authentication/logout-callback
。openid
、profile
和每个作用域。id_token token
或每个响应类型(id_token``token
)。fragment
。SPA
:表示不与 IdentityServer 托管的 SPA。
openid
、profile
和每个作用域。id_token token
或每个响应类型(id_token``token
)。fragment
。IdentityServerJwt
:表示与 IdentityServer 一起托管的 API。
API
:表示不与 IdentityServer 托管的 API。
通过配置系统将应用添加到 Clients
或 Resources
的列表来配置这些应用。
配置每个客户端的 redirect_uri
和 post_logout_redirect_uri
属性,如以下示例中所示:
"IdentityServer": { "Clients": { "MySPA": { "Profile": "SPA", "RedirectUri": "https://www.example.com/authentication/login-callback", "LogoutUri": "https://www.example.com/authentication/logout-callback" } } }
配置资源时,可以按如下所示配置资源的作用域:
"IdentityServer": { "Resources": { "MyExternalApi": { "Profile": "API", "Scopes": "a b c" } } }
你还可以通过代码使用采用操作来配置选项的 AddApiAuthorization
的重载来配置客户端和资源。
AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options => { options.Clients.AddSPA( "My SPA", spa => spa.WithRedirectUri("http://www.example.com/authentication/login-callback") .WithLogoutRedirectUri( "http://www.example.com/authentication/logout-callback")); options.ApiResources.AddApiResource("MyExternalApi", resource => resource.WithScopes("a", "b", "c")); });