.NET8과 SpringBoot 중 익숙한 .NET8 부터 구현
.NET API 프로젝트생성 및 의존성 설치
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools
Microsoft.AspNetCore.Identity.EntityFrameworkCore
Microsoft.AspNetCore.Authentication.JwtBearer
//ApplicationDbContext.cs
public class ApplicationDbContext : IdentityDbContext<IdentityUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
}
//Program.cs
ConfigurationManager configuration = builder.Configuration;
builder.Services.AddDbContext<ApplicationDbContext>(opt => opt.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));
//Identity 서비스 등록
builder.Services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
//identity 옵션 설정
builder.Services.Configure<IdentityOptions>(options =>
{
// Password settings.
options.Password.RequireDigit = true;
//소문자포함여부
options.Password.RequireLowercase = false;
//영문자 이외의 문자
options.Password.RequireNonAlphanumeric = false;
//대문자포함
options.Password.RequireUppercase = false;
//최소길이
options.Password.RequiredLength = 4;
//암호에 포함되야하는 최소 문자수
options.Password.RequiredUniqueChars = 0;
});
//패키지 관리자 콘솔(PMC)
Add-Migration InitialIdentityModel
Update-Database
// Code First 방식으로 테이블 생성 확인
//RegisterModel
public class RegisterModel
{
[Required(ErrorMessage = "회원명은 필수 입니다.")]
public string? username { get; set; }
[EmailAddress]
[Required(ErrorMessage = "이메일은 필수 입니다.")]
public string? email { get; set; }
[Required(ErrorMessage = "비밀번호는 필수 입니다.")]
public string? password { get; set; }
}
//AuthController
[HttpPost]
[Route("register")]
public async Task<IActionResult> Register([FromBody] RegisterModel model)
{
var userExists = await _userManager.FindByNameAsync(model.Username);
if (userExists != null)
return StatusCode(StatusCodes.Status500InternalServerError, new Response { Status = "Error", Message = "User already exists!" });
IdentityUser user = new()
{
Email = model.Email,
SecurityStamp = Guid.NewGuid().ToString(),
UserName = model.Username
};
var result = await _userManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
return StatusCode(StatusCodes.Status500InternalServerError, new Response { Status = "Error", Message = "User creation failed! Please check user details and try again." });
return Ok(new Response { Status = "Success", Message = "User created successfully!" });
}
//Program.cs
//기본인증 스키마 추가
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
// Adding Jwt Bearer
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters()
{
//발급자 유효성 확인
ValidateIssuer = true,
//사용자 유효성 확인
ValidateAudience = true,
//유효한 발급자 값
ValidAudience = configuration["JWT:ValidAudience"],
//유효한 사용자값
ValidIssuer = configuration["JWT:ValidIssuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["JWT:Secret"]))
};
});
...
app.UseAuthentication();
app.UseAuthorization();
//appsettings.json
"JWT": {
"ValidAudience": "https://localhost:7047",
"ValidIssuer": "https://localhost:7047",
"Secret": "abcdef123456789"
},
//LoginModel
public class LoginModel
{
[Required(ErrorMessage = "ID는 필수입니다.")]
public string? Username { get; set; }
[Required(ErrorMessage = "비밀번호는 필수입니다.")]
public string? Password { get; set; }
}
//AuthController
[HttpPost]
[Route("login")]
public async Task<IActionResult> Login([FromBody] LoginModel model)
{
var user = await _userManager.FindByNameAsync(model.Username);
if (user != null && await _userManager.CheckPasswordAsync(user, model.Password))
{
var userRoles = await _userManager.GetRolesAsync(user);
//유저 정보
var authClaims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
};
foreach (var userRole in userRoles)
{
authClaims.Add(new Claim(ClaimTypes.Role, userRole));
}
var token = GetToken(authClaims);
return Ok(new
{
token = new JwtSecurityTokenHandler().WriteToken(token),
expiration = token.ValidTo
});
}
return Unauthorized();
}
private JwtSecurityToken GetToken(List<Claim> authClaims)
{
var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Secret"]));
var token = new JwtSecurityToken(
issuer: _configuration["JWT:ValidIssuer"],
audience: _configuration["JWT:ValidAudience"],
expires: DateTime.Now.AddDays(1),
claims: authClaims,
signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)
);
return token;
}