on Identity ASP.NET Core 소개

작성자: Rick Anderson

ASP.NET Core Identity:

  • UI(사용자 인터페이스) 로그인 기능을 지원하는 API입니다.
  • 사용자, 암호, 프로필 데이터, 역할, 클레임, 토큰, 메일 확인 등을 관리합니다.

사용자는 에 저장된 로그인 정보를 사용하여 계정을 Identity 만들거나 외부 로그인 공급자를 사용할 수 있습니다. 지원되는 외부 로그인 공급자에는 Facebook, Google, Microsoft 계정 및 Twitter가 포함됩니다.

모든 사용자의 인증을 전역으로 요구하는 방법에 대한 자세한 내용은 인증된 사용자 요구를 참조하세요.

Identity 소스 코드는 GitHub 사용할 수 있습니다. 스캐폴드 Identity 생성된 파일을 보고 와의 템플릿 상호 작용을 Identity 검토합니다.

Identity는 일반적으로 SQL Server 데이터베이스를 사용하여 사용자 이름, 암호 및 프로필 데이터를 저장하도록 구성됩니다. 또는 다른 영구 저장소(예: Azure Table Storage)를 사용할 수 있습니다.

이 항목에서는 를 사용하여 Identity 사용자를 등록, 로그인 및 로그아웃하는 방법을 알아봅니다. 참고: 템플릿은 사용자 이름과 전자 메일을 사용자에 대해 동일하게 처리합니다. 를 사용하는 앱을 만드는 방법에 대한 자세한 지침은 Identity 다음 단계를 참조하세요.

Microsoft ID 플랫폼 다음과 같습니다.

  • Azure AD(Azure Active Directory) 개발자 플랫폼의 진화
  • 에 관련이 없는 ASP.NET Core Identity 경우

ASP.NET Core Identity는 ASP.NET Core 웹앱에 UI(사용자 인터페이스) 로그인 기능을 추가합니다. 웹 API 및 SPA를 보호하려면 다음 중 하나를 사용합니다.

IdentityServer4는 ASP.NET Core용 OpenID Connect 및 OAuth 2.0 프레임워크입니다. IdentityServer4에서는 다음과 같은 보안 기능을 사용할 수 있습니다.

  • AaaS(Authentication as a Service)
  • 여러 응용 프로그램 유형에 대한 SSO(Single Sign-On/Off)
  • API에 대한 액세스 제어
  • 페더레이션 게이트웨이

자세한 내용은 IdentityServer4 시작을 참조하세요.

샘플 코드(다운로드 방법)를 보거나 다운로드합니다.

인증을 사용하여 웹앱 만들기

개별 사용자 계정으로 ASP.NET Core 웹 애플리케이션 프로젝트를 만듭니다.

  • ASP.NET Core 웹앱 템플릿을 선택합니다. 프로젝트 이름을 WebApp1로 지정하여 프로젝트 다운로드와 동일한 네임스페이스를 갖습니다. 확인 을 클릭합니다.
  • 인증 유형 입력에서 개별 사용자 계정 을 선택합니다.

생성된 프로젝트는 ASP.NET Core Identity Razor 를 클래스 라이브러리로 제공합니다. Identity Razor 클래스 라이브러리는 영역이 있는 엔드포인트를 Identity 노출합니다. 예:

  • /Identity/Account/Login
  • /Identity/Account/Logout
  • /Identity/Account/Manage

마이그레이션 적용

마이그레이션을 적용하여 데이터베이스를 초기화합니다.

PMC(패키지 관리자 콘솔)에서 다음 명령을 실행합니다.

Update-Database

테스트 등록 및 로그인

앱을 실행하고 사용자를 등록합니다. 화면 크기에 따라 탐색 토글 단추를 선택하여 등록로그인 링크를 확인해야 할 수 있습니다.

데이터베이스 보기 Identity

  • 보기 메뉴에서 SQL Server 개체 탐색기 (SSOX)를 선택 합니다.
  • (Localdb) MSSQLLocalDB (SQL Server 13) 로 이동 합니다. Dbo를 마우스 오른쪽 단추로 클릭 합니다. AspNetUsers > 뷰 데이터:

SQL Server 개체 탐색기의 AspNetUsers 테이블에 대 한 상황에 맞는 메뉴

서비스 구성 Identity

서비스는 Program.cs 에 추가됩니다. 일반적인 패턴은 다음 순서로 메서드를 호출하는 것입니다.

  1. Add{Service}
  2. Services.Configure{Service}
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebApp1.Data;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.Configure<IdentityOptions>(options =>
{
    // Password settings.
    options.Password.RequireDigit = true;
    options.Password.RequireLowercase = true;
    options.Password.RequireNonAlphanumeric = true;
    options.Password.RequireUppercase = true;
    options.Password.RequiredLength = 6;
    options.Password.RequiredUniqueChars = 1;

    // Lockout settings.
    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
    options.Lockout.MaxFailedAccessAttempts = 5;
    options.Lockout.AllowedForNewUsers = true;

    // User settings.
    options.User.AllowedUserNameCharacters =
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
    options.User.RequireUniqueEmail = false;
});

builder.Services.ConfigureApplicationCookie(options =>
{
    // Cookie settings
    options.Cookie.HttpOnly = true;
    options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

    options.LoginPath = "/Identity/Account/Login";
    options.AccessDeniedPath = "/Identity/Account/AccessDenied";
    options.SlidingExpiration = true;
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapRazorPages();

app.Run();

위의 코드는 Identity 를 기본 옵션 값으로 구성합니다. 서비스는 종속성 주입을 통해 앱에서 사용할 수 있습니다.

Identity 은 UseAuthentication 을호출하여 활성화됩니다. UseAuthentication 는 인증 미들웨어를 요청 파이프라인에 추가합니다.

템플릿에서 생성된 앱은 권한 부여를 사용하지 않습니다. app.UseAuthorization 는 앱이 권한 부여를 추가하는 경우 올바른 순서로 추가되도록 하기 위해 포함됩니다. UseRouting, , 및 는 UseAuthentication UseAuthorization UseEndpoints 앞의 코드에 표시된 순서대로 호출되어야 합니다.

에 대한 자세한 내용은 IdentityOptions IdentityOptions 및 애플리케이션 시작을 참조하세요.

Register, Login, LogOut 및 RegisterConfirmation 스캐폴드

, Register Login , LogOutRegisterConfirmation 파일을 추가합니다. 권한 부여 지침이 있는 프로젝트에 대한 스캐폴드 ID에 Razor 따라 이 섹션에 표시된 코드를 생성합니다.

레지스터 검사

사용자가 페이지에서 등록 단추를 클릭하면 Register 작업이 RegisterModel.OnPostAsync 호출됩니다. 개체의 CreateAsync에서 사용자를 만듭니다. _userManager

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
                                          .ToList();
    if (ModelState.IsValid)
    {
        var user = new IdentityUser { UserName = Input.Email, Email = Input.Email };
        var result = await _userManager.CreateAsync(user, Input.Password);
        if (result.Succeeded)
        {
            _logger.LogInformation("User created a new account with password.");

            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = user.Id, code = code },
                protocol: Request.Scheme);

            await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

            if (_userManager.Options.SignIn.RequireConfirmedAccount)
            {
                return RedirectToPage("RegisterConfirmation", 
                                      new { email = Input.Email });
            }
            else
            {
                await _signInManager.SignInAsync(user, isPersistent: false);
                return LocalRedirect(returnUrl);
            }
        }
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

기본 계정 확인 사용 안 함

기본 템플릿을 사용 하면 사용자가 Account.RegisterConfirmation 계정 확인을 위해 링크를 선택할 수 있는 위치로 리디렉션됩니다. 기본값은 Account.RegisterConfirmation 테스트에 사용 되며 자동 계정 확인은 프로덕션 앱에서 사용 하지 않도록 설정 해야 합니다.

확인 된 계정을 요구 하 고 등록 시 즉각적인 로그인을 방지 하려면 DisplayConfirmAccountLink = false /Areas/ Identity /Pages/Account/RegisterConfirmation.cshtml.cs 에서를 설정 합니다.

[AllowAnonymous]
public class RegisterConfirmationModel : PageModel
{
    private readonly UserManager<IdentityUser> _userManager;
    private readonly IEmailSender _sender;

    public RegisterConfirmationModel(UserManager<IdentityUser> userManager, IEmailSender sender)
    {
        _userManager = userManager;
        _sender = sender;
    }

    public string Email { get; set; }

    public bool DisplayConfirmAccountLink { get; set; }

    public string EmailConfirmationUrl { get; set; }

    public async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
    {
        if (email == null)
        {
            return RedirectToPage("/Index");
        }

        var user = await _userManager.FindByEmailAsync(email);
        if (user == null)
        {
            return NotFound($"Unable to load user with email '{email}'.");
        }

        Email = email;
        // Once you add a real email sender, you should remove this code that lets you confirm the account
        DisplayConfirmAccountLink = false;
        if (DisplayConfirmAccountLink)
        {
            var userId = await _userManager.GetUserIdAsync(user);
            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            EmailConfirmationUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                protocol: Request.Scheme);
        }

        return Page();
    }
}

로그인

로그인 양식은 다음과 같은 경우에 표시됩니다.

  • 로그인 링크가 선택되어 있습니다.
  • 사용자가 액세스 권한이 없거나 시스템에서 인증되지 않은 제한된 페이지에 액세스하려고 합니다.

로그인 페이지의 양식이 제출되면 OnPostAsync 작업이 호출됩니다. PasswordSignInAsync``_signInManager는 개체에서 호출됩니다.

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");

    if (ModelState.IsValid)
    {
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, 
        // set lockoutOnFailure: true
        var result = await _signInManager.PasswordSignInAsync(Input.Email,
                           Input.Password, Input.RememberMe, lockoutOnFailure: true);
        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            return LocalRedirect(returnUrl);
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToPage("./LoginWith2fa", new
            {
                ReturnUrl = returnUrl,
                RememberMe = Input.RememberMe
            });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning("User account locked out.");
            return RedirectToPage("./Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

권한 부여 결정을 내리는 방법에 대한 자세한 내용은 를 ASP.NET Core 권한 부여 소개 참조하세요.

로그아웃

로그아웃 링크는 LogoutModel.OnPost 작업을 호출합니다.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace WebApp1.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class LogoutModel : PageModel
    {
        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly ILogger<LogoutModel> _logger;

        public LogoutModel(SignInManager<IdentityUser> signInManager, ILogger<LogoutModel> logger)
        {
            _signInManager = signInManager;
            _logger = logger;
        }

        public void OnGet()
        {
        }

        public async Task<IActionResult> OnPost(string returnUrl = null)
        {
            await _signInManager.SignOutAsync();
            _logger.LogInformation("User logged out.");
            if (returnUrl != null)
            {
                return LocalRedirect(returnUrl);
            }
            else
            {
                return RedirectToPage();
            }
        }
    }
}

앞의 코드에서 return RedirectToPage(); 코드는 브라우저가 새 요청을 수행하고 사용자의 ID가 업데이트되도록 리디렉션이어야 합니다.

SignOutAsync는 에 저장된 사용자의 클레임을 cookie 지웁니다.

Post는 Pages/Shared/_LoginPartial.cshtml 에 지정됩니다.

@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
    <li class="nav-item">
        <a  class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" 
                                              title="Manage">Hello @User.Identity.Name!</a>
    </li>
    <li class="nav-item">
        <form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" 
                                  asp-route-returnUrl="@Url.Page("/", new { area = "" })" 
                                  method="post" >
            <button  type="submit" class="nav-link btn btn-link text-dark">Logout</button>
        </form>
    </li>
}
else
{
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li>
}
</ul>

테스트 Identity

기본 웹 프로젝트 템플릿은 홈 페이지에 대한 익명 액세스를 허용합니다. 를 테스트하려면 Identity 를 [Authorize] 추가합니다.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace WebApp1.Pages
{
    [Authorize]
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

로그인한 경우 로그아웃합니다. 앱을 실행하고 Privacy 링크를 선택합니다. 로그인 페이지로 리디렉션됩니다.

탐험 Identity

자세히 살펴보려면 Identity 다음을 수행합니다.

Identity 구성 요소

모든 Identity 종속 NuGet 패키지는 ASP.NET Core 공유 프레임워크에 포함됩니다.

의 기본 Identity 패키지는 Microsoft.AspNetCore. 입니다. Identity 이 패키지는 에 대한 핵심 인터페이스 집합을 ASP.NET Core Identity 포함하며 에 포함되어 Microsoft.AspNetCore.Identity.EntityFrameworkCore 있습니다.

다음으로 마이그레이션 ASP.NET Core Identity

기존 저장소 마이그레이션에 대한 자세한 내용 및 Identity 지침은 인증 및 Identity 마이그레이션을 참조하세요.

암호 강도 설정

최소 암호 요구 사항을 설정하는 샘플은 구성을 참조하세요.

AddDefault Identity 및 추가Identity

AddDefaultIdentity는 ASP.NET Core 2.1에서 도입되었습니다. 를 AddDefaultIdentity 호출하는 것은 다음을 호출하는 것과 비슷합니다.

자세한 내용은 AddDefault Identity 원본을 참조하세요.

정적 자산 게시 방지 Identity

정적 Identity 자산(UI용 스타일시트 및 Identity JavaScript 파일)을 웹 루트에 게시하지 않도록 하려면 다음 ResolveStaticWebAssetsInputsDependsOn 속성 및 RemoveIdentityAssets 대상을 앱의 프로젝트 파일에 추가합니다.

<PropertyGroup>
  <ResolveStaticWebAssetsInputsDependsOn>RemoveIdentityAssets</ResolveStaticWebAssetsInputsDependsOn>
</PropertyGroup>

<Target Name="RemoveIdentityAssets">
  <ItemGroup>
    <StaticWebAsset Remove="@(StaticWebAsset)" Condition="%(SourceId) == 'Microsoft.AspNetCore.Identity.UI'" />
  </ItemGroup>
</Target>

다음 단계

작성자: Rick Anderson

ASP.NET Core Identity:

  • UI(사용자 인터페이스) 로그인 기능을 지원하는 API입니다.
  • 사용자, 암호, 프로필 데이터, 역할, 클레임, 토큰, 메일 확인 등을 관리합니다.

사용자는 에 저장된 로그인 정보를 사용하여 계정을 Identity 만들거나 외부 로그인 공급자를 사용할 수 있습니다. 지원되는 외부 로그인 공급자에는 Facebook, Google, Microsoft 계정 및 Twitter가 포함됩니다.

모든 사용자의 인증을 전역으로 요구하는 방법에 대한 자세한 내용은 인증된 사용자 요구를 참조하세요.

Identity 소스 코드는 GitHub 사용할 수 있습니다. 스캐폴드 Identity 생성된 파일을 보고 와의 템플릿 상호 작용을 Identity 검토합니다.

Identity는 일반적으로 SQL Server 데이터베이스를 사용하여 사용자 이름, 암호 및 프로필 데이터를 저장하도록 구성됩니다. 또는 다른 영구 저장소(예: Azure Table Storage)를 사용할 수 있습니다.

이 항목에서는 를 사용하여 Identity 사용자를 등록, 로그인 및 로그아웃하는 방법을 알아봅니다. 참고: 템플릿은 사용자 이름과 전자 메일을 사용자에 대해 동일하게 처리합니다. 를 사용하는 앱을 만드는 방법에 대한 자세한 지침은 Identity 다음 단계를 참조하세요.

Microsoft ID 플랫폼 다음과 같습니다.

  • Azure AD(Azure Active Directory) 개발자 플랫폼의 진화
  • 에 관련이 없는 ASP.NET Core Identity 경우

ASP.NET Core Identity는 ASP.NET Core 웹앱에 UI(사용자 인터페이스) 로그인 기능을 추가합니다. 웹 API 및 SPA를 보호하려면 다음 중 하나를 사용합니다.

IdentityServer4는 ASP.NET Core용 OpenID Connect 및 OAuth 2.0 프레임워크입니다. IdentityServer4에서는 다음과 같은 보안 기능을 사용할 수 있습니다.

  • AaaS(Authentication as a Service)
  • 여러 응용 프로그램 유형에 대한 SSO(Single Sign-On/Off)
  • API에 대한 액세스 제어
  • 페더레이션 게이트웨이

자세한 내용은 IdentityServer4 시작을 참조하세요.

샘플 코드(다운로드 방법)를 보거나 다운로드합니다.

인증을 사용하여 웹앱 만들기

개별 사용자 계정으로 ASP.NET Core 웹 애플리케이션 프로젝트를 만듭니다.

  • 파일 > 새로 > 만들기 Project 선택합니다.
  • 새 ASP.NET Core 웹 애플리케이션 을 선택합니다. 프로젝트 이름을 WebApp1로 지정하여 프로젝트 다운로드와 동일한 네임스페이스를 갖습니다. 확인 을 클릭합니다.
  • ASP.NET Core 웹 애플리케이션을 선택한 다음, 인증 변경을 선택합니다.
  • 개별 사용자 계정을 선택하고 확인을 클릭합니다.

생성된 프로젝트는 ASP.NET Core Identity Razor 를 클래스 라이브러리로 제공합니다. Identity Razor 클래스 라이브러리는 영역이 있는 엔드포인트를 Identity 노출합니다. 예:

  • /Identity/Account/Login
  • /Identity/Account/Logout
  • /Identity/Account/Manage

마이그레이션 적용

마이그레이션을 적용하여 데이터베이스를 초기화합니다.

PMC(패키지 관리자 콘솔)에서 다음 명령을 실행합니다.

PM> Update-Database

테스트 등록 및 로그인

앱을 실행하고 사용자를 등록합니다. 화면 크기에 따라 탐색 토글 단추를 선택하여 등록로그인 링크를 확인해야 할 수 있습니다.

데이터베이스 보기 Identity

  • 보기 메뉴에서 SQL Server 개체 탐색기 (SSOX)를 선택 합니다.
  • (Localdb) MSSQLLocalDB (SQL Server 13) 로 이동 합니다. Dbo를 마우스 오른쪽 단추로 클릭 합니다. AspNetUsers > 뷰 데이터:

SQL Server 개체 탐색기의 AspNetUsers 테이블에 대 한 상황에 맞는 메뉴

서비스 구성 Identity

서비스는 에 ConfigureServices 추가됩니다. 일반적인 패턴은 모든 Add{Service} 메서드를 호출한 후 모든 services.Configure{Service} 메서드를 호출하는 것입니다.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
     // options.UseSqlite(
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();

    services.Configure<IdentityOptions>(options =>
    {
        // Password settings.
        options.Password.RequireDigit = true;
        options.Password.RequireLowercase = true;
        options.Password.RequireNonAlphanumeric = true;
        options.Password.RequireUppercase = true;
        options.Password.RequiredLength = 6;
        options.Password.RequiredUniqueChars = 1;

        // Lockout settings.
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
        options.Lockout.MaxFailedAccessAttempts = 5;
        options.Lockout.AllowedForNewUsers = true;

        // User settings.
        options.User.AllowedUserNameCharacters =
        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
        options.User.RequireUniqueEmail = false;
    });

    services.ConfigureApplicationCookie(options =>
    {
        // Cookie settings
        options.Cookie.HttpOnly = true;
        options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

        options.LoginPath = "/Identity/Account/Login";
        options.AccessDeniedPath = "/Identity/Account/AccessDenied";
        options.SlidingExpiration = true;
    });
}

위의 강조 표시된 코드는 기본 옵션 값으로 를 Identity 구성합니다. 서비스는 종속성 주입을 통해 앱에서 사용할 수 있습니다.

Identity 를 호출하여 를 사용할 수 UseAuthentication 있습니다. UseAuthentication 는 인증 미들웨어를 요청 파이프라인에 추가합니다.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        // options.UseSqlite(
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDatabaseDeveloperPageExceptionFilter();
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();

    services.Configure<IdentityOptions>(options =>
    {
        // Password settings.
        options.Password.RequireDigit = true;
        options.Password.RequireLowercase = true;
        options.Password.RequireNonAlphanumeric = true;
        options.Password.RequireUppercase = true;
        options.Password.RequiredLength = 6;
        options.Password.RequiredUniqueChars = 1;

        // Lockout settings.
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
        options.Lockout.MaxFailedAccessAttempts = 5;
        options.Lockout.AllowedForNewUsers = true;

        // User settings.
        options.User.AllowedUserNameCharacters =
        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
        options.User.RequireUniqueEmail = false;
    });

    services.ConfigureApplicationCookie(options =>
    {
        // Cookie settings
        options.Cookie.HttpOnly = true;
        options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

        options.LoginPath = "/Identity/Account/Login";
        options.AccessDeniedPath = "/Identity/Account/AccessDenied";
        options.SlidingExpiration = true;
    });
}

위의 코드는 Identity 를 기본 옵션 값으로 구성합니다. 서비스는 종속성 주입을 통해 앱에서 사용할 수 있습니다.

Identity 은 UseAuthentication 을호출하여 활성화됩니다. UseAuthentication 는 인증 미들웨어를 요청 파이프라인에 추가합니다.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseMigrationsEndPoint();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

템플릿에서 생성된 앱은 권한 부여를 사용하지 않습니다. app.UseAuthorization 는 앱이 권한 부여를 추가하는 경우 올바른 순서로 추가되도록 하기 위해 포함됩니다. UseRouting, UseAuthentication UseAuthorization , 및 는 이전 UseEndpoints 코드에 표시된 순서대로 호출되어야 합니다.

및 에 대한 자세한 내용은 IdentityOptions Startup 및 애플리케이션 시작을 IdentityOptions 참조하세요.

Register, Login, LogOut 및 RegisterConfirmation 스캐폴드

, Register Login , LogOutRegisterConfirmation 파일을 추가합니다. 권한 부여 지침이 있는 Razor 프로젝트에 ID 스캐폴드를 따라 이 섹션에 표시된 코드를 생성합니다.

레지스터 검사

사용자가 페이지에서 등록 단추를 클릭하면 Register 작업이 RegisterModel.OnPostAsync 호출됩니다. 사용자는 개체에 대해 CreateAsync에 의해 만들어집니다. _userManager

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
                                          .ToList();
    if (ModelState.IsValid)
    {
        var user = new IdentityUser { UserName = Input.Email, Email = Input.Email };
        var result = await _userManager.CreateAsync(user, Input.Password);
        if (result.Succeeded)
        {
            _logger.LogInformation("User created a new account with password.");

            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = user.Id, code = code },
                protocol: Request.Scheme);

            await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

            if (_userManager.Options.SignIn.RequireConfirmedAccount)
            {
                return RedirectToPage("RegisterConfirmation", 
                                      new { email = Input.Email });
            }
            else
            {
                await _signInManager.SignInAsync(user, isPersistent: false);
                return LocalRedirect(returnUrl);
            }
        }
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

기본 계정 확인 사용 안 함

기본 템플릿을 사용 하면 사용자가 Account.RegisterConfirmation 계정 확인을 위해 링크를 선택할 수 있는 위치로 리디렉션됩니다. 기본값은 Account.RegisterConfirmation 테스트에 사용 되며 자동 계정 확인은 프로덕션 앱에서 사용 하지 않도록 설정 해야 합니다.

확인 된 계정을 요구 하 고 등록 시 즉각적인 로그인을 방지 하려면 DisplayConfirmAccountLink = false /Areas/ Identity /Pages/Account/RegisterConfirmation.cshtml.cs 에서를 설정 합니다.

[AllowAnonymous]
public class RegisterConfirmationModel : PageModel
{
    private readonly UserManager<IdentityUser> _userManager;
    private readonly IEmailSender _sender;

    public RegisterConfirmationModel(UserManager<IdentityUser> userManager, IEmailSender sender)
    {
        _userManager = userManager;
        _sender = sender;
    }

    public string Email { get; set; }

    public bool DisplayConfirmAccountLink { get; set; }

    public string EmailConfirmationUrl { get; set; }

    public async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
    {
        if (email == null)
        {
            return RedirectToPage("/Index");
        }

        var user = await _userManager.FindByEmailAsync(email);
        if (user == null)
        {
            return NotFound($"Unable to load user with email '{email}'.");
        }

        Email = email;
        // Once you add a real email sender, you should remove this code that lets you confirm the account
        DisplayConfirmAccountLink = false;
        if (DisplayConfirmAccountLink)
        {
            var userId = await _userManager.GetUserIdAsync(user);
            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            EmailConfirmationUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                protocol: Request.Scheme);
        }

        return Page();
    }
}

로그인

로그인 양식은 다음과 같은 경우에 표시됩니다.

  • 로그인 링크가 선택되어 있습니다.
  • 사용자가 액세스 권한이 없거나 시스템에서 인증되지 않은 제한된 페이지에 액세스하려고 합니다.

로그인 페이지의 양식이 제출되면 OnPostAsync 작업이 호출됩니다. PasswordSignInAsync``_signInManager는 개체에서 호출됩니다.

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");

    if (ModelState.IsValid)
    {
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, 
        // set lockoutOnFailure: true
        var result = await _signInManager.PasswordSignInAsync(Input.Email,
                           Input.Password, Input.RememberMe, lockoutOnFailure: true);
        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            return LocalRedirect(returnUrl);
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToPage("./LoginWith2fa", new
            {
                ReturnUrl = returnUrl,
                RememberMe = Input.RememberMe
            });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning("User account locked out.");
            return RedirectToPage("./Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

권한 부여 결정을 내리는 방법에 대한 자세한 내용은 을 ASP.NET Core 권한 부여 소개 참조하세요.

로그아웃

로그아웃 링크는 LogoutModel.OnPost 작업을 호출합니다.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace WebApp1.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class LogoutModel : PageModel
    {
        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly ILogger<LogoutModel> _logger;

        public LogoutModel(SignInManager<IdentityUser> signInManager, ILogger<LogoutModel> logger)
        {
            _signInManager = signInManager;
            _logger = logger;
        }

        public void OnGet()
        {
        }

        public async Task<IActionResult> OnPost(string returnUrl = null)
        {
            await _signInManager.SignOutAsync();
            _logger.LogInformation("User logged out.");
            if (returnUrl != null)
            {
                return LocalRedirect(returnUrl);
            }
            else
            {
                return RedirectToPage();
            }
        }
    }
}

앞의 코드에서 코드는 return RedirectToPage(); 브라우저가 새 요청을 수행하고 사용자의 ID가 업데이트되도록 리디렉션이어야 합니다.

SignOutAsync는 에 저장된 사용자의 클레임을 cookie 지웁니다.

Post는 Pages/Shared/_LoginPartial.cshtml 에 지정됩니다.

@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
    <li class="nav-item">
        <a  class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" 
                                              title="Manage">Hello @User.Identity.Name!</a>
    </li>
    <li class="nav-item">
        <form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" 
                                  asp-route-returnUrl="@Url.Page("/", new { area = "" })" 
                                  method="post" >
            <button  type="submit" class="nav-link btn btn-link text-dark">Logout</button>
        </form>
    </li>
}
else
{
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li>
}
</ul>

테스트 Identity

기본 웹 프로젝트 템플릿은 홈 페이지에 대한 익명 액세스를 허용합니다. 를 테스트하려면 Identity 를 [Authorize] 추가합니다.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace WebApp1.Pages
{
    [Authorize]
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

로그인한 경우 로그아웃합니다. 앱을 실행하고 Privacy 링크를 선택합니다. 로그인 페이지로 리디렉션됩니다.

탐험 Identity

자세히 살펴보려면 Identity 다음을 수행합니다.

Identity 구성 요소

모든 Identity 종속 NuGet 패키지는 ASP.NET Core 공유 프레임워크에 포함됩니다.

의 기본 Identity 패키지는 Microsoft.AspNetCore. 입니다. Identity 이 패키지는 에 대한 핵심 인터페이스 집합을 ASP.NET Core Identity 포함하며 에 포함되어 Microsoft.AspNetCore.Identity.EntityFrameworkCore 있습니다.

다음으로 마이그레이션 ASP.NET Core Identity

기존 저장소 마이그레이션에 대한 자세한 내용 및 Identity 지침은 인증 및 Identity 마이그레이션을 참조하세요.

암호 강도 설정

최소 암호 요구 사항을 설정하는 샘플은 구성을 참조하세요.

AddDefault Identity 및 추가Identity

AddDefaultIdentity는 ASP.NET Core 2.1에서 도입되었습니다. 를 AddDefaultIdentity 호출하는 것은 다음을 호출하는 것과 비슷합니다.

자세한 내용은 AddDefault Identity 원본을 참조하세요.

정적 자산 게시 방지 Identity

정적 Identity 자산(UI용 스타일시트 및 Identity JavaScript 파일)을 웹 루트에 게시하지 않도록 하려면 다음 ResolveStaticWebAssetsInputsDependsOn 속성 및 RemoveIdentityAssets 대상을 앱의 프로젝트 파일에 추가합니다.

<PropertyGroup>
  <ResolveStaticWebAssetsInputsDependsOn>RemoveIdentityAssets</ResolveStaticWebAssetsInputsDependsOn>
</PropertyGroup>

<Target Name="RemoveIdentityAssets">
  <ItemGroup>
    <StaticWebAsset Remove="@(StaticWebAsset)" Condition="%(SourceId) == 'Microsoft.AspNetCore.Identity.UI'" />
  </ItemGroup>
</Target>

다음 단계