使用Flare从0搭建一个聊天室-2

364 阅读3分钟

这是我参与8月更文挑战的第15天,活动详情查看:8月更文挑战

过滤器

通常来说,一个聊天室在用户注册和登陆之后,访问其他接口时需要对其进行身份验证,在用户登陆后,登陆接口会为用户返回一个JWT token,用户需要在访问其他资源、接口时带上这个token,其中一种方式是称为Bearer的认证方式

Bearer

Bearer认证方式具体的资料可以在这里找到 Bearer,简单来说,在用户获得Token之后,需要在http header中带上。

Authorization: Bearer <token>

服务端解析得到token之后对其身份进行校验。

为了实现身份校验的拦截功能,通常在HTTP服务中通过过滤器或拦截器实现。

Flare Http Filter

flare中提供了一个HttpFilter的基类,如下:

class HttpFilter {
 public:
  virtual ~HttpFilter() = default;

  // Action to be taken by the framework.
  enum class Action {
    // Call next filter, or there is none, the actual HTTP handler.
    KeepProcessing,

    // Drop this request, nothing will be returned in this case. No futher
    // action (e.g. calling remaining filters, calling the actual HTTP handler)
    // is required.
    Drop,

    // Return immediately with what's filled in `response`, any pending filter
    // will not be called, neither will be the actual HTTP handler.
    EarlyReturn
  };

  // The framework calls this method before handing request to corresponding
  // handler.
  //
  // The implementation may mutate any of arguments if it deems fit. But be
  // caution not to confuse other filters / handler.
  virtual Action OnFilter(HttpRequest* request, HttpResponse* response,
                          HttpServerContext* context) = 0;
};

HttpFilter会在HttpHandler调用之前,以链式调用的方式调用每个filter,filter中可以对用户的request进行解析和判定,可以对response进行填充,返回值有三种,可以继续执行、直接丢弃和提前返回,我们通过实现一个HttpFilter的子类去实现接口的拦截,具体的身份验证Filter如下:

class LoginFilter : public flare::HttpFilter {
 private:
  flare::detail::UriMatcher _uri_matcher;

 public:
  explicit LoginFilter(flare::detail::UriMatcher uri_matcher = {});
  virtual ~LoginFilter() = default;

  Action OnFilter(flare::HttpRequest* request, flare::HttpResponse* response,
                  flare::HttpServerContext* context) override;
};
std::optional<std::string_view> ParseCredential(std::string_view cred) {
  // Authorization: 
  static constexpr auto kPrefix = "Bearer "sv;
  if (!flare::StartsWith(cred, kPrefix)) {
    return std::nullopt;
  }
  return cred.substr(kPrefix.size());
}

LoginFilter::LoginFilter(flare::detail::UriMatcher uri_matcher)
    : _uri_matcher(std::move(uri_matcher)) {}

flare::HttpFilter::Action LoginFilter::OnFilter(
    flare::HttpRequest* request, flare::HttpResponse* response,
    flare::HttpServerContext* context) {
  if (_uri_matcher(request->uri())) {
    return Action::KeepProcessing;
  }
  auto token =
      ParseCredential(request->headers()->TryGet("Authorization").value_or(""));
  if (!token) {
    response->set_status(flare::HttpStatus::BadRequest);
    return Action::EarlyReturn;
  }
  auto username = TokenUtil::Instance()->VerifyToken(*token);
  if (!username) {
    response->set_status(flare::HttpStatus::BadRequest);
    return Action::EarlyReturn;
  }
  request->headers()->Append("username", *username);
  return LoginFilter::Action::KeepProcessing;
}

在上述程序中,我们在OnFilter中实现了Bearer认证方式的解析,解析出token、然后调用jwt-cpp的接口对其进行正确性验证,当验证不通过时,设置相应状态为BadRequest,直接提前返回,若验证通过,则继续后面的处理过程。 在filter中,有一个UriMatcher类型的成员变量,用于对uri进行过滤,当匹配到uri时,不进行身份验证。

增加Filter

在http server上,我们也需要将我们实现的filter配置上去。

server.AddHttpFilter(std::make_unique<LoginFilter>(flare::detail::UriMatcher(std::regex("^/user(Login([/\?#](.*?))?)?$"))));

通过AddHttpFilter接口增加了LogFilter,并配置了用户注册和用户登陆等接口不用进行身份验证。

小结

本文为大家介绍了Flare中Http filter的实现方式,由于Http restful接口通常通过Json格式传输数据,所以接下来介绍一下c++中,如何方便的处理Json数据到Struct的相互转换。