Chap14-TokenVarifyAndUserManager
Chap14-TokenVarifyAndUserManager
流程梳理
先是简单的梳理一下登陆的流程

目前为止,一直到验证token有效性已经完成,现在就是完成之后的内容。
StatusServer的改动
StatusServer添加了grpc服务Login用于验证是否token有效
grpc::Status StatusServiceImpl::Login(grpc::ServerContext* context, const message::LoginRequest* request, message::LoginResponse* response)
{
auto uid = request->uid();
auto token = request->token();
std::lock_guard<std::mutex> lock(_token_mutex);
auto it = _tokens.find(uid);
if (it == _tokens.end()) {
response->set_error(static_cast<int>(ErrorCodes::ERROR_UID_INVALID));
} else if (it->second != token) {
response->set_error(static_cast<int>(ErrorCodes::ERROR_TOKEN_INVALID));
}
response->set_error(static_cast<int>(ErrorCodes::SUCCESS));
response->set_uid(uid);
response->set_token(token);
return grpc::Status::OK;
}
ChatServer的改动
首先看ChatServer的LogicSystem的回调函数:
_function_callbacks[MsgId::ID_CHAT_LOGIN] = [this](std::shared_ptr<Session> session, uint16_t msg_id, const std::string& msg) {
json j = json::parse(msg);
auto uid = j["uid"].get<int>();
auto token = j["token"].get<std::string>();
SPDLOG_INFO("Thread: {},User {} Login with token {}", thread_id_to_string(std::this_thread::get_id()), uid, token);
auto reply = StatusClient::GetInstance()->Login(uid, token);
json jj;
Defer defer([this, &jj, session]() {
std::string return_str = jj.dump(4);
session->Send(return_str, static_cast<int>(MsgId::ID_CHAT_LOGIN_RSP));
});
jj["error"] = reply.error();
if (reply.error() != static_cast<int>(ErrorCodes::SUCCESS)) {
return;
}
std::string base_key = USER_BASE_INFO + std::to_string(uid);
auto user_info = std::make_shared<UserInfo>();
bool b_base = GetBaseInfo(base_key, uid, user_info);
if (!b_base) {
jj["error"] = ErrorCodes::ERROR_UID_INVALID;
return;
}
jj["uid"] = uid;
jj["name"] = user_info->name;
jj["email"] = user_info->email;
jj["password"] = user_info->password;
};
这里首先指出defer,捕获了json变量jj,利用RAII,无论是否异常,只要作用域结束就会发送数据(Send),而无需多次判断多次return.
然后是接下来的流程,我们调用grpc服务Login进行验证token是否有效,如果有效的话,那么顺势去查询用户的信息,存储在UserInfo的结构体中(实际这里有些冗余,因为在GateWayServer返回登陆信息的时候,就已经查询过一次了,下面给出代码回顾):
#pragma once
#include <string>
struct UserInfo{
UserInfo()
: name("")
, password("")
, email("")
, nick("")
, desc("")
, icon("")
, back("")
, sex(0)
, uid(0){}
std::string name;
std::string password;
std::string email;
std::string nick;
std::string desc;
std::string icon;
std::string back;
int uid;
int sex;
};
//GateWayServer
RegistHandlers("/userLogin", RequestType::POST, [this](std::shared_ptr<Session> session) {
auto body_str = beast::buffers_to_string(session->_request.body().data());
SPDLOG_DEBUG("receive userLogin request, body: {}", body_str);
session->_response.set(http::field::content_type, "text/json");
json j = json::parse(body_str);
if (j.is_null() || j.is_discarded()) {
SPDLOG_WARN("Invalid json");
j["error"] = ErrorCodes::ERROR_JSON;
std::string str = j.dump(4);
beast::ostream(session->_response.body()) << str;
return true;
}
auto user = j["user"].get<std::string>();
auto password = j["password"].get<std::string>();
// 验证密码得到uid
UserInfo userInfo;
bool ok = MysqlManager::GetInstance()->CheckPwd(user, password, userInfo);
if (!ok) {
SPDLOG_WARN("Failed to login user: {}", user);
j["error"] = ErrorCodes::ERROR_USER_OR_PASSWORD_INCORRECT;
std::string str = j.dump(4);
beast::ostream(session->_response.body()) << str;
return true;
}
// 登录成功,根据uid寻找服务器
auto reply = StatusClient::GetInstance()->GetChatServer(userInfo.uid);
if (reply.error()) {
SPDLOG_ERROR("Failed to load user chat server: {}", reply.error());
j["error"] = ErrorCodes::RPCFAILED;
std::string str = j.dump(4);
beast::ostream(session->_response.body()) << str;
return true;
}
SPDLOG_INFO("User {} login success.", userInfo.uid);
j["token"] = reply.token();
j["host"] = reply.host();
j["port"] = reply.port();
j["uid"] = userInfo.uid;
j["name"] = userInfo.name;
j["email"] = userInfo.email;
j["error"] = reply.error();
j["message"] = "登录成功";
beast::ostream(session->_response.body()) << j.dump(4);
return true;
});
这里看到确实,已经返回过用户信息了。
在客户端的对ID_LOGIN_USER的处理中,也早已得到了信息,只不过没有利用。
_handlers[RequestType::ID_LOGIN_USER] = [this](QJsonObject json){
int error = json["error"].toInt();
if(error != static_cast<int>(ErrorCodes::SUCCESS)){
showToolTip(loginBtn,"参数错误");
loginBtn->setEnabled(true);
return;
}
showToolTip(loginBtn,"正在连接服务器");
auto email = json["email"].toString();
ServerInfo si;
si.uid = json["uid"].toInt();
si.host = json["host"].toString();
si.port = json["port"].toString();
si.token = json["token"].toString();
si.email = json["email"].toString();
si.name = json["name"].toString();
_uid = si.uid;
_token = si.token;
qDebug() << si.uid <<"\t" << si.host << "\t" << si.token << "\t" << si.email <<"\t" << si.name;
emit on_tcp_connect(si);
};
不过我们暂时不对其进行修改,保持当前的操作。
下面是查询操作GetBaseInfo的参考:
bool LogicSystem::GetBaseInfo(std::string base_key, int uid, std::shared_ptr<UserInfo>& userinfo)
{
std::string info_str = "";
bool b_base = RedisManager::GetInstance()->Get(base_key, info_str);
if (b_base) {
json j = json::parse(info_str);
userinfo->name = j["name"].get<std::string>();
userinfo->password = j["password"].get<std::string>();
userinfo->email = j["email"].get<std::string>();
// userinfo->uid = j["uid"].get<int>();
// userinfo->sex = j["sex"].get<int>();
// userinfo->nick = j["nick"].get<std::string>();
// userinfo->desc = j["desc"].get<std::string>();
// userinfo->icon = j["icon"].get<std::string>();
SPDLOG_INFO("uid:{},name:{},email:{}", uid, userinfo->name, userinfo->email);
} else {
userinfo = MysqlManager::GetInstance()->GetUser(uid);
if (userinfo == nullptr) {
return false;
}
json j;
j["name"] = userinfo->name;
j["email"] = userinfo->email;
j["password"] = userinfo->password;
RedisManager::GetInstance()->Set(base_key, j.dump());
}
return true;
}
至于mysql数据层的代码与日常的查询无异,这里不再赘述。

浙公网安备 33010602011771号