forked from AcaMate/AcaMate_API
[✨] JWT 생성 + 확인 로직 구현 중 & 회원가입, 로그아웃 API 구현 중
This commit is contained in:
parent
55f40e56cf
commit
f1a901820f
|
@ -72,7 +72,9 @@ builder.Services.AddAuthentication(options =>
|
|||
// JWT 설정부 끝
|
||||
|
||||
builder.Services.AddControllers();
|
||||
|
||||
// 여기다가 API 있는 컨트롤러들 AddScoped 하면 되는건가?
|
||||
builder.Services.AddScoped<AcaMate.Common.Token.JwtTokenService>();
|
||||
// builder.Services.AddScoped<UserService>(); //
|
||||
// builder.Services.AddScoped<UserController>();
|
||||
|
||||
|
|
|
@ -6,6 +6,9 @@ using System.Collections.Generic;
|
|||
using AcaMate.Common.Models;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
|
||||
using System.Security.Cryptography;
|
||||
|
||||
|
@ -30,8 +33,9 @@ public class JwtTokenService
|
|||
// Jti 는 토큰 식별자로 토큰의 고유 ID 이다.
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
||||
// jwt 토큰이 가지는 권한
|
||||
new Claim(ClaimTypes.Role, role)
|
||||
new Claim(ClaimTypes.Role, role),
|
||||
// 추가 클레임 예: new Claim(ClaimTypes.Role, "Admin")
|
||||
new Claim(ClaimTypes.NameIdentifier, uid)
|
||||
};
|
||||
|
||||
// 2. 비밀 키와 SigningCredentials 생성
|
||||
|
@ -43,7 +47,7 @@ public class JwtTokenService
|
|||
issuer: _jwtSettings.Issuer,
|
||||
audience: _jwtSettings.Audience,
|
||||
claims: claims,
|
||||
expires: DateTime.UtcNow.AddMinutes(_jwtSettings.ExpiryMinutes),
|
||||
expires: DateTime.Now.AddMinutes(_jwtSettings.ExpiryMinutes),
|
||||
signingCredentials: credentials
|
||||
);
|
||||
|
||||
|
@ -63,10 +67,46 @@ public class JwtTokenService
|
|||
return new RefreshToken()
|
||||
{
|
||||
uid = uid,
|
||||
token = Convert.ToBase64String(randomNumber),
|
||||
refresh_token = Convert.ToBase64String(randomNumber),
|
||||
create_Date = DateTime.UtcNow,
|
||||
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 RefreshTokenExpiryDays { get; set; }
|
||||
}
|
||||
[Table(("refresh_token"))]
|
||||
|
||||
|
||||
[Table("refresh_token")]
|
||||
public class RefreshToken
|
||||
{
|
||||
[Key]
|
||||
[Required(ErrorMessage = "필수 항목 누락")]
|
||||
public string uid { get; set; }
|
||||
public string token { get; set; }
|
||||
public string refresh_token { get; set; }
|
||||
public DateTime create_Date { get; set; }
|
||||
public DateTime expire_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.Models;
|
||||
using AcaMate.V1.Models;
|
||||
|
@ -9,6 +12,7 @@ using AcaMate.Common.Token;
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.EntityFrameworkCore.Query;
|
||||
using MySqlConnector;
|
||||
using System.Linq;
|
||||
|
||||
namespace AcaMate.V1.Controllers;
|
||||
|
||||
|
@ -235,8 +239,49 @@ public class UserController : ControllerBase
|
|||
}
|
||||
|
||||
// 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
|
||||
{
|
||||
|
@ -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;}
|
||||
}
|
||||
|
||||
[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")]
|
||||
public class Location
|
||||
|
|
Loading…
Reference in New Issue
Block a user