지난번에는 회원가입을 만들었다.
이번엔 로그인을 구현해보자.
구체적인 로그인 과정

- 클라이언트가 인증서버로 nickname, password를 보낸다.
- 인증서버는 postgres에서 nickname에 해당하는 hashed_pw와 user_id를 SELECT한다.
- BCrypt::validatePassword 함수와 hashed_pw를 사용해 클라가 보낸 pw가 유효한지 확인한다.
- 유효하지 않다면 해당 nickname의 로그인 실패 횟수를 redis에 만료시간 10분으로 업데이트하고, 5회가 넘어가면 차단한다.
- 유효하다면 user_id, nickname을 jwt에 담고, 게임서버와 공유하는 secret_key로 서명한다.
- 클라에게 success와 jwt를 보낸다.
- 클라가 게임서버로 jwt를 보내고, 게임서버는 그것을 인증서버와 공유하는 secret_key를 사용해 유효성 검사를 한다.
여기서 7번은 클라-게임서버 에서 구현할 부분이니 6번까지만 구현해보았다.
void Handle_C_LOGIN(const PacketSessionRef& session, const Protocol::C_LOGIN& pkt)
{
Protocol::S_LOGIN response;
std::string nickname = pkt.nickname();
std::string password = pkt.password();
std::string q1_password = "SELECT user_id, password FROM auth.users WHERE nickname = '" + nickname + "'";
PGConnection* pg = GDBConnectionPool->PopPG();
PGresult* pgResult = pg->ExecuteSQL(q1_password);
GDBConnectionPool->Push(pg);
if (PQntuples(pgResult) != 1)
{//존재하지 않는 nickname
PQclear(pgResult);
response.set_success(false);
session->Send(ClientPacketHandler::MakeWriteBuffer(response));
return;
}
std::string a_user_id = PQgetvalue(pgResult, 0, 0);
std::string a_password = PQgetvalue(pgResult, 0, 1);
PQclear(pgResult);
if (BCrypt::validatePassword(password, a_password))
{//로그인 성공
std::string token = CreateAccessToken(a_user_id, nickname);
response.set_success(true);
response.set_token(token);
std::cout << "token : " << token << std::endl;
session->Send(ClientPacketHandler::MakeWriteBuffer(response));
}
else
{//비밀번호 틀림
std::string query = "INCR " + nickname + ":fail_count EX 600";
RedisConnection* redis = GDBConnectionPool->PopRedis();
redisReply* reply = redis->Execute(query);
int fail_count = reply->integer;
freeReplyObject(reply);
GDBConnectionPool->Push(redis);
if (fail_count >= 5)
{
//TODO 차단
}
response.set_success(false);
session->Send(ClientPacketHandler::MakeWriteBuffer(response));
}
}
CreateAccessToken
jwt-cpp를 사용하려면 깃허브에서 받아야한다.
https://github.com/Thalhammer/jwt-cpp
GitHub - Thalhammer/jwt-cpp: A header only library for creating and validating json web tokens in c++
A header only library for creating and validating json web tokens in c++ - Thalhammer/jwt-cpp
github.com
git clone https://github.com/Thalhammer/jwt-cpp.git
jwt-cpp는 헤더만으로 동작하기때문에 헤더만 include해주면 된다.
Makefile에서 include항목에 -Ijwt-cpp/include 를 추가해주자
INC = -I$(SERVER_CORE_DIR) -Ijwt-cpp/include
#include <jwt-cpp/jwt.h>
std::string CreateAccessToken(const std::string& user_id, const std::string& nickname)
{
const std::string SECRET_KEY = std::getenv("JWT_SECRET_KEY");
auto token = jwt::create()
.set_issuer("auth_server") // 발급자
.set_type("JWT")
.set_payload_claim("user_id", jwt::claim(user_id))
.set_payload_claim("nickname", jwt::claim(nickname))
.set_issued_at(std::chrono::system_clock::now())
.set_expires_at(std::chrono::system_clock::now()
+ std::chrono::hours(1)) // 1시간
.sign(jwt::algorithm::hs256{SECRET_KEY}); // 비밀키로 서명
return token;
}
더미클라이언트
더미클라이언트에서 로그인을 성공해서 토큰을 받은 뒤에, secret_key를 사용해 유효성을 검사해봤다.
void Handle_S_LOGIN(const PacketSessionRef& session, const Protocol::S_LOGIN& pkt)
{
bool success = pkt.success();
if (success)
{
std::string token = pkt.token();
std::string user_id;
if (VerifyAccessToken(token, user_id))
{
std::cout << "login suceess! user : " << user_id << std::endl;
}
}
else
{
std::cout << "login fail!" << std::endl;
session->Disconnect();
}
}
bool VerifyAccessToken(const std::string& token, std::string& out_user_id)
{
const std::string SECRET_KEY = std::getenv("JWT_SECRET_KEY");
try
{
auto verifier = jwt::verify()
.allow_algorithm(jwt::algorithm::hs256{SECRET_KEY})
.with_issuer("auth_server"); // 발급자 확인
auto decoded = jwt::decode(token);
verifier.verify(decoded); // 서명 + 만료시간 자동 검증
out_user_id = decoded.get_payload_claim("user_id").as_string();
return true;
}
catch (const std::exception& e)
{
std::cout << "exeption" << std::endl;
// 서명 불일치, 만료, 형식 오류 전부 여기로 떨어짐
return false;
}
}

회원가입 부터 로그인까지 다 잘 되는 모습이다.
'프로젝트 > Project_Island' 카테고리의 다른 글
| 21. EmailAPI - SMTP (0) | 2026.03.13 |
|---|---|
| 20. DBConnection 리팩토링 (0) | 2026.03.11 |
| 18. 회원가입 (0) | 2026.03.10 |
| 17. Session, EpollEvent 포인터 참조 문제 해결 (0) | 2026.03.09 |
| 16. RedisConnection (0) | 2026.03.08 |