跳转到主要内容

大家好!

在本文中,我将讨论 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,它会检查用户是否遵守我们创建的策略。

就是这样! 现在看起来并不难,但我花了一些时间看一些东西。

如果您对这种技术组合有其他方法,请告诉我们,留下您的笔记!

我希望我能帮助你!

再见!

文章链接