재우니의 블로그

 

추가 ASP.NET Core 3.1 에서 별도의 claims 를 추가하고자 아래 블로그를 토대로 구현하고자 했습니다.

 

 

https://levelup.gitconnected.com/add-extra-user-claims-in-asp-net-core-web-applications-1f28c98c9ec6

 

Add extra user claims in ASP.NET Core web applications

The ASP.NET Core documentation explains thoroughly what a claim is and how to check for a claim.  However, there is no information about…

levelup.gitconnected.com

 

사용자 정보를 관장하는 IdentityUser 클래스에 별도로 만든 필드 즉, 이름이나 생일 등등을 확장하고자 할 경우, 이를 상속받아 확장시켜야합니다. 아래에 4개 정도의 속성을 추가했는데요. 사용자 코드, 이름, 사용자특성, 활성화 여부 정도를 확장시킬 예정입니다. 당연히 database 의 테이블에도 아래 4개 필드가 존재합니다.

 

using System.Collections.Generic;
using System.Security.Claims;
using AspNetCore.Identity.Dapper.Stores;
using Microsoft.AspNetCore.Identity;

namespace AspNetCore.Identity.Dapper.Models
{
    public class ApplicationUser : IdentityUser
    {
        [PersonalData]
        public long UserID { get; set; }
        [PersonalData]
        public string Name { get; set; }
        [PersonalData]
        public int UserType { get; set; }
        [PersonalData]
        public bool IsActive { get; set; } = true;

        internal List<Claim> Claims { get; set; }
        internal List<UserRole> Roles { get; set; }
        internal List<UserLoginInfo> Logins { get; set; }
        internal List<UserToken> Tokens { get; set; }
    }
}

 

 

위의 model 을 기반으로 이제 claim 을 커스터 마이징해야겠는데요. 위의 블로그 내용을 최대한 참조했는데, GenerateClaimsAsync() 함수를 override 할 경우 startup.cs 에서 해당 메소드를 호출되지 않아, 저는 아래 msdn 을 참고하여 CreateAsync() 함수를 사용했습니다.

 

https://docs.microsoft.com/en-us/aspnet/core/security/authentication/add-user-data?view=aspnetcore-3.1&tabs=visual-studio#add-claims-to-identity-using-iuserclaimsprincipalfactory

 

Add, download, and delete user data to Identity in an ASP.NET Core project

Learn how to add custom user data to Identity in an ASP.NET Core project. Delete data per GDPR.

docs.microsoft.com

 

 

using AspNetCore.Identity.Dapper.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;

namespace IdentityCoreDapper.Factory
{
    public class AppClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser>
	{
		public AppClaimsPrincipalFactory(
		UserManager<ApplicationUser> userManager,
		IOptions<IdentityOptions> optionsAccessor)
		: base(userManager, optionsAccessor)
		{ }

		public async override Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
		{
			var principal = await base.CreateAsync(user);
			var identity = (ClaimsIdentity)principal.Identity;

			var claims = new List<Claim>();
            
            //추가한 클래임이며, 키값은 UserID 입니다.
			claims.Add(new Claim("UserID", user.UserID.ToString()));

			identity.AddClaims(claims);
			return principal;
		}
	}
}

 

이제 위의 factory 클래스를 statup.cs 의 confConfigureServices() 에서 dependency injection container 로 등록해야 합니다.  이 부분도 블로그처럼 services.AddIdentity() 에 추가하지 않고, AddScoped로 DI 설정을 하였습니다.(코드제일 아래 2번째)

 

services.AddScoped<IUserClaimsPrincipalFactory, AppClaimsPrincipalFactory>();

 

public void ConfigureServices(IServiceCollection services)
{
    // Add identity types
    services.AddIdentity<ApplicationUser, ApplicationRole>()                        
        .AddDefaultTokenProviders();

    services.AddIdentityCore<ApplicationUser>(options =>
    {
        options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
        options.User.RequireUniqueEmail = true;

        options.Password.RequireDigit = true;
        options.Password.RequiredLength = 8;
        options.Password.RequireLowercase = true;
        options.Password.RequireNonAlphanumeric = false;
        options.Password.RequireUppercase = true;
    })
    .AddRoles<ApplicationRole>()
    .AddDefaultTokenProviders();

    
    services.AddTransient<IUserStore<ApplicationUser>, UserStore>();
    services.AddTransient<IRoleStore<ApplicationRole>, RoleStore>();

    //추가한 부분
    services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, AppClaimsPrincipalFactory>();

    services.AddControllersWithViews();
}

 

이제 view 를 통해서 설정한 claim 을 가져오도록 하죠. claim 은 인증해야 얻을 수 있습니다.

가져오는 방법은  @(User.FindFirst("UserID").Value) 처럼 claim 의 키값을 지정하면 바인딩 됩니다.

 

@using Microsoft.AspNetCore.Identity
@using AspNetCore.Identity.Dapper.Models
@using System.Security.Claims
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager

<ul class="navbar-nav">
    @if (SignInManager.IsSignedIn(User))
    {
        <li class="nav-item">
            <a class="nav-link text-dark" asp-controller="Manage" asp-action="Index" title="Manage">
                Hello @User.Identity.Name!
                @(User.FindFirst("UserID").Value)
            </a>
        </li>
        <li class="nav-item">
            <form class="form-inline" asp-controller="Account" asp-action="Logout" asp-route-returnUrl="@Url.Action("Index", "Home", new { area = "" })">
                <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-controller="Account" asp-action="Register">Register</a>
        </li>
        <li class="nav-item">
            <a class="nav-link text-dark" asp-controller="Account" asp-action="Login">Login</a>
        </li>
    }
</ul>