ASP.NET Core의 토큰 기반 인증 (새로 고침)
ASP.NET Core 애플리케이션으로 작업하고 있습니다. 토큰 기반 인증을 구현하려고하는데 새로운 보안 시스템 을 사용하는 방법을 알 수 없습니다 .
내 시나리오 : 클라이언트가 토큰을 요청합니다. 내 서버는 사용자에게 권한을 부여하고 다음 요청에서 클라이언트가 사용할 access_token을 반환해야합니다.
필요한 것을 정확히 구현하는 방법에 대한 두 가지 훌륭한 기사가 있습니다.
문제는 ASP.NET Core에서 동일한 작업을 수행하는 방법이 명확하지 않다는 것입니다.
내 질문은 : 토큰 기반 인증을 사용하도록 ASP.NET Core Web Api 애플리케이션을 구성하는 방법 입니다. 어떤 방향을 추구해야합니까? 최신 버전에 대한 기사를 작성했거나 어디에서 찾을 수 있는지 알고 있습니까?
감사합니다!
에서 근무 매트 Dekrey의 멋진 대답 , 내가 ASP.NET 코어 (1.0.1)에 대한 작업, 토큰 기반 인증의 완전히 동작하는 예제를 만들었습니다. GitHub ( 1.0.0-rc1 , beta8 , beta7의 대체 분기) 의이 저장소에서 전체 코드 를 찾을 수 있지만 간단히 말해서 중요한 단계는 다음과 같습니다.
애플리케이션 용 키 생성
이 예에서는 앱이 시작될 때마다 임의의 키를 생성합니다. 키를 생성하여 어딘가에 저장하고 애플리케이션에 제공해야합니다. 무작위 키를 생성하는 방법과 .json 파일에서 가져 오는 방법에 대해서는이 파일을 참조하십시오 . @kspearrin의 의견에서 제안했듯이 데이터 보호 API 는 키를 "올바르게"관리하기위한 이상적인 후보처럼 보이지만 아직 가능한지 확인하지 못했습니다. 문제가 해결되면 풀 리퀘스트를 제출하십시오!
Startup.cs-ConfigureServices
여기에서 토큰에 서명 할 개인 키를로드해야하며, 토큰이 표시 될 때이를 확인하는데도 사용할 것입니다. key
아래의 Configure 메서드에서 재사용 할 클래스 수준 변수에 키를 저장하고 있습니다. TokenAuthOptions 는 키를 생성하기 위해 TokenController에 필요한 서명 ID, 대상 및 발급자를 보유하는 간단한 클래스입니다.
// Replace this with some sort of loading from config / file.
RSAParameters keyParams = RSAKeyUtils.GetRandomKey();
// Create the key, and a set of token options to record signing credentials
// using that key, along with the other parameters we will need in the
// token controlller.
key = new RsaSecurityKey(keyParams);
tokenOptions = new TokenAuthOptions()
{
Audience = TokenAudience,
Issuer = TokenIssuer,
SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.Sha256Digest)
};
// Save the token options into an instance so they're accessible to the
// controller.
services.AddSingleton<TokenAuthOptions>(tokenOptions);
// Enable the use of an [Authorize("Bearer")] attribute on methods and
// classes to protect.
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser().Build());
});
또한 [Authorize("Bearer")]
보호하려는 엔드 포인트와 클래스 에서 사용할 수 있도록 권한 부여 정책을 설정했습니다 .
Startup.cs-구성
여기에서 JwtBearerAuthentication을 구성해야합니다.
app.UseJwtBearerAuthentication(new JwtBearerOptions {
TokenValidationParameters = new TokenValidationParameters {
IssuerSigningKey = key,
ValidAudience = tokenOptions.Audience,
ValidIssuer = tokenOptions.Issuer,
// When receiving a token, check that it is still valid.
ValidateLifetime = true,
// This defines the maximum allowable clock skew - i.e.
// provides a tolerance on the token expiry time
// when validating the lifetime. As we're creating the tokens
// locally and validating them on the same machines which
// should have synchronised time, this can be set to zero.
// Where external tokens are used, some leeway here could be
// useful.
ClockSkew = TimeSpan.FromMinutes(0)
}
});
TokenController
토큰 컨트롤러에는 Startup.cs에로드 된 키를 사용하여 서명 된 키를 생성하는 메서드가 있어야합니다. Startup에 TokenAuthOptions 인스턴스를 등록 했으므로 TokenController의 생성자에이를 주입해야합니다.
[Route("api/[controller]")]
public class TokenController : Controller
{
private readonly TokenAuthOptions tokenOptions;
public TokenController(TokenAuthOptions tokenOptions)
{
this.tokenOptions = tokenOptions;
}
...
그런 다음 로그인 끝점에 대한 처리기에서 토큰을 생성해야합니다. 제 예에서는 사용자 이름과 암호를 가져 와서 if 문을 사용하여 유효성을 검사하지만, 중요한 것은 클레임을 생성하거나로드하는 것입니다. 기반 신원을 확인하고 토큰을 생성합니다.
public class AuthRequest
{
public string username { get; set; }
public string password { get; set; }
}
/// <summary>
/// Request a new token for a given username/password pair.
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
[HttpPost]
public dynamic Post([FromBody] AuthRequest req)
{
// Obviously, at this point you need to validate the username and password against whatever system you wish.
if ((req.username == "TEST" && req.password == "TEST") || (req.username == "TEST2" && req.password == "TEST"))
{
DateTime? expires = DateTime.UtcNow.AddMinutes(2);
var token = GetToken(req.username, expires);
return new { authenticated = true, entityId = 1, token = token, tokenExpires = expires };
}
return new { authenticated = false };
}
private string GetToken(string user, DateTime? expires)
{
var handler = new JwtSecurityTokenHandler();
// Here, you should create or look up an identity for the user which is being authenticated.
// For now, just creating a simple generic identity.
ClaimsIdentity identity = new ClaimsIdentity(new GenericIdentity(user, "TokenAuth"), new[] { new Claim("EntityID", "1", ClaimValueTypes.Integer) });
var securityToken = handler.CreateToken(new Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor() {
Issuer = tokenOptions.Issuer,
Audience = tokenOptions.Audience,
SigningCredentials = tokenOptions.SigningCredentials,
Subject = identity,
Expires = expires
});
return handler.WriteToken(securityToken);
}
그리고 그게 다야. [Authorize("Bearer")]
보호하려는 메서드 나 클래스에 추가 하기 만하면 토큰이없는 상태에서 액세스하려고하면 오류가 발생합니다. 500 오류 대신 401을 반환하려면 여기 예제에서와 같이 사용자 지정 예외 처리기를 등록해야합니다 .
이것은 내 다른 답변과 실제로 중복 되며 더 많은 관심을 끌수록 더 최신 상태로 유지하는 경향이 있습니다. 댓글도 유용 할 수 있습니다!
.Net Core 2 용으로 업데이트되었습니다.
이 답변의 이전 버전은 RSA를 사용했습니다. 토큰을 생성하는 동일한 코드가 토큰을 확인하는 경우에는 실제로 필요하지 않습니다. 그러나 책임을 분배하는 경우 .NET Framework 인스턴스를 사용하여이 작업을 수행하고 싶을 것입니다 Microsoft.IdentityModel.Tokens.RsaSecurityKey
.
나중에 사용할 몇 가지 상수를 만듭니다. 내가 한 일은 다음과 같습니다.
const string TokenAudience = "Myself"; const string TokenIssuer = "MyProject";
이것을 Startup.cs의
ConfigureServices
. 나중에 이러한 설정에 액세스하기 위해 종속성 주입을 사용합니다. 나는 당신authenticationConfiguration
이 디버그 및 생산을 위해 다른 구성을 가질 수 있는ConfigurationSection
또는Configuration
객체 라고 가정하고 있습니다 . 키를 안전하게 보관하십시오! 모든 문자열이 될 수 있습니다.var keySecret = authenticationConfiguration["JwtSigningKey"]; var symmetricKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(keySecret)); services.AddTransient(_ => new JwtSignInHandler(symmetricKey)); services.AddAuthentication(options => { // This causes the default authentication scheme to be JWT. // Without this, the Authorization header is not checked and // you'll get no results. However, this also means that if // you're already using cookies in your app, they won't be // checked by default. options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.TokenValidationParameters.ValidateIssuerSigningKey = true; options.TokenValidationParameters.IssuerSigningKey = symmetricKey; options.TokenValidationParameters.ValidAudience = JwtSignInHandler.TokenAudience; options.TokenValidationParameters.ValidIssuer = JwtSignInHandler.TokenIssuer; });
다른 답변이 다음과 같은 다른 설정을 변경하는 것을 보았습니다
ClockSkew
. 기본값은 시계가 정확히 동기화되지 않은 분산 환경에서 작동하도록 설정됩니다. 변경해야하는 유일한 설정입니다.인증을 설정합니다.
User
정보 를 필요로하는 미들웨어 ( 예 :app.UseMvc()
.app.UseAuthentication();
이로 인해 토큰이
SignInManager
또는 다른 것과 함께 방출되지는 않습니다 . JWT를 출력하기위한 자체 메커니즘을 제공해야합니다. 아래를 참조하세요.을 지정할 수 있습니다
AuthorizationPolicy
. 이렇게하면 .NET을 사용하는 인증으로 Bearer 토큰 만 허용하는 컨트롤러 및 작업을 지정할 수 있습니다[Authorize("Bearer")]
.services.AddAuthorization(auth => { auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder() .AddAuthenticationTypes(JwtBearerDefaults.AuthenticationType) .RequireAuthenticatedUser().Build()); });
여기에 까다로운 부분이 있습니다 : 토큰 구축.
class JwtSignInHandler { public const string TokenAudience = "Myself"; public const string TokenIssuer = "MyProject"; private readonly SymmetricSecurityKey key; public JwtSignInHandler(SymmetricSecurityKey symmetricKey) { this.key = symmetricKey; } public string BuildJwt(ClaimsPrincipal principal) { var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: TokenIssuer, audience: TokenAudience, claims: principal.Claims, expires: DateTime.Now.AddMinutes(20), signingCredentials: creds ); return new JwtSecurityTokenHandler().WriteToken(token); } }
그런 다음 토큰을 원하는 컨트롤러에서 다음과 같이합니다.
[HttpPost] public string AnonymousSignIn([FromServices] JwtSignInHandler tokenFactory) { var principal = new System.Security.Claims.ClaimsPrincipal(new[] { new System.Security.Claims.ClaimsIdentity(new[] { new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.Name, "Demo User") }) }); return tokenFactory.BuildJwt(principal); }
여기서는 이미 교장이 있다고 가정합니다. 당신이 ID를 사용하는 경우 사용할 수 있습니다
IUserClaimsPrincipalFactory<>
당신을 변환하는User
으로ClaimsPrincipal
.테스트하려면 : 토큰을 가져 와서 jwt.io 의 양식에 넣으십시오 . 위에서 제공 한 지침을 통해 구성의 비밀을 사용하여 서명을 확인할 수도 있습니다!
.Net 4.5의 베어러 전용 인증과 함께 HTML 페이지의 부분보기에서이를 렌더링하는 경우 이제 a
ViewComponent
를 사용 하여 동일한 작업을 수행 할 수 있습니다 . 위의 Controller Action 코드와 거의 동일합니다.
설명하는 내용을 달성하려면 OAuth2 / OpenID Connect 인증 서버와 API에 대한 액세스 토큰을 확인하는 미들웨어가 모두 필요합니다. Katana는를 제공하는 데 사용 OAuthAuthorizationServerMiddleware
되었지만 ASP.NET Core에는 더 이상 존재하지 않습니다.
앞서 언급 한 자습서에서 사용하는 OAuth2 권한 부여 서버 미들웨어의 실험적 포크 인 AspNet.Security.OpenIdConnect.Server를 살펴볼 것을 제안 합니다. OWIN / Katana 3 버전과 둘 다 지원하는 ASP.NET Core 버전이 있습니다. net451
(.NET Desktop) 및 netstandard1.4
(.NET Core와 호환).
https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server
AspNet.Security.OpenIdConnect.Server를 사용하여 OpenID Connect 권한 부여 서버를 구성하는 방법과 서버 미들웨어에서 발급 한 암호화 된 액세스 토큰의 유효성을 검사하는 방법을 보여주는 MVC Core 샘플을 놓치지 마십시오 : https://github.com/aspnet- contrib / AspNet.Security.OpenIdConnect.Server / blob / dev / samples / Mvc / Mvc.Server / Startup.cs
기본 인증에 해당하는 OAuth2 인 리소스 소유자 비밀번호 부여를 구현하는 방법을 설명하는이 블로그 게시물을 읽을 수도 있습니다. http://kevinchalet.com/2016/07/13/creating-your-own-openid- asos-implementing-the-resource-owner-password-credentials-grant /로 서버 연결
Startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication();
}
public void Configure(IApplicationBuilder app)
{
// Add a new middleware validating the encrypted
// access tokens issued by the OIDC server.
app.UseOAuthValidation();
// Add a new middleware issuing tokens.
app.UseOpenIdConnectServer(options =>
{
options.TokenEndpointPath = "/connect/token";
// Override OnValidateTokenRequest to skip client authentication.
options.Provider.OnValidateTokenRequest = context =>
{
// Reject the token requests that don't use
// grant_type=password or grant_type=refresh_token.
if (!context.Request.IsPasswordGrantType() &&
!context.Request.IsRefreshTokenGrantType())
{
context.Reject(
error: OpenIdConnectConstants.Errors.UnsupportedGrantType,
description: "Only grant_type=password and refresh_token " +
"requests are accepted by this
return Task.FromResult(0);
}
// Since there's only one application and since it's a public client
// (i.e a client that cannot keep its credentials private),
// call Skip() to inform the server the request should be
// accepted without enforcing client authentication.
context.Skip();
return Task.FromResult(0);
};
// Override OnHandleTokenRequest to support
// grant_type=password token requests.
options.Provider.OnHandleTokenRequest = context =>
{
// Only handle grant_type=password token requests and let the
// OpenID Connect server middleware handle the other grant types.
if (context.Request.IsPasswordGrantType())
{
// Do your credentials validation here.
// Note: you can call Reject() with a message
// to indicate that authentication failed.
var identity = new ClaimsIdentity(context.Options.AuthenticationScheme);
identity.AddClaim(OpenIdConnectConstants.Claims.Subject, "[unique id]");
// By default, claims are not serialized
// in the access and identity tokens.
// Use the overload taking a "destinations"
// parameter to make sure your claims
// are correctly inserted in the appropriate tokens.
identity.AddClaim("urn:customclaim", "value",
OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(identity),
new AuthenticationProperties(),
context.Options.AuthenticationScheme);
// Call SetScopes with the list of scopes you want to grant
// (specify offline_access to issue a refresh token).
ticket.SetScopes("profile", "offline_access");
context.Validate(ticket);
}
return Task.FromResult(0);
};
});
}
}
project.json
{
"dependencies": {
"AspNet.Security.OAuth.Validation": "1.0.0",
"AspNet.Security.OpenIdConnect.Server": "1.0.0"
}
}
행운을 빕니다!
당신은 사용할 수 있습니다 OpenIddict을 (벌목) 토큰을 제공하기 위해 다음 사용 UseJwtBearerAuthentication
하는 API가 / 컨트롤러에 액세스 할 때 그 유효성을 검사 할 수 있습니다.
이것은 기본적으로 필요한 모든 구성입니다 Startup.cs
.
ConfigureServices :
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
// this line is added for OpenIddict to plug in
.AddOpenIddictCore<Application>(config => config.UseEntityFramework());
구성
app.UseOpenIddictCore(builder =>
{
// here you tell openiddict you're wanting to use jwt tokens
builder.Options.UseJwtTokens();
// NOTE: for dev consumption only! for live, this is not encouraged!
builder.Options.AllowInsecureHttp = true;
builder.Options.ApplicationCanDisplayErrors = true;
});
// use jwt bearer authentication to validate the tokens
app.UseJwtBearerAuthentication(options =>
{
options.AutomaticAuthenticate = true;
options.AutomaticChallenge = true;
options.RequireHttpsMetadata = false;
// must match the resource on your token request
options.Audience = "http://localhost:58292/";
options.Authority = "http://localhost:58292/";
});
DbContext가 .NET에서 파생해야하는 것과 같은 다른 사소한 사항이 하나 또는 두 개 있습니다 OpenIddictContext<ApplicationUser, Application, ApplicationRole, string>
.
You can see a full length explanation (including the functioning github repo) on this blog post of mine: http://capesean.co.za/blog/asp-net-5-jwt-tokens/
You can have a look at the OpenId connect samples which illustrate how to deal with different authentication mechanisms, including JWT Tokens:
https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Samples
If you look at the Cordova Backend project, the configuration for the API is like so:
app.UseWhen(context => context.Request.Path.StartsWithSegments(new PathString("/api")),
branch => {
branch.UseJwtBearerAuthentication(options => {
options.AutomaticAuthenticate = true;
options.AutomaticChallenge = true;
options.RequireHttpsMetadata = false;
options.Audience = "localhost:54540";
options.Authority = "localhost:54540";
});
});
The logic in /Providers/AuthorizationProvider.cs and the RessourceController of that project are also worth having a look at ;).
Moreover, I have implemented a single page application with token based authentication implementation using the Aurelia front end framework and ASP.NET core. There is also a signal R persistent connection. However I have not done any DB implementation. Code can be seen here: https://github.com/alexandre-spieser/AureliaAspNetCoreAuth
Hope this helps,
Best,
Alex
참고URL : https://stackoverflow.com/questions/30546542/token-based-authentication-in-asp-net-core-refreshed
'developer tip' 카테고리의 다른 글
ggplot에서 패싯 순서 수정 (0) | 2020.11.15 |
---|---|
Android Studio : 무기한 실행되는 백그라운드 작업 (0) | 2020.11.15 |
Git 정리 란 무엇입니까? (0) | 2020.11.15 |
생성기를 사용한 비동기 / 대기 및 ES6 수율의 차이점 (0) | 2020.11.15 |
Typescript : 중첩 된 개체에 대한 인터페이스를 어떻게 정의합니까? (0) | 2020.11.15 |