JWT를 사용한 .NET8 회원가입, 로그인 구현

하하하·2024년 10월 30일
0

JWT

목록 보기
1/2

.NET8과 SpringBoot 중 익숙한 .NET8 부터 구현

.NET API 프로젝트생성 및 의존성 설치
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools
Microsoft.AspNetCore.Identity.EntityFrameworkCore
Microsoft.AspNetCore.Authentication.JwtBearer

  1. DB 환경구성

//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 방식으로 테이블 생성 확인
  1. 회원가입
//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!" });
    }
  1. 로그인

//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;
   }
  1. 실행결과
profile
하하하

0개의 댓글