ASP.NET Core의 토큰 기반 인증
ASP.NET Core 응용 프로그램으로 작업하고 있습니다. 토큰 기반 인증을 구현하려고하는데 새로운 보안 시스템 을 사용하는 방법을 알 수 없습니다 . 나는 예제를 살펴 보았지만 도움이되지 않았으며 쿠키 인증이나 외부 인증 (GitHub, Microsoft, Twitter)을 사용하고 있습니다.
내 시나리오는 : angularjs 응용 프로그램은 /token
사용자 이름과 암호를 전달하는 URL을 요청해야 합니다. WebApi는 access_token
다음 요청에서 angularjs 앱이 사용할 사용자를 승인하고 반환해야 합니다.
ASP.NET Web API 2, Owin 및 Identity를 사용하여 현재 버전의 ASP.NET 토큰 기반 인증에 필요한 것을 정확하게 구현하는 방법에 대한 훌륭한 기사를 찾았습니다 . 그러나 ASP.NET Core에서 동일한 작업을 수행하는 방법은 분명하지 않습니다.
내 질문은 : 토큰 기반 인증과 함께 작동하도록 ASP.NET Core WebApi 응용 프로그램을 구성하는 방법은 무엇입니까?
.Net Core 2 용으로 업데이트되었습니다.
이 답변의 이전 버전은 RSA를 사용했습니다. 토큰을 생성하는 동일한 코드가 토큰을 확인하는 경우 실제로 필요하지 않습니다. 그러나 책임을 분배하는 경우 여전히의 인스턴스를 사용하여이 작업을 수행하려고합니다 Microsoft.IdentityModel.Tokens.RsaSecurityKey
.
나중에 사용할 상수를 몇 개 만듭니다. 여기 내가 한 일이 있습니다.
const string TokenAudience = "Myself"; const string TokenIssuer = "MyProject";
이것을 Startup.cs 's에 추가하십시오
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
. 이를 통해 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
를 사용 하여 동일한 작업을 수행 할 수 있습니다 . 위의 컨트롤러 액션 코드와 대부분 동일합니다.
에서 근무 매트 Dekrey의 멋진 대답 , 내가 ASP.NET 코어 (1.0.1)에 대한 작업, 토큰 기반 인증의 완전히 동작하는 예제를 만들었습니다. 이 저장소에서 GitHub 의 전체 코드 ( 1.0.0-rc1 , beta8 , beta7의 대체 브랜치)를 찾을 수 있지만 중요한 단계는 다음과 같습니다.
응용 프로그램의 키 생성
내 예제에서는 앱이 시작될 때마다 임의의 키를 생성하고 키를 생성하여 어딘가에 저장하여 애플리케이션에 제공해야합니다. 무작위 키를 생성하는 방법과 .json 파일에서 키를 가져 오는 방법에 대해서는이 파일을 참조하십시오 . @kspearrin의 의견에서 제안한 것처럼 Data Protection API 는 키를 "정확하게"관리하기위한 이상적인 후보처럼 보이지만 아직 가능한 경우 해결하지 못했습니다. 풀 요청을 제출하면 제출하십시오!
Startup.cs-서비스 구성
여기서 토큰에 서명 할 개인 키를로드해야하며, 토큰이 제시되면이를 확인하는 데에도 사용됩니다. 우리는 키를 클래스 수준 변수에 저장하고 있습니다.이 변수 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)
}
});
토큰 컨트롤러
토큰 컨트롤러에는 Startup.cs에로드 된 키를 사용하여 서명 된 키를 생성하는 방법이 필요합니다. Startup에 TokenAuthOptions 인스턴스를 등록 했으므로 TokenController의 생성자에 TokenAuthOptions 인스턴스를 삽입해야합니다.
[Route("api/[controller]")]
public class TokenController : Controller
{
private readonly TokenAuthOptions tokenOptions;
public TokenController(TokenAuthOptions tokenOptions)
{
this.tokenOptions = tokenOptions;
}
...
그런 다음 로그인 끝점에 대한 처리기에서 토큰을 생성해야합니다. 제 예에서는 사용자 이름과 암호를 사용하고 if 문을 사용하여 유효성을 검사하지만 클레임을 만들거나로드하는 것이 중요합니다. 기반 ID 및 해당 토큰 생성 :
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);
}
And that should be it. Just add [Authorize("Bearer")]
to any method or class you want to protect, and you should get an error if you attempt to access it without a token present. If you want to return a 401 instead of a 500 error, you'll need to register a custom exception handler as I have in my example here.
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:
// Create a new branch where the registered middleware will be executed only for non API calls.
app.UseWhen(context => !context.Request.Path.StartsWithSegments(new PathString("/api")), branch => {
// Insert a new cookies middleware in the pipeline to store
// the user identity returned by the external identity provider.
branch.UseCookieAuthentication(new CookieAuthenticationOptions {
AutomaticAuthenticate = true,
AutomaticChallenge = true,
AuthenticationScheme = "ServerCookie",
CookieName = CookieAuthenticationDefaults.CookiePrefix + "ServerCookie",
ExpireTimeSpan = TimeSpan.FromMinutes(5),
LoginPath = new PathString("/signin"),
LogoutPath = new PathString("/signout")
});
branch.UseGoogleAuthentication(new GoogleOptions {
ClientId = "560027070069-37ldt4kfuohhu3m495hk2j4pjp92d382.apps.googleusercontent.com",
ClientSecret = "n2Q-GEw9RQjzcRbU3qhfTj8f"
});
branch.UseTwitterAuthentication(new TwitterOptions {
ConsumerKey = "6XaCTaLbMqfj6ww3zvZ5g",
ConsumerSecret = "Il2eFzGIrYhz6BWjYhVXBPQSfZuS4xoHpSSyD9PI"
});
});
The logic in /Providers/AuthorizationProvider.cs and the RessourceController of that project are also worth having a look at ;).
Alternatively you can also use the following code to validate tokens (there is also a snippet to make it work with signalR):
// Add a new middleware validating access tokens.
app.UseOAuthValidation(options =>
{
// Automatic authentication must be enabled
// for SignalR to receive the access token.
options.AutomaticAuthenticate = true;
options.Events = new OAuthValidationEvents
{
// Note: for SignalR connections, the default Authorization header does not work,
// because the WebSockets JS API doesn't allow setting custom parameters.
// To work around this limitation, the access token is retrieved from the query string.
OnRetrieveToken = context =>
{
// Note: when the token is missing from the query string,
// context.Token is null and the JWT bearer middleware will
// automatically try to retrieve it from the Authorization header.
context.Token = context.Request.Query["access_token"];
return Task.FromResult(0);
}
};
});
For issuing token you can use the openId Connect server packages like so:
// Add a new middleware issuing access tokens.
app.UseOpenIdConnectServer(options =>
{
options.Provider = new AuthenticationProvider();
// Enable the authorization, logout, token and userinfo endpoints.
//options.AuthorizationEndpointPath = "/connect/authorize";
//options.LogoutEndpointPath = "/connect/logout";
options.TokenEndpointPath = "/connect/token";
//options.UserinfoEndpointPath = "/connect/userinfo";
// Note: if you don't explicitly register a signing key, one is automatically generated and
// persisted on the disk. If the key cannot be persisted, an exception is thrown.
//
// On production, using a X.509 certificate stored in the machine store is recommended.
// You can generate a self-signed certificate using Pluralsight's self-cert utility:
// https://s3.amazonaws.com/pluralsight-free/keith-brown/samples/SelfCert.zip
//
// options.SigningCredentials.AddCertificate("7D2A741FE34CC2C7369237A5F2078988E17A6A75");
//
// Alternatively, you can also store the certificate as an embedded .pfx resource
// directly in this assembly or in a file published alongside this project:
//
// options.SigningCredentials.AddCertificate(
// assembly: typeof(Startup).GetTypeInfo().Assembly,
// resource: "Nancy.Server.Certificate.pfx",
// password: "Owin.Security.OpenIdConnect.Server");
// Note: see AuthorizationController.cs for more
// information concerning ApplicationCanDisplayErrors.
options.ApplicationCanDisplayErrors = true // in dev only ...;
options.AllowInsecureHttp = true // in dev only...;
});
EDIT: 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
Have a look at OpenIddict - it's a new project (at the time of writing) that makes it easy to configure the creation of JWT tokens and refresh tokens in ASP.NET 5. The validation of the tokens is handled by other software.
Assuming you use Identity
with Entity Framework
, the last line is what you'd add to your ConfigureServices
method:
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
.AddOpenIddictCore<Application>(config => config.UseEntityFramework());
In Configure
, you set up OpenIddict to serve JWT tokens:
app.UseOpenIddictCore(builder =>
{
// 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;
});
You also configure the validation of tokens in Configure
:
// use jwt bearer authentication
app.UseJwtBearerAuthentication(options =>
{
options.AutomaticAuthenticate = true;
options.AutomaticChallenge = true;
options.RequireHttpsMetadata = false;
options.Audience = "http://localhost:58292/";
options.Authority = "http://localhost:58292/";
});
There are one or two other minor things, such as your DbContext needs to derive from OpenIddictContext.
You can see a full length explanation on this blog post: http://capesean.co.za/blog/asp-net-5-jwt-tokens/
A functioning demo is available at: https://github.com/capesean/openiddict-test
참고URL : https://stackoverflow.com/questions/29048122/token-based-authentication-in-asp-net-core
'Programing' 카테고리의 다른 글
Ruby 1.9.2가“.”을 제거하는 이유 (0) | 2020.06.08 |
---|---|
MSBuild 내장 변수 목록 (0) | 2020.06.08 |
setUp ()과 setUpBeforeClass ()의 차이점 (0) | 2020.06.08 |
display : flex를 사용하여 CSS로 남은 수직 공간 채우기 (0) | 2020.06.08 |
스프링 부트 REST 서비스 예외 처리 (0) | 2020.06.08 |