net for example crear autenticacion asp c# security asp.net-web-api jwt

for - token web service c#



Autenticación JWT para API web ASP.NET (4)

Estoy tratando de admitir el token portador JWT (JSON Web Token) en mi aplicación API web y me estoy perdiendo.

Veo soporte para .NET Core y para aplicaciones OWIN.
Actualmente estoy alojando mi aplicación en IIS.

¿Cómo puedo lograr este módulo de autenticación en mi aplicación? ¿Hay alguna forma en que pueda usar la configuración de <authentication> similar a la forma en que uso formularios / autenticación de Windows?


Aquí hay una implementación mínima y segura de una autenticación basada en notificaciones utilizando el token JWT en una API web ASP.NET Core.

En primer lugar, debe exponer un punto final que devuelve un token JWT con notificaciones asignadas a un usuario:

/// <summary> /// Login provides API to verify user and returns authentication token. /// API Path: api/account/login /// </summary> /// <param name="paramUser">Username and Password</param> /// <returns>{Token: [Token] }</returns> [HttpPost("login")] [AllowAnonymous] public async Task<IActionResult> Login([FromBody] UserRequestVM paramUser, CancellationToken ct) { var result = await UserApplication.PasswordSignInAsync(paramUser.Email, paramUser.Password, false, lockoutOnFailure: false); if (result.Succeeded) { UserRequestVM request = new UserRequestVM(); request.Email = paramUser.Email; ApplicationUser UserDetails = await this.GetUserByEmail(request); List<ApplicationClaim> UserClaims = await this.ClaimApplication.GetListByUser(UserDetails); var Claims = new ClaimsIdentity(new Claim[] { new Claim(JwtRegisteredClaimNames.Sub, paramUser.Email.ToString()), new Claim(UserId, UserDetails.UserId.ToString()) }); //Adding UserClaims to JWT claims foreach (var item in UserClaims) { Claims.AddClaim(new Claim(item.ClaimCode, string.Empty)); } var tokenHandler = new JwtSecurityTokenHandler(); // this information will be retrived from you Configuration //I have injected Configuration provider service into my controller var encryptionkey = Configuration["Jwt:Encryptionkey"]; var key = Encoding.ASCII.GetBytes(encryptionkey); var tokenDescriptor = new SecurityTokenDescriptor { Issuer = Configuration["Jwt:Issuer"], Subject = Claims, // this information will be retrived from you Configuration //I have injected Configuration provider service into my controller Expires = DateTime.UtcNow.AddMinutes(Convert.ToDouble(Configuration["Jwt:ExpiryTimeInMinutes"])), //algorithm to sign the token SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) }; var token = tokenHandler.CreateToken(tokenDescriptor); var tokenString = tokenHandler.WriteToken(token); return Ok(new { token = tokenString }); } return BadRequest("Wrong Username or password"); }

ahora necesita Agregar autenticación a sus servicios en ConfigureServices dentro de su startup.cs para agregar autenticación JWT como su servicio de autenticación predeterminado de esta manera:

services.AddAuthentication(x => { x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(cfg => { cfg.RequireHttpsMetadata = false; cfg.SaveToken = true; cfg.TokenValidationParameters = new TokenValidationParameters() { //ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["JWT:Encryptionkey"])), ValidateAudience = false, ValidateLifetime = true, ValidIssuer = configuration["Jwt:Issuer"], //ValidAudience = Configuration["Jwt:Audience"], //IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWT:Key"])), }; });

ahora puede agregar políticas a sus servicios de autorización como esta:

services.AddAuthorization(options => { options.AddPolicy("YourPolicyNameHere", policy => policy.RequireClaim("YourClaimNameHere")); });

ALTERNATIVAMENTE , también puede (no es necesario) llenar todos sus reclamos de su base de datos, ya que esto solo se ejecutará una vez en el inicio de su aplicación y agregarlos a políticas como esta:

services.AddAuthorization(async options => { var ClaimList = await claimApplication.GetList(applicationClaim); foreach (var item in ClaimList) { options.AddPolicy(item.ClaimCode, policy => policy.RequireClaim(item.ClaimCode)); } });

ahora puede poner el filtro de Políticas en cualquiera de los métodos que desea autorizar de esta manera:

[HttpPost("update")] [Authorize(Policy = "ACC_UP")] public async Task<IActionResult> Update([FromBody] UserRequestVM requestVm, CancellationToken ct) { //your logic goes here }

Espero que esto ayude


Creo que debería usar un servidor de fiesta 3D para admitir el token JWT y no hay compatibilidad JWT lista para usar en WEB API 2.

Sin embargo, hay un proyecto OWIN para admitir algún formato de token firmado (no JWT). Funciona como un protocolo OAuth reducido para proporcionar solo una forma simple de autenticación para un sitio web.

Puede leer más sobre esto, por ejemplo, here .

Es bastante largo, pero la mayoría de las partes son detalles con controladores e identidad ASP.NET que quizás no necesite en absoluto. Los más importantes son

Paso 9: Agregue soporte para OAuth Bearer Tokens Generation

Paso 12: Prueba de la API de back-end

Allí puede leer cómo configurar el punto final (por ejemplo, "/ token") al que puede acceder desde la interfaz (y detalles sobre el formato de la solicitud).

Otros pasos proporcionan detalles sobre cómo conectar ese punto final a la base de datos, etc. y puede elegir las partes que necesita.


Logré lograrlo con un esfuerzo mínimo (tan simple como con ASP.NET Core).

Para eso utilizo el archivo OWIN Startup.cs y la biblioteca Microsoft.Owin.Security.Jwt .

Para que la aplicación llegue a Startup.cs necesitamos modificar Web.config :

<configuration> <appSettings> <add key="owin:AutomaticAppStartup" value="true" /> ...

Así es como debe verse Startup.cs :

using MyApp.Helpers; using Microsoft.IdentityModel.Tokens; using Microsoft.Owin; using Microsoft.Owin.Security; using Microsoft.Owin.Security.Jwt; using Owin; [assembly: OwinStartup(typeof(MyApp.App_Start.Startup))] namespace MyApp.App_Start { public class Startup { public void Configuration(IAppBuilder app) { app.UseJwtBearerAuthentication( new JwtBearerAuthenticationOptions { AuthenticationMode = AuthenticationMode.Active, TokenValidationParameters = new TokenValidationParameters() { ValidAudience = ConfigHelper.GetAudience(), ValidIssuer = ConfigHelper.GetIssuer(), IssuerSigningKey = ConfigHelper.GetSymmetricSecurityKey(), ValidateLifetime = true, ValidateIssuerSigningKey = true } }); } } }

Muchos de ustedes usan ASP.NET Core hoy en día, por lo que, como pueden ver, no difiere mucho de lo que tenemos allí.

Primero me dejó perplejo, estaba tratando de implementar proveedores personalizados, etc. Pero no esperaba que fuera tan simple. OWIN simplemente rocas!

Solo una cosa para mencionar: después de habilitar OWIN Startup, la biblioteca NSWag dejó de funcionar para mí (por ejemplo, algunos de ustedes querrían generar automáticamente proxy HTTP de mecanografía para la aplicación Angular).

La solución también fue muy simple: reemplacé NSWag por Swashbuckle y no tuve más problemas.

Ok, ahora compartiendo el código ConfigHelper :

public class ConfigHelper { public static string GetIssuer() { string result = System.Configuration.ConfigurationManager.AppSettings["Issuer"]; return result; } public static string GetAudience() { string result = System.Configuration.ConfigurationManager.AppSettings["Audience"]; return result; } public static SigningCredentials GetSigningCredentials() { var result = new SigningCredentials(GetSymmetricSecurityKey(), SecurityAlgorithms.HmacSha256); return result; } public static string GetSecurityKey() { string result = System.Configuration.ConfigurationManager.AppSettings["SecurityKey"]; return result; } public static byte[] GetSymmetricSecurityKeyAsBytes() { var issuerSigningKey = GetSecurityKey(); byte[] data = Encoding.UTF8.GetBytes(issuerSigningKey); return data; } public static SymmetricSecurityKey GetSymmetricSecurityKey() { byte[] data = GetSymmetricSecurityKeyAsBytes(); var result = new SymmetricSecurityKey(data); return result; } public static string GetCorsOrigins() { string result = System.Configuration.ConfigurationManager.AppSettings["CorsOrigins"]; return result; } }

Otro aspecto importante: envié el token JWT a través del encabezado de autorización , por lo que el código mecanografiado me busca de la siguiente manera:

(El siguiente código es generado por NSWag )

@Injectable() export class TeamsServiceProxy { private http: HttpClient; private baseUrl: string; protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined; constructor(@Inject(HttpClient) http: HttpClient, @Optional() @Inject(API_BASE_URL) baseUrl?: string) { this.http = http; this.baseUrl = baseUrl ? baseUrl : "https://localhost:44384"; } add(input: TeamDto | null): Observable<boolean> { let url_ = this.baseUrl + "/api/Teams/Add"; url_ = url_.replace(/[?&]$/, ""); const content_ = JSON.stringify(input); let options_ : any = { body: content_, observe: "response", responseType: "blob", headers: new HttpHeaders({ "Content-Type": "application/json", "Accept": "application/json", "Authorization": "Bearer " + localStorage.getItem(''token'') }) };

Ver parte de encabezados - "Authorization": "Bearer " + localStorage.getItem(''token'')


Respondí esta pregunta: Cómo asegurar una API web ASP.NET hace 4 años usando HMAC.

Ahora, muchas cosas cambiaron en seguridad, especialmente JWT se está volviendo popular. Aquí intentaré explicar cómo usar JWT de la manera más simple y básica que pueda, para que no nos perdamos de la selva de OWIN, Oauth2, ASP.NET Identity ... :).

Si no conoce el token JWT, debe echar un vistazo a:

https://tools.ietf.org/html/rfc7519

Básicamente, un token JWT se ve así:

<base64-encoded header>.<base64-encoded claims>.<base64-encoded signature>

Ejemplo:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1NzI0LCJleHAiOjE0Nzc1NjY5MjQsImlhdCI6MTQ3NzU2NTcyNH0.6MzD1VwA5AcOcajkFyKhLYybr3h13iZjDyHm9zysDFQ

Un token JWT tiene tres secciones:

  1. Encabezado: formato JSON que está codificado en Base64
  2. Reclamaciones: formato JSON que está codificado en Base64.
  3. Firma: Creado y firmado basado en Encabezado y Reclamaciones que está codificado en Base64.

Si usa el sitio web jwt.io con el token anterior, puede decodificar el token y verlo como a continuación:

Técnicamente, JWT usa una firma que está firmada desde encabezados y notificaciones con algoritmo de seguridad especificado en los encabezados (ejemplo: HMACSHA256). Por lo tanto, se requiere que JWT se transfiera a través de HTTP si almacena información confidencial en las notificaciones.

Ahora, para usar la autenticación JWT, realmente no necesita un middleware OWIN si tiene un sistema Web Api heredado. El concepto simple es cómo proporcionar el token JWT y cómo validar el token cuando llega la solicitud. Eso es.

Volviendo a la demostración, para mantener el token JWT ligero, solo almaceno el username y el expiration time en JWT. Pero de esta manera, debe reconstruir una nueva identidad local (principal) para agregar más información como: roles ... si desea hacer la autorización de roles. Pero, si desea agregar más información a JWT, depende de usted: es muy flexible.

En lugar de utilizar el middleware OWIN, simplemente puede proporcionar un punto final de token JWT mediante la acción del controlador:

public class TokenController : ApiController { // This is naive endpoint for demo, it should use Basic authentication // to provide token or POST request [AllowAnonymous] public string Get(string username, string password) { if (CheckUser(username, password)) { return JwtManager.GenerateToken(username); } throw new HttpResponseException(HttpStatusCode.Unauthorized); } public bool CheckUser(string username, string password) { // should check in the database return true; } }

Esta es una acción ingenua; en producción, debe usar una solicitud POST o un punto final de autenticación básica para proporcionar el token JWT.

¿Cómo generar el token en función del username de username ?

Puede usar el paquete NuGet llamado System.IdentityModel.Tokens.Jwt de Microsoft para generar el token, o incluso otro paquete si lo desea. En la demostración, uso HMACSHA256 con SymmetricKey :

/// <summary> /// Use the below code to generate symmetric Secret Key /// var hmac = new HMACSHA256(); /// var key = Convert.ToBase64String(hmac.Key); /// </summary> private const string Secret = "db3OIsj+BXE9NZDy0t8W3TcNekrF+2d/1sFnWG4HnV8TZY30iTOdtVWJG8abWvB1GlOgJuQZdcF2Luqm/hccMw=="; public static string GenerateToken(string username, int expireMinutes = 20) { var symmetricKey = Convert.FromBase64String(Secret); var tokenHandler = new JwtSecurityTokenHandler(); var now = DateTime.UtcNow; var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, username) }), Expires = now.AddMinutes(Convert.ToInt32(expireMinutes)), SigningCredentials = new SigningCredentials( new SymmetricSecurityKey(symmetricKey), SecurityAlgorithms.HmacSha256Signature) }; var stoken = tokenHandler.CreateToken(tokenDescriptor); var token = tokenHandler.WriteToken(stoken); return token; }

El punto final para proporcionar el token JWT está hecho. Ahora, ¿cómo validar el JWT cuando llega la solicitud? En la demostración, he creado JwtAuthenticationAttribute que hereda de IAuthenticationFilter (más detalles sobre el filtro de autenticación here ).

Con este atributo, puede autenticar cualquier acción: solo tiene que poner este atributo en esa acción.

public class ValueController : ApiController { [JwtAuthentication] public string Get() { return "value"; } }

También puede usar el middleware OWIN o DelegateHander si desea validar todas las solicitudes entrantes para su WebAPI (no específico para el Controlador o la acción)

A continuación se muestra el método principal del filtro de autenticación:

private static bool ValidateToken(string token, out string username) { username = null; var simplePrinciple = JwtManager.GetPrincipal(token); var identity = simplePrinciple.Identity as ClaimsIdentity; if (identity == null) return false; if (!identity.IsAuthenticated) return false; var usernameClaim = identity.FindFirst(ClaimTypes.Name); username = usernameClaim?.Value; if (string.IsNullOrEmpty(username)) return false; // More validate to check whether username exists in system return true; } protected Task<IPrincipal> AuthenticateJwtToken(string token) { string username; if (ValidateToken(token, out username)) { // based on username to get more information from database // in order to build local identity var claims = new List<Claim> { new Claim(ClaimTypes.Name, username) // Add more claims if needed: Roles, ... }; var identity = new ClaimsIdentity(claims, "Jwt"); IPrincipal user = new ClaimsPrincipal(identity); return Task.FromResult(user); } return Task.FromResult<IPrincipal>(null); }

El flujo de trabajo consiste en utilizar la biblioteca JWT (paquete NuGet arriba) para validar el token JWT y luego devolver ClaimsPrincipal . Puede realizar más validaciones como verificar si el usuario existe en su sistema y agregar otras validaciones personalizadas si lo desea. El código para validar el token JWT y recuperar el principal:

public static ClaimsPrincipal GetPrincipal(string token) { try { var tokenHandler = new JwtSecurityTokenHandler(); var jwtToken = tokenHandler.ReadToken(token) as JwtSecurityToken; if (jwtToken == null) return null; var symmetricKey = Convert.FromBase64String(Secret); var validationParameters = new TokenValidationParameters() { RequireExpirationTime = true, ValidateIssuer = false, ValidateAudience = false, IssuerSigningKey = new SymmetricSecurityKey(symmetricKey) }; SecurityToken securityToken; var principal = tokenHandler.ValidateToken(token, validationParameters, out securityToken); return principal; } catch (Exception) { //should write log return null; } }

Si se valida el token JWT y se devuelve el principal, debe crear una nueva identidad local y agregar más información para verificar la autorización del rol.

Recuerde agregar config.Filters.Add(new AuthorizeAttribute()); (autorización predeterminada) a nivel global para evitar cualquier solicitud anónima a sus recursos.

Puede usar Postman para probar la demostración:

Solicitar token (ingenuo como mencioné anteriormente, solo para demostración):

GET http://localhost:{port}/api/token?username=cuong&password=1

Coloque el token JWT en el encabezado de la solicitud autorizada, por ejemplo:

GET http://localhost:{port}/api/value Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1MjU4LCJleHAiOjE0Nzc1NjY0NTgsImlhdCI6MTQ3NzU2NTI1OH0.dSwwufd4-gztkLpttZsZ1255oEzpWCJkayR_4yvNL1s

La demostración se pone aquí: https://github.com/cuongle/WebApi.Jwt