Migración de la Identity autenticación a ASP.NET Core 2.0
Por Scott Addie y Hao Scott
ASP.NET Core 2.0 tiene un nuevo modelo para la autenticación y que Identity simplifica la configuración mediante el uso de servicios. ASP.NET Core Las aplicaciones 1.x que usan la autenticación o se pueden actualizar para usar el nuevo modelo, como Identity se describe a continuación.
Actualización de espacios de nombres
En la versión 1.x, se encontraron clases como IdentityRole y en el espacio de nombres IdentityUser Microsoft.AspNetCore.Identity.EntityFrameworkCore .
En la versión 2.0, el espacio Microsoft.AspNetCore.Identity de nombres se convirtió en el nuevo hogar de varias de estas clases. Con el código Identity predeterminado, las clases afectadas incluyen ApplicationUser y Startup . Ajuste las using instrucciones para resolver las referencias afectadas.
Middleware y servicios de autenticación
En los proyectos 1.x, la autenticación se configura a través de middleware. Se invoca un método de middleware para cada esquema de autenticación que quiera admitir.
En el ejemplo 1.x siguiente se configura la autenticación de Facebook con Identity en Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory)
{
app.UseIdentity();
app.UseFacebookAuthentication(new FacebookOptions {
AppId = Configuration["auth:facebook:appid"],
AppSecret = Configuration["auth:facebook:appsecret"]
});
}
En los proyectos 2.0, la autenticación se configura a través de servicios. Cada esquema de autenticación se registra en ConfigureServices el método de Startup.cs. El UseIdentity método se reemplaza por UseAuthentication .
En el ejemplo 2.0 siguiente se configura la autenticación de Facebook con Identity en Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
// If you want to tweak Identity cookies, they're no longer part of IdentityOptions.
services.ConfigureApplicationCookie(options => options.LoginPath = "/Account/LogIn");
services.AddAuthentication()
.AddFacebook(options =>
{
options.AppId = Configuration["auth:facebook:appid"];
options.AppSecret = Configuration["auth:facebook:appsecret"];
});
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory) {
app.UseAuthentication();
}
El método agrega un único componente de middleware de autenticación, que es responsable de la autenticación UseAuthentication automática y del control de las solicitudes de autenticación remota. Reemplaza todos los componentes de middleware individuales por un único componente de middleware común.
A continuación se muestran las instrucciones de migración 2.0 para cada esquema de autenticación principal.
CookieAutenticación basada en
Seleccione una de las dos opciones siguientes y realice los cambios necesarios en Startup.cs:
Uso cookie de con Identity
Reemplace
UseIdentitypor en el métodoUseAuthenticationConfigure:app.UseAuthentication();AddIdentityInvoque el método en el método para agregar los servicios deConfigureServicescookie autenticación.Opcionalmente, invoque
ConfigureApplicationCookieel método o en el método para ajustar laConfigureExternalCookieConfigureServicesIdentity cookie configuración.services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); services.ConfigureApplicationCookie(options => options.LoginPath = "/Account/LogIn");
Usar cookie sin Identity
Reemplace la
UseCookieAuthenticationllamada al método en el método porConfigureUseAuthentication:app.UseAuthentication();AddAuthenticationInvoqueAddCookielos métodos y en el métodoConfigureServices:// If you don't want the cookie to be automatically authenticated and assigned to HttpContext.User, // remove the CookieAuthenticationDefaults.AuthenticationScheme parameter passed to AddAuthentication. services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.LoginPath = "/Account/LogIn"; options.LogoutPath = "/Account/LogOff"; });
Autenticación de portador JWT
Realice los siguientes cambios en Startup.cs:
Reemplace la
UseJwtBearerAuthenticationllamada al método en el método porConfigureUseAuthentication:app.UseAuthentication();AddJwtBearerInvoque el método en el métodoConfigureServices:services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Audience = "http://localhost:5001/"; options.Authority = "http://localhost:5000/"; });Este fragmento de código no usa Identity , por lo que el esquema predeterminado debe establecerse pasando al
JwtBearerDefaults.AuthenticationSchememétodoAddAuthentication.
Autenticación de Conectar OpenID (OIDC)
Realice los siguientes cambios en Startup.cs:
Reemplace la
UseOpenIdConnectAuthenticationllamada al método en el método porConfigureUseAuthentication:app.UseAuthentication();AddOpenIdConnectInvoque el método en el métodoConfigureServices:services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie() .AddOpenIdConnect(options => { options.Authority = Configuration["auth:oidc:authority"]; options.ClientId = Configuration["auth:oidc:clientid"]; });Reemplace la
PostLogoutRedirectUripropiedad de la acción porOpenIdConnectOptionsSignedOutRedirectUri:.AddOpenIdConnect(options => { options.SignedOutRedirectUri = "https://contoso.com"; });
Autenticación con Facebook
Realice los siguientes cambios en Startup.cs:
Reemplace la
UseFacebookAuthenticationllamada al método en el método porConfigureUseAuthentication:app.UseAuthentication();AddFacebookInvoque el método en el métodoConfigureServices:services.AddAuthentication() .AddFacebook(options => { options.AppId = Configuration["auth:facebook:appid"]; options.AppSecret = Configuration["auth:facebook:appsecret"]; });
Autenticación con Google
Realice los siguientes cambios en Startup.cs:
Reemplace la
UseGoogleAuthenticationllamada al método en el método porConfigureUseAuthentication:app.UseAuthentication();AddGoogleInvoque el método en el métodoConfigureServices:services.AddAuthentication() .AddGoogle(options => { options.ClientId = Configuration["auth:google:clientid"]; options.ClientSecret = Configuration["auth:google:clientsecret"]; });
Autenticación con cuenta Microsoft
Para obtener más información sobre la autenticación de cuentas Microsoft, consulte este GitHub problema.
Realice los siguientes cambios en Startup.cs:
Reemplace la
UseMicrosoftAccountAuthenticationllamada al método en el método porConfigureUseAuthentication:app.UseAuthentication();AddMicrosoftAccountInvoque el método en el métodoConfigureServices:services.AddAuthentication() .AddMicrosoftAccount(options => { options.ClientId = Configuration["auth:microsoft:clientid"]; options.ClientSecret = Configuration["auth:microsoft:clientsecret"]; });
Autenticación con Twitter
Realice los siguientes cambios en Startup.cs:
Reemplace la
UseTwitterAuthenticationllamada al método en el método porConfigureUseAuthentication:app.UseAuthentication();AddTwitterInvoque el método en el métodoConfigureServices:services.AddAuthentication() .AddTwitter(options => { options.ConsumerKey = Configuration["auth:twitter:consumerkey"]; options.ConsumerSecret = Configuration["auth:twitter:consumersecret"]; });
Establecimiento de esquemas de autenticación predeterminados
En la versión 1.x, las propiedades y de la clase base AuthenticationOptions estaban diseñadas para establecerse AutomaticAuthenticate en un esquema de AutomaticChallenge autenticación único. No había ninguna manera correcta de aplicar esto.
En la versión 2.0, estas dos propiedades se han quitado como propiedades en la instancia AuthenticationOptions individual. Se pueden configurar en la llamada AddAuthentication al método dentro del método de ConfigureServices Startup.cs:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);
En el fragmento de código anterior, el esquema predeterminado se establece en CookieAuthenticationDefaults.AuthenticationScheme (" Cookie s").
Como alternativa, use una versión sobrecargada del AddAuthentication método para establecer más de una propiedad. En el siguiente ejemplo de método sobrecargado, el esquema predeterminado se establece en CookieAuthenticationDefaults.AuthenticationScheme . El esquema de autenticación también se puede especificar dentro de los atributos [Authorize] individuales o las directivas de autorización.
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
});
Defina un esquema predeterminado en 2.0 si se cumple una de las condiciones siguientes:
- Quiere que el usuario inicie sesión automáticamente.
- Use el atributo
[Authorize]o las directivas de autorización sin especificar esquemas.
Una excepción a esta regla es el AddIdentity método . Este método agrega s automáticamente y establece los esquemas de autenticación y cookie desafío predeterminados en la aplicación cookie IdentityConstants.ApplicationScheme . Además, establece el esquema de inicio de sesión predeterminado en el externo cookie IdentityConstants.ExternalScheme .
Uso de extensiones de autenticación httpContext
La IAuthenticationManager interfaz es el punto de entrada principal en el sistema de autenticación 1.x. Se ha reemplazado por un nuevo conjunto de métodos HttpContext de extensión en el espacio de nombres Microsoft.AspNetCore.Authentication .
Por ejemplo, los proyectos 1.x hacen referencia a una Authentication propiedad :
// Clear the existing external cookie to ensure a clean login process
await HttpContext.Authentication.SignOutAsync(_externalCookieScheme);
En proyectos de la versión 2.0, importe el espacio Microsoft.AspNetCore.Authentication de nombres y elimine las referencias de Authentication propiedad:
// Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
Windows Autenticación (HTTP.sys/IISIntegration)
Hay dos variaciones de Windows autenticación:
El host solo permite usuarios autenticados. Esta variación no se ve afectada por los cambios de la versión 2.0.
El host permite usuarios anónimos y autenticados. Esta variación se ve afectada por los cambios de la versión 2.0. Por ejemplo, la aplicación debe permitir usuarios anónimos en iis oHTTP.sys nivel, pero autorizar a los usuarios en el nivel de controlador. En este escenario, establezca el esquema predeterminado en el
Startup.ConfigureServicesmétodo .Para Microsoft.AspNetCore.Server.IISIntegration,establezca el esquema predeterminado en
IISDefaults.AuthenticationScheme:using Microsoft.AspNetCore.Server.IISIntegration; services.AddAuthentication(IISDefaults.AuthenticationScheme);Para Microsoft.AspNetCore.Server.HttpSys,establezca el esquema predeterminado en
HttpSysDefaults.AuthenticationScheme:using Microsoft.AspNetCore.Server.HttpSys; services.AddAuthentication(HttpSysDefaults.AuthenticationScheme);Si no se establece el esquema predeterminado, se impide que la solicitud de autorización (desafío) funcione con la siguiente excepción:
System.InvalidOperationException: no se especificó authenticationScheme y no se encontró DefaultChallengeScheme.
Para obtener más información, vea Configurar la autenticación de Windows en ASP.NET Core.
IdentityCookieInstancias de opciones
Un efecto secundario de los cambios de la versión 2.0 es el cambio al uso de opciones con nombre en lugar de cookie instancias de opciones. Se quita la capacidad de personalizar Identity cookie los nombres de esquema.
Por ejemplo, los proyectos 1.x usan la inserción de constructores para pasar un parámetro a IdentityCookieOptions AccountController.cs y ManageController.cs. Se accede cookie al esquema de autenticación externo desde la instancia proporcionada:
public AccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IOptions<IdentityCookieOptions> identityCookieOptions,
IEmailSender emailSender,
ISmsSender smsSender,
ILoggerFactory loggerFactory)
{
_userManager = userManager;
_signInManager = signInManager;
_externalCookieScheme = identityCookieOptions.Value.ExternalCookieAuthenticationScheme;
_emailSender = emailSender;
_smsSender = smsSender;
_logger = loggerFactory.CreateLogger<AccountController>();
}
La inserción de constructores mencionada anteriormente no es necesaria en los proyectos de 2.0 y _externalCookieScheme el campo se puede eliminar:
public AccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IEmailSender emailSender,
ISmsSender smsSender,
ILoggerFactory loggerFactory)
{
_userManager = userManager;
_signInManager = signInManager;
_emailSender = emailSender;
_smsSender = smsSender;
_logger = loggerFactory.CreateLogger<AccountController>();
}
Los proyectos 1.x usaban _externalCookieScheme el campo de la manera siguiente:
// Clear the existing external cookie to ensure a clean login process
await HttpContext.Authentication.SignOutAsync(_externalCookieScheme);
En los proyectos de la versión 2.0, reemplace el código anterior por lo siguiente. La IdentityConstants.ExternalScheme constante se puede usar directamente.
// Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
Resuelva la llamada recién agregada SignOutAsync importando el siguiente espacio de nombres:
using Microsoft.AspNetCore.Authentication;
Agregar propiedades Identity de navegación POCO de usuario
Se han Entity Framework propiedades de navegación básicas (EF) de la base POCO (objeto CLR antiguo IdentityUser sin formato). Si el proyecto 1.x usa estas propiedades, vuelva a agregarlas manualmente al proyecto 2.0:
/// <summary>
/// Navigation property for the roles this user belongs to.
/// </summary>
public virtual ICollection<IdentityUserRole<int>> Roles { get; } = new List<IdentityUserRole<int>>();
/// <summary>
/// Navigation property for the claims this user possesses.
/// </summary>
public virtual ICollection<IdentityUserClaim<int>> Claims { get; } = new List<IdentityUserClaim<int>>();
/// <summary>
/// Navigation property for this users login accounts.
/// </summary>
public virtual ICollection<IdentityUserLogin<int>> Logins { get; } = new List<IdentityUserLogin<int>>();
Para evitar claves externas duplicadas al ejecutar migraciones EF Core, agregue lo siguiente al método de la IdentityDbContext OnModelCreating clase (después de la base.OnModelCreating(); llamada):
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Core Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Core Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
builder.Entity<ApplicationUser>()
.HasMany(e => e.Claims)
.WithOne()
.HasForeignKey(e => e.UserId)
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<ApplicationUser>()
.HasMany(e => e.Logins)
.WithOne()
.HasForeignKey(e => e.UserId)
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<ApplicationUser>()
.HasMany(e => e.Roles)
.WithOne()
.HasForeignKey(e => e.UserId)
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
}
Reemplazar GetExternalAuthenticationSchemes
El método GetExternalAuthenticationSchemes sincrónico se quitó en favor de una versión asincrónica. Los proyectos 1.x tienen el código siguiente en Controllers/ManageController.cs:
var otherLogins = _signInManager.GetExternalAuthenticationSchemes().Where(auth => userLogins.All(ul => auth.AuthenticationScheme != ul.LoginProvider)).ToList();
Este método también aparece en Views/Account/Login.cshtml:
@{
var loginProviders = SignInManager.GetExternalAuthenticationSchemes().ToList();
if (loginProviders.Count == 0)
{
<div>
<p>
There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a>
for details on setting up this ASP.NET application to support logging in via external services.
</p>
</div>
}
else
{
<form asp-controller="Account" asp-action="ExternalLogin" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal">
<div>
<p>
@foreach (var provider in loginProviders)
{
<button type="submit" class="btn btn-default" name="provider" value="@provider.AuthenticationScheme" title="Log in using your @provider.DisplayName account">@provider.AuthenticationScheme</button>
}
</p>
</div>
</form>
}
}
En proyectos de la versión 2.0, use el GetExternalAuthenticationSchemesAsync método . El cambio en ManageController.cs es similar al código siguiente:
var schemes = await _signInManager.GetExternalAuthenticationSchemesAsync();
var otherLogins = schemes.Where(auth => userLogins.All(ul => auth.Name != ul.LoginProvider)).ToList();
En Login.cshtml, la propiedad a la que se tiene acceso AuthenticationScheme en el bucle cambia a foreach Name :
@{
var loginProviders = (await SignInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (loginProviders.Count == 0)
{
<div>
<p>
There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a>
for details on setting up this ASP.NET application to support logging in via external services.
</p>
</div>
}
else
{
<form asp-controller="Account" asp-action="ExternalLogin" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal">
<div>
<p>
@foreach (var provider in loginProviders)
{
<button type="submit" class="btn btn-default" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
}
</p>
</div>
</form>
}
}
Cambio de la propiedad ManageLoginsViewModel
Un ManageLoginsViewModel objeto se usa en la acción de ManageLogins ManageController.cs. En proyectos 1.x, el tipo de valor devuelto de OtherLogins propiedad del objeto es IList<AuthenticationDescription> . Este tipo de valor devuelto requiere una importación de Microsoft.AspNetCore.Http.Authentication :
using System.Collections.Generic;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Identity;
namespace AspNetCoreDotNetCore1App.Models.ManageViewModels
{
public class ManageLoginsViewModel
{
public IList<UserLoginInfo> CurrentLogins { get; set; }
public IList<AuthenticationDescription> OtherLogins { get; set; }
}
}
En los proyectos de la versión 2.0, el tipo de valor devuelto cambia a IList<AuthenticationScheme> . Este nuevo tipo de valor devuelto requiere reemplazar Microsoft.AspNetCore.Http.Authentication la importación por una Microsoft.AspNetCore.Authentication importación.
using System.Collections.Generic;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
namespace AspNetCoreDotNetCore2App.Models.ManageViewModels
{
public class ManageLoginsViewModel
{
public IList<UserLoginInfo> CurrentLogins { get; set; }
public IList<AuthenticationScheme> OtherLogins { get; set; }
}
}
Recursos adicionales
Para obtener más información, consulte la explicación del problema de autenticación 2.0 en GitHub.