Angularが前面にあるWebAPIプロジェクトがあり、JWTトークンを使用して保護したいと思います。すでにユーザー/パスの検証が行われているので、JWT部分を実装する必要があると思います。
私はJwtAuthForWebAPIに落ち着いたと思うので、それを使用した例は素晴らしいでしょう。
[Authorize]で装飾されていないメソッドは通常どおりに動作し、[Authorize]で装飾されたメソッドは、クライアントから渡されたトークンが一致しない場合は401になると思います。
最初の認証時にトークンをクライアントに送り返す方法がまだわかりません。
マジックストリングを使って始めようとしているので、次のコードがあります。
RegisterRoutes(GlobalConfiguration.Configuration.Routes);
var builder = new SecurityTokenBuilder();
var jwtHandler = new JwtAuthenticationMessageHandler
{
AllowedAudience = "http://xxxx.com",
Issuer = "corp",
SigningToken = builder.CreateFromKey(Convert.ToBase64String(new byte[]{4,2,2,6}))
};
GlobalConfiguration.Configuration.MessageHandlers.Add(jwtHandler);
しかし、それが最初にどのようにクライアントに戻るのかはわかりません。クライアントでこれを処理する方法は理解していると思いますが、このインタラクションのAngular側も表示できる場合は、ボーナスポイントがあります。
結局、自分に合ったソリューションを作成するために、いくつかの異なる場所から情報を取得する必要がありました(実際には、本番環境で実行可能なソリューションの始まりですが、機能します!)
JwtAuthForWebAPIを削除しました(ただし、Authorizationヘッダーのないリクエストが[Authorize]で保護されていないWebAPI Controllerメソッドに流れるようにするために1つ借りました)。
代わりに、MicrosoftのJWTライブラリを使用しています( Microsoft .NETFrameworkのJSONWebトークンハンドラー -NuGetから)。
私の認証方法では、実際の認証を行った後、トークンの文字列バージョンを作成し、認証された名前(この場合は同じユーザー名が渡されます)と実際には可能性が高い役割とともにトークンを返します認証中に導出されます。
方法は次のとおりです。
[HttpPost]
public LoginResult PostSignIn([FromBody] Credentials credentials)
{
var auth = new LoginResult() { Authenticated = false };
if (TryLogon(credentials.UserName, credentials.Password))
{
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, credentials.UserName),
new Claim(ClaimTypes.Role, "Admin")
}),
AppliesToAddress = ConfigurationManager.AppSettings["JwtAllowedAudience"],
TokenIssuerName = ConfigurationManager.AppSettings["JwtValidIssuer"],
SigningCredentials = new SigningCredentials(new
InMemorySymmetricSecurityKey(JwtTokenValidationHandler.SymmetricKey),
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
"http://www.w3.org/2001/04/xmlenc#sha256")
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
auth.Token = tokenString;
auth.Authenticated = true;
}
return auth;
}
[〜#〜]更新[〜#〜]
後続のリクエストでのトークンの処理について質問がありました。私が行ったのは、トークンの読み取り/デコードを試みるDelegatingHandlerを作成してから、プリンシパルを作成し、それをThread.CurrentPrincipalとHttpContext.Current.Userに設定することでした(両方に設定する必要があります)。最後に、適切なアクセス制限でコントローラーメソッドを装飾します。
DelegatingHandlerの要点は次のとおりです。
private static bool TryRetrieveToken(HttpRequestMessage request, out string token)
{
token = null;
IEnumerable<string> authzHeaders;
if (!request.Headers.TryGetValues("Authorization", out authzHeaders) || authzHeaders.Count() > 1)
{
return false;
}
var bearerToken = authzHeaders.ElementAt(0);
token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
return true;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpStatusCode statusCode;
string token;
var authHeader = request.Headers.Authorization;
if (authHeader == null)
{
// missing authorization header
return base.SendAsync(request, cancellationToken);
}
if (!TryRetrieveToken(request, out token))
{
statusCode = HttpStatusCode.Unauthorized;
return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(statusCode));
}
try
{
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
TokenValidationParameters validationParameters =
new TokenValidationParameters()
{
AllowedAudience = ConfigurationManager.AppSettings["JwtAllowedAudience"],
ValidIssuer = ConfigurationManager.AppSettings["JwtValidIssuer"],
SigningToken = new BinarySecretSecurityToken(SymmetricKey)
};
IPrincipal principal = tokenHandler.ValidateToken(token, validationParameters);
Thread.CurrentPrincipal = principal;
HttpContext.Current.User = principal;
return base.SendAsync(request, cancellationToken);
}
catch (SecurityTokenValidationException e)
{
statusCode = HttpStatusCode.Unauthorized;
}
catch (Exception)
{
statusCode = HttpStatusCode.InternalServerError;
}
return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(statusCode));
}
それをMessageHandlersパイプラインに追加することを忘れないでください:
public static void Start()
{
GlobalConfiguration.Configuration.MessageHandlers.Add(new JwtTokenValidationHandler());
}
最後に、コントローラーメソッドを装飾します。
[Authorize(Roles = "OneRoleHere")]
[GET("/api/admin/settings/product/allorgs")]
[HttpGet]
public List<Org> GetAllOrganizations()
{
return QueryableDependencies.GetMergedOrganizations().ToList();
}
[Authorize(Roles = "ADifferentRoleHere")]
[GET("/api/admin/settings/product/allorgswithapproval")]
[HttpGet]
public List<ApprovableOrg> GetAllOrganizationsWithApproval()
{
return QueryableDependencies.GetMergedOrganizationsWithApproval().ToList();
}
JwtAuthForWebAPIの簡単な実装を行いましたが、機能します:)