Chap14-TokenVarifyAndUserManager

Chap14-TokenVarifyAndUserManager

流程梳理

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

image-20251029194525090

目前为止,一直到验证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数据层的代码与日常的查询无异,这里不再赘述。

posted @ 2025-12-24 23:16  大胖熊哈  阅读(13)  评论(0)    收藏  举报