1/25 수정글
GitHub - kr191201/asp.net-core8
깃헙 생성하여 프로젝트 업로드 해 두었습니다.
LoginController.cs 위주로 봐주시면 됩니다.
이슈 페이지에 내용을 정리해 두었습니다.
감사합니다!
읽어주셔서 감사합니다.
혹시 읽어보시고 추가 내용이 더 필요하다 싶으시면 댓글로 알려주세요.
제가 asp.net core8을 공부한지 2개월 정도 된 것 같은데요
백지인 상태에서 jwt로그인을 구현하려다보니 막히네요.
계속 이것저것 AI한테 물어가며 수정은 해봤는데… 도저히 진도가 안 나가는 상태입니다.
도움주시면 정말 감사하겠습니다…
asp.net core8 mv
c에서 jwt로 로그인을 구현하고 있는데
막혀서 물어볼 곳이 없어서 이곳에 질문글을 작성해 보는데요.
적는다고 적었지만 빠졌거나 설명이 이상한 부분이 있다면 말씀 부탁드립니다.
현재 상황은요
로그인 버튼을 누르면 토큰값을 로컬 스토리지에 저장하고 있습니다.
그후에 사용자 정보를 불러오는 부분을 곧바로 실행하는데요
클라이언트에서 fetch로 /Login/GetUserInfo를 헤더-토큰값을 보낸뒤라서 그런지 해당 액션메서드에서는 if (User.Identity.IsAuthenticated) { 값이 true로 잘 찍히고 있습니다.
다만 로그인후에 메인페이지나 다른 페이지를 수동으로 url입력해서 실행해 보면 공통파일인 /_Nav.cshtml에서는 if (User.Identity.IsAuthenticated) { 부분이 false로 찍히고 있습니다.
제가 알고 있기로는 JWT토큰 설정을 program.cs에서 하면 검증을 통해서 User객체에 데이터가 만들어지고 이걸 기반으로 사용하면 되는 것으로 이해했거든요
근데 막상 /_Nav.cshtml에서 사용하려니 false라서 이유를 못찾겠더라고요…
Program.cs
using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.Text;
using Microsoft.IdentityModel.Tokens;
var builder = WebApplication.CreateBuilder(args);
IConfiguration configuration = builder.Configuration;
/*
- JWT 기반 인증 설정
- ASP.NET Core 애플리케이션에서 JWT(JSON Web Token) 인증을 설정하는 역할을 합니다.
- 그리고 TokenValidationParameters 속성을 통해
- 전달되는 값들은 JWT 토큰의 유효성을 검증하는 데 사용되는 중요한 정보들을 담고 있습니다.
*/
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
// 추출된 토큰에 대해 아래에서 설정된 항목들에 따라 유효성 검증을 수행합니다.
// 검증 성공 시 HttpContext.User에 사용자 정보가 설정되고, 이후 코드에서 User.Identity.IsAuthenticated를 통해 사용자 인증 여부를 확인할 수 있습니다.
// 검증 실패 시 검증에 실패하면 인증 실패 처리를 수행합니다.
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true, // 토큰을 발급한 서버(issuer)가 올바른지 확인합니다. 즉, 설정된 ValidIssuer 값과 일치하는지 비교합니다.
ValidateAudience = true, // 토큰이 발급된 대상(audience)이 올바른지 확인합니다. 즉, 설정된 ValidAudience 값과 일치하는지 비교합니다.
ValidateLifetime = true, // 토큰의 유효 기간을 검사합니다. 발급된 시간과 만료 시간을 비교하여 현재 시간이 유효 기간 내에 있는지 확인합니다.
ValidateIssuerSigningKey = true, // 토큰 서명의 유효성을 검사합니다. 즉, 설정된 IssuerSigningKey를 사용하여 토큰 서명을 검증합니다.
// 아래 3가지
// 각각 토큰 발급 서버, 토큰 사용 대상, 토큰 서명에 사용된 비밀키를 나타내는 값입니다. 이 값들은 JWT 토큰 생성 시에도 사용되어 토큰에 포함됩니다.
ValidIssuer = configuration["Jwt:Issuer"],
ValidAudience = configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt:SecretKey"]))
};
})
//.AddCookie(options =>
//{
// options.Cookie.SameSite = SameSiteMode.Strict; // SameSite 속성 설정
// options.Cookie.SecurePolicy = CookieSecurePolicy.Always; // HTTPS에서만 쿠키 전송
//})
;
builder.Services.AddControllersWithViews();
builder.Services.AddAuthorization();
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
// 요청 처리 파이프라인 설정
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler(“/Home/Error”);
app.UseHsts();
} else
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication(); // 첫번째 호출, 인증이 먼저 이루어진 후에
app.UseAuthorization(); // 두번째 호출, 권한 부여가 진행되어야 함
app.MapControllers();
app.MapRazorPages();
app.UseEndpoints(endpoints => // 4 추가
{
endpoints.MapControllers();
});
app.MapControllerRoute(
name: “default”,
pattern: “{controller=Login}/{action=Index}/{id?}”);
app.Run();
로 설정해 둔 상태입니다.
- 로그인 버튼 클릭시 실행되는 자바스크립트 함수(클라이언트단)
async function fn_loginProc() {
var USEREMAIL = ("#USEREMAIL").val();
var USERPASSWORD = (“#USERPASSWORD”).val();
const response = await fetch('https://localhost:7123/Login/proc', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ USEREMAIL: USEREMAIL, USERPASSWORD: USERPASSWORD })
});
if (response.ok) {
const data = await response.json();
localStorage.setItem('jwt', data.token); // JWT 토큰 저장
fn_GetUserInfo();
} else {
console.error('Login failed');
}
}
- 로그인 실행 로직(서버단)
[HttpPost]
public IActionResult proc([FromBody] LoginModel model)
{
try
{
_connection.Open();
var parameters = new DynamicParameters();
parameters.Add("@USEREMAIL", model.USEREMAIL, DbType.String, ParameterDirection.Input);
parameters.Add("@USERPASSWORD", model.USERPASSWORD, DbType.String, ParameterDirection.Input);
parameters.Add("@Result", dbType: DbType.Int32, direction: ParameterDirection.Output);
_connection.Execute("USP_LOGIN", parameters, commandType: CommandType.StoredProcedure); // 프로시저 실행
int result = parameters.Get<int>("@Result"); // OUTPUT 매개변수 값 읽기
if (result == 1)
{
var token = GenerateToken(model);
return Ok(new { Token = token });
}
else
{
return Unauthorized(new { Error = "Invalid email or password." }); // 인증 실패 시
}
}
catch (Exception ex)
{
// 예외 처리 (로그 기록, 사용자에게 에러 메시지 등)
return StatusCode(500, "An error occurred while processing your request.");
}
finally
{
if (_connection.State == ConnectionState.Open)
{
_connection.Close();
}
}
}
- 로그인 후 사용자 정보 불러오는 부분(클라이언트단)
function fn_GetUserInfo() {
const token = localStorage.getItem('jwt');
await fetch('https://localhost:7123/Login/GetUserInfo', {
method: 'GET',
headers: {
'Authorization': `Bearer ${localStorage.getItem('jwt')}`
}
})
.then(response => response.json())
.then(data => {
// 서버에서 받은 데이터 처리
console.log(data); // 받은 데이터 콘솔에 출력
})
.catch(error => {
// 에러 처리
console.error('Error:', error);
// 사용자에게 에러 메시지 표시
alert('데이터를 가져오는 중 오류가 발생했습니다.');
});
}
- 사용자 정보 불러오는 부분(서버단)
이곳에서는 if (User.Identity.IsAuthenticated) { 부분이 true로 떨어집니다
다만 다음 공통파일인 /_Nav.cshtml에서는 로그인상태인지 아닌지를 표시하는 부분에서 사용중인데 false로 떨어집니다
program.cs에서 설정된 부분이 자동 검증해줘서 User객체에 값을 넣어놓는다고 알고 있는데
그러면 모든 곳에서 if (User.Identity.IsAuthenticated) { 이렇게 사용할수가 있는걸로 이해하고 있거든요
근데 false로 떨어집니다.
[HttpGet]
public IActionResult GetUserInfo()
{
if (User.Identity.IsAuthenticated) {
// 1. 사용자 ID 추출
var userEmail = User.FindFirst(ClaimTypes.Email)?.Value;
var userName = User.Identity.Name;
_connection.Open();
var parameters = new DynamicParameters();
parameters.Add("@SEL_USER_EMAIL", userEmail);
var result = _connection.Query<TblUserList>("USP_USER_INFO", parameters, commandType: CommandType.StoredProcedure).ToList();
return Ok(new { IsAuthenticated = true, UserName = userName, UserEmail = userEmail }); // 인증 상태와 사용자 정보를 함께 반환
} else {
return Ok(new { IsAuthenticated = false }); // 인증되지 않은 경우에도 JSON 반환
}
//return View();
}