大家好!
在本文中,我将讨论 ADFS + Angular + ASP.NET Core API。
我需要一起做这三件事,我很难找到关于这样做的内容。当它有关于 Angular 的信息时,它没有关于 ASP.NET Core API 的信息,或者它是关于 ASP.NET MVC 项目的信息。哦,很难过!
但是现在,我将这三件事结合在一起,我想解释一些我学到的东西,并提供一个可以帮助其他人的项目。
那么,让我们开始吧!
第 1 部分:ADFS
我不会专注于在 ADFS 中配置您的应用程序,我认为您可以在 Internet 上找到足够多的相关内容。
但是,你需要知道一些事情:
由于前端将与您的后端解耦,因此您需要使用 JWT + OAUTH/OIDC 的方法,您将在其中获得访问令牌。因此,基本上“身份验证发生”在前端,后端检查请求是否有效,或者换句话说,请求的标头中是否有有效的访问令牌。
这称为隐式流(Implicit Flow),根据我当时的研究,从 ADFS 4 开始就允许这样做。
除此之外,最好记住,因为它是一个 SPA 应用程序,它有一种特定的方式可以在 ADFS 上配置它,比如“本机应用程序”或类似的东西。好吧,我想你可以在谷歌上找到它,然后你就会有一个客户 ID 和其他信息。
第 02 部分:Angular
我不会专注于创建 Angular 应用程序。我想你知道怎么做。如果没有,请检查以下链接:
https://angular.io/
https://cli.angular.io/
我有一个项目,您可以在其中建立自己的基础:https://github.com/gabrielfbarros/aspnetcore-angular-adfs/tree/master/front。
你可能会想,我并没有把重点放在应用程序的一个伟大的结构上!
为了使与 ADFS 的集成更容易,我寻找了一个可以帮助我的库。我发现的很少,但似乎更常用和更适合我的是“angular-oauth2-oidc”。你可以在这里查看:https://www.npmjs.com/package/angular-oauth2-oidc
在上面的示例中,基本上我通过 CLI 创建了一个 Angular 5 应用程序,并按照 lib 教程中的说明进行操作。我在下面详细说明:
- 您需要在 package.json 文件中添加 lib。看,如果你需要对 Angular < 6(4.3 到 5.x)的支持,你可以下载之前的 3.1.4 版本(npm i angular-oauth2-oidc@^3 --save)。如果你正在使用 Angular 6+,你可以使用最新版本。查看 npm 网站上的教程。
- 在 app.component 中,创建一个构造函数,并在其中从 lib 配置 OAuthService:
public constructor(private oauthService: OAuthService) { this.oauthService.redirectUri = window.location.origin; this.oauthService.clientId = 'YOUR_CLIENT_ID'; this.oauthService.loginUrl = 'https://YOUR_SERVER/adfs/oauth2/authorize'; this.oauthService.issuer = 'https://YOUR_SERVER/adfs'; this.oauthService.scope = "openid profile"; this.oauthService.responseType = 'id_token token'; this.oauthService.setStorage(sessionStorage); this.oauthService.tryLogin(); }
您注意到我刚刚将 redirectUri 设置为我的应用程序的基本 url。此配置应使用您在 ADFS 中配置的重定向 url 进行设置,因为在验证用户身份后,ADFS 将重定向到该配置的 url。
clientId 是 ADFS 为您的应用程序生成的。它类似于“指导”值。
loginUrl 是 ADFS 的授权端点的 url。基本上,您需要通过 ADFS 服务器的路径更改“YOUR_SERVER”,在上面的示例中。
issuer 基本上是保存 ADFS 的服务器的 url,仅以“/adfs”结尾,但在我的情况下,此路径与之前的路径 loginUrl 不同。我不知道究竟是为什么。经过大量测试后,我发现您有一个端点可以为您提供其他一些信息:https://YOUR_SERVER/adfs/.well-known/openid-configuration,其中的信息之一是发行者,即我刚刚在我的 Angular 配置中使用过。
好吧,这基本上就是我需要的所有配置,以使 lib “看到”我的 ADFS。
- 在 app.module 中,您需要在导入声明中导入“OAuthModule.forRoot()”。
- 在我的示例中,我创建了一个 home.component,它有一个向 API 发送 GET 请求的按钮,并且 API 由安全策略保护。因此,我创建了 my.service,其中创建了以下函数:
getPrivateMessage(): Observable<string> { let options = this.getAuthHeader(); return this.http.get(this.UrlService + "home/private", options) .map((res: Response) => <string>super.extractData(res)) .catch(err => super.serviceError(err, true)); }
查看“this.getAuthHeader()”调用。 您可以检查 my.service 是否扩展了我创建该函数的 base.service:
protected getAuthHeader(): RequestOptions { this.Token = this.oauthService.getAccessToken(); let headers = new Headers({ 'Content-Type': 'application/json' }); headers.append('Authorization', `Bearer ${this.Token}`); let options = new RequestOptions({ headers: headers }); return options; }
好吧,这就是魔法发生的地方! 在这里,我使用“this.oauthService.getAccessToken()”来获取访问令牌,并将其作为 Authorization Bearer 属性放在标头中。
- 除此之外,我创建了一个 app.routes 和一个 auth-guard.service。 在警卫服务中,我检查会话存储中是否存在有效的访问令牌。 如果没有或者如果用户登录失败,它将启动隐式流程,这将打开 ADFS 的登录页面:
canActivate(routeAc: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { if (!this.oauthService.hasValidAccessToken()) { this.oauthService.tryLogin(); } if (!this.oauthService.hasValidAccessToken()) { this.oauthService.initImplicitFlow(); return false; } return true; }
所以在我的路由中使用这个守卫,我保证用户已经过身份验证:
{ path: ‘home’, component: HomeComponent, canActivate: [AuthGuardService] }
- 最后但同样重要的是,在我的 home.component 中有一个注销功能:
logout(){ this.oauthService.logOut(); window.location.href= 'https://YOUR_SERVER/adfs/ls/?wa=wsignoutcleanup1.0'; }
好吧,你可以看到它很简单,但只有在你知道这一切之后!
在主页上,登录后,您将看到 ADFS 生成的访问令牌,该令牌将发送到 API。您可以在 https://jwt.io/ 中查看,查看 ADFS 向您返回了哪些信息。就我而言,我们能够从 ADFS 获取应用程序的几个重要信息。
第 3 部分:ASP.NET Core API
对于后端,我还有一个项目,您可以在其中建立自己的基础:https://github.com/gabrielfbarros/aspnetcore-angular-adfs/tree/master/back。
你也可以想象我并没有把重点放在应用程序的一个很好的结构上!
在上面的示例中,基本上我从 Visual Studio 的模板创建了一个 ASP.NET Core 2.0 API。
主要配置发生在 Startup.cs 类中。正如我之前提到的,API 将接收请求,并且对于每个请求,它需要检查是否提供了有效的访问令牌,否则 API 应该返回代码 401(未授权)。
为了确保任何请求都会检查访问令牌,您需要向 MVC 管道添加一个过滤器。在 ConfigureServices 方法中:
services.AddMvc(options => { var policy = new AuthorizationPolicyBuilder() .AddAuthenticationSchemes("Bearer") .RequireAuthenticatedUser() .Build(); options.Filters.Add(new AuthorizeFilter(policy)); });
看我为我的身份验证方案定义了一个名称:“Bearer”。 现在我们需要将身份验证配置为 JWT。 仍然在 ConfigureServices 方法中:
services.AddAuthentication("Bearer") .AddJwtBearer(options => { options.Authority = "https://YOUR_SERVER/adfs"; options.Audience = "microsoft:identityserver:YOUR_CLIENT_ID"; options.TokenValidationParameters = new TokenValidationParameters() { ValidIssuer = "http://YOUR_SERVER/adfs/services/trust" }; options.Events = new JwtBearerEvents { OnTokenValidated = async ctx => { var claims = new List<Claim> { new Claim("GivenType", "GivenValue") }; ctx.Principal.AddIdentity(new ClaimsIdentity(claims)); } }; });
您会看到,权威是 Angular 应用程序的发行者。
Audience 是我的致命弱点! 我花了一些时间来找出我应该在这里使用哪些信息。 您会注意到该值具有“microsoft:identityserver:”和客户端 ID。 好吧,我从访问令牌中获得了这些信息,在 https://jwt.io/ 中查看。 它是令牌中的“aud”值。
ValidIssuer 是 ADFS 的服务/信任端点。
在 Events 属性中,您可以配置 OnTokenValidated 并向身份添加声明,或者您可以在验证令牌后执行任何其他必要的操作。
并且不是强制性的,但非常有用的是配置策略,这是 ASP.NET Core 的一个新特性:
services.AddAuthorization(options => { options.AddPolicy("Given Policy", policy => policy.RequireClaim("GivenType", "GivenValue")); });
在这里,我们创建一个策略并说明满足该策略所必需的声明。 看看声明与我在上面的 OnTokenValidated 中添加给用户的声明相同,因此用户将有权访问该策略。
并且为了使策略有意义,我们可以在 Controller 中使用它:
[Authorize(Policy = “Given Policy”)]
public class HomeController : Controller
因此,通过所有这些配置,任何请求,对于任何控制器,它都会检查您的 ADFS,如果标头中有有效的访问令牌。 特别是对于这个 HomeController,它会检查用户是否遵守我们创建的策略。
就是这样! 现在看起来并不难,但我花了一些时间看一些东西。
如果您对这种技术组合有其他方法,请告诉我们,留下您的笔记!
我希望我能帮助你!
再见!
- 登录 发表评论