[✨] JWT 생성 + 확인 로직 구현 중 & 회원가입, 로그아웃 API 구현 중
This commit is contained in:
parent
55f40e56cf
commit
f1a901820f
|
@ -72,7 +72,9 @@ builder.Services.AddAuthentication(options =>
|
||||||
// JWT 설정부 끝
|
// JWT 설정부 끝
|
||||||
|
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
|
|
||||||
// 여기다가 API 있는 컨트롤러들 AddScoped 하면 되는건가?
|
// 여기다가 API 있는 컨트롤러들 AddScoped 하면 되는건가?
|
||||||
|
builder.Services.AddScoped<AcaMate.Common.Token.JwtTokenService>();
|
||||||
// builder.Services.AddScoped<UserService>(); //
|
// builder.Services.AddScoped<UserService>(); //
|
||||||
// builder.Services.AddScoped<UserController>();
|
// builder.Services.AddScoped<UserController>();
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,9 @@ using System.Collections.Generic;
|
||||||
using AcaMate.Common.Models;
|
using AcaMate.Common.Models;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
|
@ -30,8 +33,9 @@ public class JwtTokenService
|
||||||
// Jti 는 토큰 식별자로 토큰의 고유 ID 이다.
|
// Jti 는 토큰 식별자로 토큰의 고유 ID 이다.
|
||||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
||||||
// jwt 토큰이 가지는 권한
|
// jwt 토큰이 가지는 권한
|
||||||
new Claim(ClaimTypes.Role, role)
|
new Claim(ClaimTypes.Role, role),
|
||||||
// 추가 클레임 예: new Claim(ClaimTypes.Role, "Admin")
|
// 추가 클레임 예: new Claim(ClaimTypes.Role, "Admin")
|
||||||
|
new Claim(ClaimTypes.NameIdentifier, uid)
|
||||||
};
|
};
|
||||||
|
|
||||||
// 2. 비밀 키와 SigningCredentials 생성
|
// 2. 비밀 키와 SigningCredentials 생성
|
||||||
|
@ -43,7 +47,7 @@ public class JwtTokenService
|
||||||
issuer: _jwtSettings.Issuer,
|
issuer: _jwtSettings.Issuer,
|
||||||
audience: _jwtSettings.Audience,
|
audience: _jwtSettings.Audience,
|
||||||
claims: claims,
|
claims: claims,
|
||||||
expires: DateTime.UtcNow.AddMinutes(_jwtSettings.ExpiryMinutes),
|
expires: DateTime.Now.AddMinutes(_jwtSettings.ExpiryMinutes),
|
||||||
signingCredentials: credentials
|
signingCredentials: credentials
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -63,10 +67,46 @@ public class JwtTokenService
|
||||||
return new RefreshToken()
|
return new RefreshToken()
|
||||||
{
|
{
|
||||||
uid = uid,
|
uid = uid,
|
||||||
token = Convert.ToBase64String(randomNumber),
|
refresh_token = Convert.ToBase64String(randomNumber),
|
||||||
create_Date = DateTime.UtcNow,
|
create_Date = DateTime.UtcNow,
|
||||||
expire_date = DateTime.UtcNow.AddDays(_jwtSettings.RefreshTokenExpiryDays)
|
expire_date = DateTime.UtcNow.AddDays(_jwtSettings.RefreshTokenExpiryDays)
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ClaimsPrincipal ValidateToken(string token)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(token)) return null;
|
||||||
|
var tokenHandler = new JwtSecurityTokenHandler();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var key = Encoding.UTF8.GetBytes(_jwtSettings.SecretKey);
|
||||||
|
var validationParameters = new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
IssuerSigningKey = new SymmetricSecurityKey(key),
|
||||||
|
ValidateIssuer = true,
|
||||||
|
ValidIssuer = _jwtSettings.Issuer,
|
||||||
|
ValidateAudience = true,
|
||||||
|
ValidAudience = _jwtSettings.Audience,
|
||||||
|
ValidateLifetime = true,
|
||||||
|
ClockSkew = TimeSpan.FromMinutes(_jwtSettings.ClockSkewMinutes)
|
||||||
|
};
|
||||||
|
var principal = tokenHandler.ValidateToken(token, validationParameters, out var securityToken);
|
||||||
|
return principal;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"검증 실패 {e}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -12,16 +12,34 @@ public class JwtSettings
|
||||||
public int ClockSkewMinutes { get; set; }
|
public int ClockSkewMinutes { get; set; }
|
||||||
public int RefreshTokenExpiryDays { get; set; }
|
public int RefreshTokenExpiryDays { get; set; }
|
||||||
}
|
}
|
||||||
[Table(("refresh_token"))]
|
|
||||||
|
|
||||||
|
[Table("refresh_token")]
|
||||||
public class RefreshToken
|
public class RefreshToken
|
||||||
{
|
{
|
||||||
[Key]
|
[Key]
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
public string uid { get; set; }
|
public string uid { get; set; }
|
||||||
public string token { get; set; }
|
public string refresh_token { get; set; }
|
||||||
public DateTime create_Date { get; set; }
|
public DateTime create_Date { get; set; }
|
||||||
public DateTime expire_date { get; set; }
|
public DateTime expire_date { get; set; }
|
||||||
|
|
||||||
// 이건 로그아웃시에 폐기 시킬예정이니 그떄 변경하는걸로 합시다.
|
// 이건 로그아웃시에 폐기 시킬예정이니 그떄 변경하는걸로 합시다.
|
||||||
public DateTime? revoke_Date { get; set; }
|
public DateTime? revoke_Date { get; set; }
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
"""
|
||||||
|
토큰 동작 관련
|
||||||
|
다시 물어보자 토큰 로직 관련해서 일단은 로그인을 예로 들면
|
||||||
|
1. 로그인을 진행한다.
|
||||||
|
2. 회원이 DB에 정상적으로 존재한다.
|
||||||
|
3. 엑세스 토큰과 리프레시 토큰을 생성한다.
|
||||||
|
4. 엑세스 토큰은 클라이언트로 리프래시 토큰은 서버에 저장하고 클라이언트로도 보낸다.
|
||||||
|
5. (상황1) 시간이 경과해 엑세스 토큰의 시간이 경과했다.
|
||||||
|
6. (상황2) 회원 정보에 대한 접근이 필요한 동작을 수행한다.
|
||||||
|
7. 엑세스 토큰과 리프레시 토큰을 서버로 전송한다.
|
||||||
|
8. 엑세스 토큰이 만료가 되었음이 확인이 되면 리프레시 토큰으로 새 엑세스를 만들기 위해 리프레시 토큰을 확인한다.
|
||||||
|
9. 리프레시 토큰이 만료가 되지 않았다면 리프레시 토큰을 토대로 엑세스 토큰을 생성한다.
|
||||||
|
10. 생성된 엑세스 토큰을 가지고 상황2의 동작을 수행하고 엑세스 토큰을 반환한다.
|
||||||
|
"""
|
||||||
|
*/
|
|
@ -1,3 +1,6 @@
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using AcaMate.Common.Data;
|
using AcaMate.Common.Data;
|
||||||
using AcaMate.Common.Models;
|
using AcaMate.Common.Models;
|
||||||
using AcaMate.V1.Models;
|
using AcaMate.V1.Models;
|
||||||
|
@ -9,6 +12,7 @@ using AcaMate.Common.Token;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.EntityFrameworkCore.Query;
|
using Microsoft.EntityFrameworkCore.Query;
|
||||||
using MySqlConnector;
|
using MySqlConnector;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace AcaMate.V1.Controllers;
|
namespace AcaMate.V1.Controllers;
|
||||||
|
|
||||||
|
@ -235,8 +239,49 @@ public class UserController : ControllerBase
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: jwt 토큰 만들어서 여기서 보내는 작업을 해야 함
|
// TO-DO: jwt 토큰 만들어서 여기서 보내는 작업을 해야 함
|
||||||
|
var token = _jwtTokenService.GenerateJwtToken(uid, "admin");
|
||||||
|
var refreshToken = _jwtTokenService.GenerateRefreshToken(uid);
|
||||||
|
await SaveData<RefreshToken, string>(refreshToken, rt => rt.uid);
|
||||||
|
|
||||||
|
|
||||||
|
/* */
|
||||||
|
var principalToken = _jwtTokenService.ValidateToken(token);
|
||||||
|
if (principalToken != null)
|
||||||
|
{
|
||||||
|
var jti = principalToken.FindFirst(JwtRegisteredClaimNames.Jti)?.Value;
|
||||||
|
var sub = principalToken.FindFirst(JwtRegisteredClaimNames.Sub)?.Value;
|
||||||
|
var id = principalToken.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||||
|
var id2 = principalToken.FindFirst(JwtRegisteredClaimNames.Sub)?.Value;
|
||||||
|
// var check = principalToken.FindFirst("sub")?.Value;
|
||||||
|
_logger.LogInformation($"토큰? - {jti}");
|
||||||
|
_logger.LogInformation($"토큰? - {sub}");
|
||||||
|
_logger.LogInformation($"토큰? - {id}");
|
||||||
|
// TODO: 도대체 토큰 이거 sub확인이 왜 안되는걸까.?
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
_logger.LogInformation("dd");
|
||||||
|
}
|
||||||
|
/* */
|
||||||
|
|
||||||
return Ok($"회원가입 : {uid}");
|
var result = new APIResponseStatus<dynamic>()
|
||||||
|
{
|
||||||
|
status = new Status()
|
||||||
|
{
|
||||||
|
code = "000",
|
||||||
|
message = "정상"
|
||||||
|
},
|
||||||
|
data = new
|
||||||
|
{
|
||||||
|
uid = uid,
|
||||||
|
accessToken = token,
|
||||||
|
refreshToken = refreshToken.refresh_token
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(result.JsonToString());
|
||||||
}
|
}
|
||||||
private async Task<bool> SaveData<T, K> (T entity, Expression<Func<T, K>> key) where T : class
|
private async Task<bool> SaveData<T, K> (T entity, Expression<Func<T, K>> key) where T : class
|
||||||
{
|
{
|
||||||
|
@ -287,5 +332,26 @@ public class UserController : ControllerBase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("logout")]
|
||||||
|
[CustomOperation("로그아웃", "사용자 로그아웃", "사용자")]
|
||||||
|
public async Task<IActionResult> LogOut(string token, string refresh)//([FromBody] UserAll request)
|
||||||
|
{
|
||||||
|
var principalToken = _jwtTokenService.ValidateToken(token);
|
||||||
|
if (principalToken != null)
|
||||||
|
{
|
||||||
|
// var uid = principalToken.FindFirst(JwtRegisteredClaimNames.Jti)?.Value;
|
||||||
|
var uid = principalToken.FindFirst("sub")?.Value;
|
||||||
|
_logger.LogInformation($"토큰? - {uid}");
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
_logger.LogInformation("dd");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok("로그아웃");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -79,26 +79,6 @@ public class Permission
|
||||||
public bool market_email_yn {get; set;}
|
public bool market_email_yn {get; set;}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Table("token")]
|
|
||||||
public class Token
|
|
||||||
{
|
|
||||||
[Key]
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public string uid { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public string refresh_token { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public DateTime create_date { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public DateTime expires_date { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public DateTime revoke_date { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[Table("location")]
|
[Table("location")]
|
||||||
public class Location
|
public class Location
|
||||||
|
|
Loading…
Reference in New Issue
Block a user