Qt+QtWebApp開發筆記(四):http服務器使用Session和Cookie實現用戶密碼登錄和註銷功能

前言

  前面實現了基礎的跳轉,那麼動態交互中登錄是常用功能。
  本篇實現一個動態交互的簡單登錄和註銷功能,在Qt中使用Session和Cookie技術。

 

Demo

  在這裏插入圖片描述

下載地址

 

Html處理用戶輸入Session與Cookie

表單登錄submit

  Web應用程序通常處理用戶輸入。將開發一個登錄表單,看看進展如何。
創建一個名爲LoginController的新類。同樣,它是從HttpRequestHandl派生的

logincontroller.h:

#ifndef LOGINCONTROLLER_H
#define LOGINCONTROLLER_H

#include "httprequesthandler.h"

using namespace stefanfrings;

class LoginController : public HttpRequestHandler {
    Q_OBJECT
public:
    LoginController(QObject* parent=0);
    void service(HttpRequest& request, HttpResponse& response);
};

#endif // LOGINCONTROLLER_H

logincontroller.cpp:

#include "logincontroller.h"

LoginController::LoginController(QObject* parent)
    :HttpRequestHandler(parent) {
    // empty
}

void LoginController::service(HttpRequest &request, HttpResponse &response) {
    QByteArray username=request.getParameter("username");
    QByteArray password=request.getParameter("password");

    qDebug("username=%s",username.constData());
    qDebug("password=%s",password.constData());

    response.setHeader("Content-Type", "text/html; charset=UTF-8");
    response.write("<html><body>");

    if (username=="test" and password=="hello") {
        response.write("Yes, correct");
    }
    else {
        response.write("<form method='POST' action='/login'>");
        if (!username.isEmpty()) {
            response.write("No, that was wrong!<br><br>");
        }
        response.write("Please log in:<br>");
        response.write("Name:  <input type='text' name='username'><br>");
        response.write("Password: <input type='password' name='password'><br>");
        response.write("<input type='submit'>");
        response.write("</form");
    }

    response.write("</body></html>",true);
}

  (PS:html代表是提交表單)
  將這個新控制器添加到請求映射器中,修改requestmapper.h:

#ifndef REQUESTMAPPER_H
#define REQUESTMAPPER_H

#include "httprequesthandler.h"
#include "helloworldcontroller.h"
#include "listdatacontroller.h"
#include "logincontroller.h"

class RequestMapper : public HttpRequestHandler {
    Q_OBJECT
public:
    RequestMapper(QObject* parent=0);
    void service(HttpRequest& request, HttpResponse& response);
private:
    HelloWorldController helloWorldController;
    ListDataController listDataController;
    LoginController loginController;
};

#endif // REQUESTMAPPER_H

  修改requestmapper.cpp(切入了/login,調用loginController):

#include "requestmapper.h"

RequestMapper::RequestMapper(QObject* parent)
    : HttpRequestHandler(parent) {
    // empty
}

void RequestMapper::service(HttpRequest& request, HttpResponse& response) {
    QByteArray path=request.getPath();
    qDebug("RequestMapper: path=%s",path.data());

    if (path=="/" || path=="/hello") {
        helloWorldController.service(request, response);
    }
    else if (path=="/list") {
        listDataController.service(request, response);
    }
    else if (path=="/login") {
        loginController.service(request, response);
    }
    else {
        response.setStatus(404,"Not found");
        response.write("The URL is wrong, no such document.");
    }

    qDebug("RequestMapper: finished request");
}

  運行程序並打開URLhttp://localhost:8080/login.將看到以下表格:

  在這裏插入圖片描述
  嘗試使用錯誤的名稱和密碼登錄。然後瀏覽器顯示錯誤消息“That was wrong”,並提示重試。如果輸入了正確的憑據(用戶名“test”和密碼“hello”),則會收到成功消息。

  HTML表單定義了兩個名爲“username”和“password”的輸入字段。控制器使用request.getParameter()來獲取這些值。

  當參數爲空或傳入的HTTP請求中沒有這樣的參數時,Request.getParameter() 返回一個空的QByteArray。後一種情況發生在打開URL時http://localhost:8080/login開始只有當用戶單擊提交按鈕時,表單字段纔會從web瀏覽器發送到web服務器。

  如果需要區分空字段和缺失字段,那麼可以使用request.getParameterMap(),然後檢查所需參數是否在返回的映射中。

  作爲表單的替代方案,參數也可以作爲URL的一部分進行傳輸。例如,也可以通過打開URL登錄http://localhost:8080/login?username=test&password=hello.

  在URL中使用某些特殊字符時,必須將其編碼爲轉義序列。例如,如果用戶名是“Stefan Frings”,那麼必須寫http://localhost:8080/login?username=Stefan%20Frings&password=hello.HttpRequest類會自動將其解碼回原始形式“Stefan Frings”。

  如果需要將字符串編碼爲URL格式,可以使用QUrl類。

服務器本地會話session

  (PS:session和cookie是一起搭配使用的,cookie存在本地 session可以拿到cookie來判斷是否登錄了,等一些已有的狀態)
  下一個合乎邏輯的步驟是處理會話數據。這意味着,將當前用戶的數據保存在某個地方,並在後續請求中使用這些數據。將存儲的第一個數據是用戶的姓名和登錄時間。
  QtWebApp使用隱藏的cookie來識別用戶。
  必須在控制會話存儲類的配置文件webapp1.ini中添加一個新的部分:

[sessions]
expirationTime=3600000
cookieName=sessionid
;cookieDomain=mydomain.com
cookiePath=/
cookieComment=Identifies the user

  過期時間定義從內存中刪除未使用的會話後的毫秒數。當用戶在該時間之後返回時,他的會話將丟失,因此他必須再次登錄。

  • expirationTime:保存時間,實際上從後面的cookie截圖可以看到是3600000ms,則是3600秒一小時的時間過期,這個保存後的默認過期時間。
      在這裏插入圖片描述
  • sessionid:cookie名稱可以是任意名稱,但通常使用名稱“sessionid”。有些負載均衡器依賴於這個名稱,所以除非有充分的理由,否則不應該更改它。
  • cookieDomain:每個cookie總是綁定到一個域。由google.com生成的cookie只發送到該域上的服務器。如果將cookieDomain參數留空或將其註釋掉,則該參數將由web瀏覽器自動設置。可以指定另一個域名,但不知道男人爲什麼要這麼做。所以,除非知道自己在做什麼,否則就把它空着。
  • cookiePath:cookie路徑可用於將cookie限制在的域的一部分。如果將cookiePath更改爲/my/very/cool/online/shop,則瀏覽器將僅針對以該路徑開頭的頁面將cookie發送到的服務器。默認值爲“/”,這意味着cookie對域中的所有網站都有效。
  • cookieComment:cookieComment是一些網絡瀏覽器在cookie管理屏幕中顯示的文本。
      需要HttpSessionStore類的一個實例,整個程序都可以訪問該實例,因此可以在全局空間中訪問。因此,創建了兩個新文件,第一個是global.h:
#ifndef GLOBAL_H
#define GLOBAL_H

#include "httpsessionstore.h"

using namespace stefanfrings;

extern HttpSessionStore* sessionStore;

#endif // GLOBAL_H

  global.cpp:

#include "global.h"

HttpSessionStore* sessionStore;

  現在有了一個名爲“sessionStore”的全局靜態指針,整個程序可以通過包含global.h文件來訪問該指針。讓加載新的配置設置並初始化sessionStore。
  main.cpp中的更改:

#include "global.h"

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    QString configFileName=searchConfigFile();

    // Session store
    QSettings* sessionSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
    sessionSettings->beginGroup("sessions");
    sessionStore=new HttpSessionStore(sessionSettings,&app);

    // HTTP server
    QSettings* listenerSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
    listenerSettings->beginGroup("listener");
    new HttpListener(listenerSettings,new RequestMapper(&app),&app);

    return app.exec();
}

  請注意,main.cpp現在加載配置文件兩次。sessionSettings對象選擇“sessions”部分,而listenerSettings選擇“listener”部分。對於每個部分,需要一個單獨的QSettings實例,否則這些部分可能會混淆。
  既然已經爲會話數據創建了一個存儲,就可以開始使用它了。添加到logincontroller.cpp:

#include <QTime>
#include "logincontroller.h"
#include "global.h"

LoginController::LoginController(QObject* parent)
    :HttpRequestHandler(parent) {
    // empty
}

void LoginController::service(HttpRequest &request, HttpResponse &response) {
    HttpSession session=sessionStore->getSession(request,response,true);
    QByteArray username=request.getParameter("username");
    QByteArray password=request.getParameter("password");

    qDebug("username=%s",username.constData());
    qDebug("password=%s",password.constData());

    response.setHeader("Content-Type", "text/html; charset=UTF-8");
    response.write("<html><body>");

    if (session.contains("username")) {
        QByteArray username=session.get("username").toByteArray();
        QTime logintime=session.get("logintime").toTime();
        response.write("You are already logged in.<br>");
        response.write("Your name is: "+username+"<br>");
        response.write("You logged in at: "+logintime.toString("HH:mm:ss")+"<br>");
    }
    else {
        if (username=="test" and password=="hello") {
            response.write("Yes, correct");
            session.set("username",username);
            session.set("logintime",QTime::currentTime());
        }
        else {
            response.write("<form method='POST' action='/login'>");
            if (!username.isEmpty()) {
                response.write("No, that was wrong!<br><br>");
            }
            response.write("Please log in:<br>");
            response.write("Name:  <input type='text' name='username'><br>");
            response.write("Password: <input type='password' name='password'><br>");
            response.write("<input type='submit'>");
            response.write("</form")
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章