Qt+QtWebApp開(kāi)發(fā)筆記(四):http服務(wù)器使用Session和Cookie實(shí)現(xiàn)用戶(hù)密碼登錄和注銷(xiāo)功
前言
??前面實(shí)現(xiàn)了基礎(chǔ)的跳轉(zhuǎn),那么動(dòng)態(tài)交互中登錄是常用功能。
??本篇實(shí)現(xiàn)一個(gè)動(dòng)態(tài)交互的簡(jiǎn)單登錄和注銷(xiāo)功能,在Qt中使用Session和Cookie技術(shù)。
Demo
??

下載地址
??鏈接:https://pan.baidu.com/s/1nkmsHgr-11Khe9k6Ntyf_g?pwd=1234
Html處理用戶(hù)輸入Session與Cookie
表單登錄submit
??Web應(yīng)用程序通常處理用戶(hù)輸入。將開(kāi)發(fā)一個(gè)登錄表單,看看進(jìn)展如何。
創(chuàng)建一個(gè)名為L(zhǎng)oginController的新類(lèi)。同樣,它是從HttpRequestHandl派生的
logincontroller.h:
#ifndef LOGINCONTROLLER_H#define LOGINCONTROLLER_H#include "httprequesthandler.h"using namespace stefanfrings;class LoginController : public HttpRequestHandler {
? ?Q_OBJECTpublic:
? ?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代表是提交表單)
??將這個(gè)新控制器添加到請(qǐng)求映射器中,修改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_OBJECTpublic:
? ?RequestMapper(QObject* parent=0);
? ?void service(HttpRequest& request, HttpResponse& response);private:
? ?HelloWorldController helloWorldController;
? ?ListDataController listDataController;
? ?LoginController loginController;};#endif // REQUESTMAPPER_H
??修改requestmapper.cpp(切入了/login,調(diào)用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");}
??運(yùn)行程序并打開(kāi)URLhttp://localhost:8080/login.將看到以下表格:
??

??嘗試使用錯(cuò)誤的名稱(chēng)和密碼登錄。然后瀏覽器顯示錯(cuò)誤消息“That was wrong”,并提示重試。如果輸入了正確的憑據(jù)(用戶(hù)名“test”和密碼“hello”),則會(huì)收到成功消息。
??HTML表單定義了兩個(gè)名為“username”和“password”的輸入字段??刂破魇褂胷equest.getParameter()來(lái)獲取這些值。
??當(dāng)參數(shù)為空或傳入的HTTP請(qǐng)求中沒(méi)有這樣的參數(shù)時(shí),Request.getParameter()?返回一個(gè)空的QByteArray。后一種情況發(fā)生在打開(kāi)URL時(shí)http://localhost:8080/login開(kāi)始只有當(dāng)用戶(hù)單擊提交按鈕時(shí),表單字段才會(huì)從web瀏覽器發(fā)送到web服務(wù)器。
??如果需要區(qū)分空字段和缺失字段,那么可以使用request.getParameterMap(),然后檢查所需參數(shù)是否在返回的映射中。
??作為表單的替代方案,參數(shù)也可以作為URL的一部分進(jìn)行傳輸。例如,也可以通過(guò)打開(kāi)URL登錄http://localhost:8080/login?username=test&password=hello.
??在URL中使用某些特殊字符時(shí),必須將其編碼為轉(zhuǎn)義序列。例如,如果用戶(hù)名是“Stefan Frings”,那么必須寫(xiě)http://localhost:8080/login?username=Stefan%20Frings&password=hello.HttpRequest類(lèi)會(huì)自動(dòng)將其解碼回原始形式“Stefan Frings”。
??如果需要將字符串編碼為URL格式,可以使用QUrl類(lèi)。
服務(wù)器本地會(huì)話session
??(PS:session和cookie是一起搭配使用的,cookie存在本地 session可以拿到cookie來(lái)判斷是否登錄了,等一些已有的狀態(tài))
??下一個(gè)合乎邏輯的步驟是處理會(huì)話數(shù)據(jù)。這意味著,將當(dāng)前用戶(hù)的數(shù)據(jù)保存在某個(gè)地方,并在后續(xù)請(qǐng)求中使用這些數(shù)據(jù)。將存儲(chǔ)的第一個(gè)數(shù)據(jù)是用戶(hù)的姓名和登錄時(shí)間。
??QtWebApp使用隱藏的cookie來(lái)識(shí)別用戶(hù)。
??必須在控制會(huì)話存儲(chǔ)類(lèi)的配置文件webapp1.ini中添加一個(gè)新的部分:
[sessions]expirationTime=3600000cookieName=sessionid;cookieDomain=mydomain.com
cookiePath=/cookieComment=Identifies the user
??過(guò)期時(shí)間定義從內(nèi)存中刪除未使用的會(huì)話后的毫秒數(shù)。當(dāng)用戶(hù)在該時(shí)間之后返回時(shí),他的會(huì)話將丟失,因此他必須再次登錄。
expirationTime:保存時(shí)間,實(shí)際上從后面的cookie截圖可以看到是3600000ms,則是3600秒一小時(shí)的時(shí)間過(guò)期,這個(gè)保存后的默認(rèn)過(guò)期時(shí)間。
??

sessionid:cookie名稱(chēng)可以是任意名稱(chēng),但通常使用名稱(chēng)“sessionid”。有些負(fù)載均衡器依賴(lài)于這個(gè)名稱(chēng),所以除非有充分的理由,否則不應(yīng)該更改它。
cookieDomain:每個(gè)cookie總是綁定到一個(gè)域。由google.com生成的cookie只發(fā)送到該域上的服務(wù)器。如果將cookieDomain參數(shù)留空或?qū)⑵渥⑨尩?,則該參數(shù)將由web瀏覽器自動(dòng)設(shè)置??梢灾付硪粋€(gè)域名,但不知道男人為什么要這么做。所以,除非知道自己在做什么,否則就把它空著。
cookiePath:cookie路徑可用于將cookie限制在的域的一部分。如果將cookiePath更改為/my/very/cool/online/shop,則瀏覽器將僅針對(duì)以該路徑開(kāi)頭的頁(yè)面將cookie發(fā)送到的服務(wù)器。默認(rèn)值為“/”,這意味著cookie對(duì)域中的所有網(wǎng)站都有效。
cookieComment:cookieComment是一些網(wǎng)絡(luò)瀏覽器在cookie管理屏幕中顯示的文本。
??需要HttpSessionStore類(lèi)的一個(gè)實(shí)例,整個(gè)程序都可以訪問(wèn)該實(shí)例,因此可以在全局空間中訪問(wèn)。因此,創(chuàng)建了兩個(gè)新文件,第一個(gè)是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;
??現(xiàn)在有了一個(gè)名為“sessionStore”的全局靜態(tài)指針,整個(gè)程序可以通過(guò)包含global.h文件來(lái)訪問(wèn)該指針。讓加載新的配置設(shè)置并初始化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();}
??請(qǐng)注意,main.cpp現(xiàn)在加載配置文件兩次。sessionSettings對(duì)象選擇“sessions”部分,而listenerSettings選擇“l(fā)istener”部分。對(duì)于每個(gè)部分,需要一個(gè)單獨(dú)的QSettings實(shí)例,否則這些部分可能會(huì)混淆。
??既然已經(jīng)為會(huì)話數(shù)據(jù)創(chuàng)建了一個(gè)存儲(chǔ),就可以開(kāi)始使用它了。添加到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");
? ? ? ?}
? ?}
? ?response.write("</body></html>",true);}
??在這里,重要的是在第一次調(diào)用response.write()之前調(diào)用sessionStore->getSession(),因?yàn)樗鼊?chuàng)建或刷新會(huì)話cookie,從技術(shù)上講,它是一個(gè)HTTP頭。并且所有HTTP標(biāo)頭都必須在HTML文檔之前發(fā)送。
??通過(guò)打開(kāi)來(lái)運(yùn)行和測(cè)試程序http://localhost:8080/login.
??

??現(xiàn)在查看web服務(wù)器的控制臺(tái)窗口時(shí),看到一個(gè)cookie和一個(gè)會(huì)話是用一個(gè)唯一的id創(chuàng)建的,這個(gè)id是一個(gè)長(zhǎng)的隨機(jī)十六進(jìn)制數(shù)。
??從Chromium瀏覽器中截取了以下截圖,可以在其中看到cookie:
??

??用戶(hù)會(huì)話最初為空。它只是存在,并且有一個(gè)唯一的id號(hào)。沒(méi)有別的。輸入用戶(hù)名“test”和密碼“hello”。然后會(huì)得到預(yù)期的確認(rèn)。
??

??在確認(rèn)登錄成功的同時(shí),服務(wù)器將用戶(hù)名和登錄時(shí)間輸入到用戶(hù)會(huì)話中??梢詫⑷魏螌?duì)象放入QVariant支持的會(huì)話中。當(dāng)將一個(gè)對(duì)象放入會(huì)話時(shí),會(huì)給它一個(gè)符號(hào)名稱(chēng),以便以后訪問(wèn)。
??現(xiàn)在再次打開(kāi)URLhttp://localhost:8080/login.然后會(huì)看到所有這些工作的結(jié)果:
??

??因此,在成功驗(yàn)證用戶(hù)名和密碼后,服務(wù)器使用會(huì)話來(lái)記住該用戶(hù)的數(shù)據(jù)。
??會(huì)話存儲(chǔ)保存在內(nèi)存中。重新啟動(dòng)web服務(wù)器時(shí),會(huì)話存儲(chǔ)中的所有數(shù)據(jù)都會(huì)丟失。因此,只將其用于一些臨時(shí)數(shù)據(jù)。持久性數(shù)據(jù)屬于一個(gè)數(shù)據(jù)庫(kù)。
用戶(hù)本地終端的存儲(chǔ)cookies
??作為會(huì)話存儲(chǔ)的替代方案,也可以在cookie中存儲(chǔ)少量數(shù)據(jù)。Cookie存儲(chǔ)在web瀏覽器的客戶(hù)端,而不是服務(wù)器端。Cookie只能存儲(chǔ)8位的文本,并且只能保證4 KB的容量。此外,每個(gè)域的cookie數(shù)量是有限的,因此請(qǐng)保留使用它們。
??為了嘗試這一點(diǎn),添加一個(gè)名為CookieTestController的新控制器類(lèi),并將其綁定到路徑“/cookie”。
cookietestcontroller.cpp:
#include "cookietestcontroller.h" CookieTestController::CookieTestController(QObject* parent) ? ? : HttpRequestHandler(parent) { ? ? // empty } void CookieTestController::service(HttpRequest &request, HttpResponse &response) { ? ? QByteArray cookie=request.getCookie("testcookie"); ? ? if (!cookie.isEmpty()) { ? ? ? ? response.write("Found cookie with value: "+cookie,true); ? ? } ? ? else { ? ? ? ? HttpCookie cookie("testcookie","123456789",60); ? ? ? ? response.setCookie(cookie); ? ? ? ? response.write("A new cookie has been created.",true); ? ? } }
??cookie的名稱(chēng)為“testcookie”,其值為“123456789”。60是以秒為單位的有效時(shí)間。?此cookie在創(chuàng)建一分鐘后過(guò)期。這段短暫的時(shí)間來(lái)看看cookie過(guò)期后會(huì)發(fā)生什么。通常會(huì)使用更大的時(shí)間,可能是幾天。
??Request.getCookie()只返回cookie的值,而不是整個(gè)HttpCookie對(duì)象。這是出于性能原因。
??運(yùn)行程序并打開(kāi)http://localhost:8080/cookie.
??

??然后再次加載同一頁(yè)面,將看到服務(wù)器正確接收到cookie:
??

??Cookie存儲(chǔ)在網(wǎng)絡(luò)瀏覽器中,并隨每個(gè)HTTP請(qǐng)求一起發(fā)送到網(wǎng)絡(luò)服務(wù)器。除了會(huì)話數(shù)據(jù)之外,如果重新啟動(dòng)服務(wù)器,cookie不會(huì)丟失,因?yàn)閏ookie存儲(chǔ)在客戶(hù)端。
??Chromium瀏覽器的屏幕截圖:
??

??在這里,可以看到會(huì)話cookie仍然存在?,F(xiàn)在有兩個(gè)本地主機(jī)域的cookie。
??如果等待兩分鐘,然后再次加載http://localhost:8080/cookie將看到測(cè)試cookie已過(guò)期。服務(wù)器會(huì)創(chuàng)建一個(gè)新的。
??如果想防止cookie在用戶(hù)處于活動(dòng)狀態(tài)時(shí)過(guò)期,那么必須在每次請(qǐng)求時(shí)重新創(chuàng)建cookie。然后,瀏覽器會(huì)為每個(gè)請(qǐng)求計(jì)算一個(gè)新的截止日期。
Demo增量:實(shí)戰(zhàn)用戶(hù)登錄
步驟一:準(zhǔn)備代碼模板
??準(zhǔn)備之前的demo v1.2.0模板:
??

步驟二:新增登錄入口處理LoginRequestHandler
??

??修改一下:
??

??

步驟三:將登錄界面的html的添加進(jìn)登錄入口
??

void LoginRequestHandler::service(HttpRequest &request, HttpResponse &response) { ? ? QString str; ? ? QString path = request.getPath(); ? ? LOG << path; ? ? str = "<!DOCTYPE html>" ? ? ? ? ? "<html lang=\"en\">" ? ? ? ? ? "<head>" ? ? ? ? ? "<meta charset=\"UTF-8\">" ? ? ? ? ? "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">" ? ? ? ? ? "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">" ? ? ? ? ? "<title>長(zhǎng)沙紅胖子Qt</title>" ? ? ? ? ? "</head>" ? ? ? ? ? "<body>" ? ? ? ? ? " ? ?<a href=\"javascript:history.back(-1);\">返回上一頁(yè)</a>" ? ? ? ? ? " ? ?<form method='POST' action='/login'>" ? ? ? ? ? " ? ? ? ?<p>Please login</p>" ? ? ? ? ? " ? ? ? ?<p>Name: <input type='text' name='username'></p>" ? ? ? ? ? " ? ? ? ?<p>Password: <input type='password' name='password'></p>" ? ? ? ? ? " ? ? ? ?<p><input type='submit'></p>" ? ? ? ? ? " ? ?</form>" ? ? ? ? ? "</body>"; ? ? // 返回文本(需要在瀏覽器上看,所以將Qt內(nèi)部編碼都轉(zhuǎn)成GBK輸出即可,不管他本身是哪個(gè)編碼) // ? ?QByteArray byteArray = _pTextCodec->fromUnicode(str); ? ? QByteArray byteArray = str.toUtf8(); ? ? response.write(byteArray); }
步驟四:在原來(lái)Index上新增一個(gè)連接地址跳轉(zhuǎn)登錄
??

??

??這個(gè)時(shí)候是可以跳轉(zhuǎn)了。
??

??此時(shí),登錄打印:
??

??可以,其路徑提交也是觸發(fā)了一次進(jìn)入login的service,這時(shí)候就需要通過(guò)Session的變量來(lái)判斷是第一次進(jìn)入還是提交登錄。
步驟五:添加sessionStore頭文件
??SessionStore頭文件在httpserver模塊中。
??

步驟六:添加唯一實(shí)例類(lèi)sessionStore,封裝session存儲(chǔ)類(lèi)
??

??

??

步驟七:添加配置文件
[sessions] expirationTime=3600000 cookieName=sessionid ;cookieDomain=mydomain.com cookiePath=/ cookieComment=Identifies the user
??

步驟八:添加session加載配置代碼
??

??

步驟九:通過(guò)session判斷
??在沒(méi)有session之前是可以獲取表單提交的參數(shù):
??

??添加session判斷后:
void LoginRequestHandler::service(HttpRequest &request, HttpResponse &response) { ? ? QString str; ? ? QString path = request.getPath(); ? ? LOG << path; ? ? // 這里獲取到的param是直接獲取的 ? ? QString userName = request.getParameter("username"); ? ? QString password = request.getParameter("password"); ? ? LOG << userName << password; ? ? // 獲取session(單例模式封裝后的全局獲取) ? ? HttpSession httpSession = ? ? ? ? ? ? HttpSessionStoreManager::getInstance()->getHttpSessionStore()->getSession(request,response,true); ? ? // 先判斷是否session能獲取到 ? ? if(!httpSession.contains("username")) ? ? { ? ? ? ? // 這里沒(méi)有cookie,提交登錄則是可以保存本地cookie ? ? ? ? if(userName == "root" && password == "root123456") ? ? ? ? { ? ? ? ? ? ? httpSession.set("username" , userName); ? ? ? ? ? ? httpSession.set("logintime", QTime::currentTime()); ? ? ? ? ? ? httpSession.set("timeout" ?, 60); ? ? ? ? ? ? QString username = httpSession.get("username").toString(); ? ? ? ? ? ? QTime logintime = httpSession.get("logintime").toTime(); ? ? ? ? ? ? int timeout = httpSession.get("timeout").toInt(); ? ? ? ? ? ? str = QString("<!DOCTYPE html>" ? ? ? ? ? ? ? ? ? ? ? ? ? "<html lang=\"en\">" ? ? ? ? ? ? ? ? ? ? ? ? ? "<head>" ? ? ? ? ? ? ? ? ? ? ? ? ? "<meta charset=\"UTF-8\">" ? ? ? ? ? ? ? ? ? ? ? ? ? "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">" ? ? ? ? ? ? ? ? ? ? ? ? ? "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">" ? ? ? ? ? ? ? ? ? ? ? ? ? "<title>長(zhǎng)沙紅胖子Qt</title>" ? ? ? ? ? ? ? ? ? ? ? ? ? "</head>" ? ? ? ? ? ? ? ? ? ? ? ? ? "<body>" ? ? ? ? ? ? ? ? ? ? ? ? ? " ? ?<a href=\"javascript:history.back(-1);\">注銷(xiāo)登錄</a>" ? ? ? ? ? ? ? ? ? ? ? ? ? " ? ?<p>登錄賬戶(hù):%1</p>" ? ? ? ? ? ? ? ? ? ? ? ? ? " ? ?<p>登錄時(shí)間:%2</p>" ? ? ? ? ? ? ? ? ? ? ? ? ? " ? ?<p>超時(shí)時(shí)間:%3</p>" ? ? ? ? ? ? ? ? ? ? ? ? ? "</body>") ? ? ? ? ? ? ? ? ? ? .arg(username) ? ? ? ? ? ? ? ? ? ? .arg(logintime.toString("hh:mm:ss:zzz")) ? ? ? ? ? ? ? ? ? ? .arg(timeout); ? ? ? ? }else{ ? ? ? ? ? ? str = "<!DOCTYPE html>" ? ? ? ? ? ? ? ? ? "<html lang=\"en\">" ? ? ? ? ? ? ? ? ? "<head>" ? ? ? ? ? ? ? ? ? "<meta charset=\"UTF-8\">" ? ? ? ? ? ? ? ? ? "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">" ? ? ? ? ? ? ? ? ? "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">" ? ? ? ? ? ? ? ? ? "<title>長(zhǎng)沙紅胖子Qt</title>" ? ? ? ? ? ? ? ? ? "</head>" ? ? ? ? ? ? ? ? ? "<body>" ? ? ? ? ? ? ? ? ? " ? ?<a href=\"javascript:history.back(-1);\">返回上一頁(yè)</a>" ? ? ? ? ? ? ? ? ? " ? ?<form method='POST' action='/login'>" ? ? ? ? ? ? ? ? ? " ? ? ? ?<p>Please login</p>" ? ? ? ? ? ? ? ? ? " ? ? ? ?<p>Name: <input type='text' name='username'></p>" ? ? ? ? ? ? ? ? ? " ? ? ? ?<p>Password: <input type='password' name='password'></p>" ? ? ? ? ? ? ? ? ? " ? ? ? ?<p><input type='submit'></p>" ? ? ? ? ? ? ? ? ? " ? ?</form>" ? ? ? ? ? ? ? ? "</body>"; ? ? ? ? } ? ? }else { ? ? ? ? // 之前已經(jīng)登錄過(guò)了 ? ? ? ? QString username = httpSession.get("username").toString(); ? ? ? ? QTime logintime = httpSession.get("logintime").toTime(); ? ? ? ? int timeout = httpSession.get("timeout").toInt(); ? ? ? ? str = QString("<!DOCTYPE html>" ? ? ? ? ? ? ? ? ? ? ? "<html lang=\"en\">" ? ? ? ? ? ? ? ? ? ? ? "<head>" ? ? ? ? ? ? ? ? ? ? ? "<meta charset=\"UTF-8\">" ? ? ? ? ? ? ? ? ? ? ? "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">" ? ? ? ? ? ? ? ? ? ? ? "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">" ? ? ? ? ? ? ? ? ? ? ? "<title>長(zhǎng)沙紅胖子Qt</title>" ? ? ? ? ? ? ? ? ? ? ? "</head>" ? ? ? ? ? ? ? ? ? ? ? "<body>" ? ? ? ? ? ? ? ? ? ? ? " ? ?<a href=\"javascript:history.back(-1);\">注銷(xiāo)登錄</a>" ? ? ? ? ? ? ? ? ? ? ? " ? ?<p>登錄賬戶(hù):%1</p>" ? ? ? ? ? ? ? ? ? ? ? " ? ?<p>登錄時(shí)間:%2</p>" ? ? ? ? ? ? ? ? ? ? ? " ? ?<p>超時(shí)時(shí)間:%3</p>" ? ? ? ? ? ? ? ? ? ? ? "</body>") ? ? ? ? ? ? ? ? .arg(username) ? ? ? ? ? ? ? ? .arg(logintime.toString("hh:mm:ss:zzz")) ? ? ? ? ? ? ? ? .arg(timeout); ? ? } ? ? // 返回文本(需要在瀏覽器上看,所以將Qt內(nèi)部編碼都轉(zhuǎn)成GBK輸出即可,不管他本身是哪個(gè)編碼) // ? ?QByteArray byteArray = _pTextCodec->fromUnicode(str); ? ? QByteArray byteArray = str.toUtf8(); ? ? response.write(byteArray); }
??查看session
??

步驟十:添加一個(gè)“注銷(xiāo)登錄”
??

// 注銷(xiāo),清空session if(path == "/login/out") { ? ? HttpSession httpSession = ? ? ? ? ? ? HttpSessionStoreManager::getInstance()->getHttpSessionStore()->getSession(request,response,true); ? ? HttpSessionStoreManager::getInstance()->getHttpSessionStore()->removeSession(httpSession); }
Demo源碼
LoginRequestHandler.h
#ifndef LOGINREQUESTHANDLER_H #define LOGINREQUESTHANDLER_H #include "httprequesthandler.h" #include "HelloWorldRequestHandler.h" #include "ListRequestHandler.h" using namespace stefanfrings; class LoginRequestHandler : public HttpRequestHandler { public: ? ? LoginRequestHandler(QObject *parent = 0); public: ? ? void service(HttpRequest& request, HttpResponse& response); private: ? ? QTextCodec *_pTextCodec; private: }; #endif // LoginRequestHandler_H
LoginRequestHandler.cpp
#include "LoginRequestHandler.h" #include "ListRequestHandler.h" #include "HttpSessionStoreManager.h" #include <QTextCodec> #include <QDebug> #include <QDateTime> //#define LOG qDebug()<<__FILE__<<__LINE__ //#define LOG qDebug()<<__FILE__<<__LINE__<<__FUNCTION__ //#define LOG qDebug()<<__FILE__<<__LINE__<<QThread()::currentThread() //#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd") #define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz") using namespace stefanfrings; LoginRequestHandler::LoginRequestHandler(QObject *parent) ? ? : HttpRequestHandler(parent) { ? ? // 返回文本(需要在瀏覽器上看,所以將Qt內(nèi)部編碼都轉(zhuǎn)成GBK輸出即可,不管他本身是哪個(gè)編碼) ? ? // WINDOWS: GBK ?GB2312 ? ? // LINUX ?: urf-8 // ? ?_pTextCodec = QTextCodec::codecForName("utf-8"); ? ? _pTextCodec = QTextCodec::codecForName("GBK"); } void LoginRequestHandler::service(HttpRequest &request, HttpResponse &response) { ? ? QString str; ? ? QString path = request.getPath(); ? ? LOG << path; ? ? // 這里獲取到的param是直接獲取的 ? ? QString userName = request.getParameter("username"); ? ? QString password = request.getParameter("password"); ? ? LOG << userName << password; ? ? // 注銷(xiāo),清空session ? ? if(path == "/login/out") ? ? { ? ? ? ? HttpSession httpSession = ? ? ? ? ? ? ? ? HttpSessionStoreManager::getInstance()->getHttpSessionStore()->getSession(request,response,true); ? ? ? ? HttpSessionStoreManager::getInstance()->getHttpSessionStore()->removeSession(httpSession); ? ? } ? ? // 獲取session(單例模式封裝后的全局獲取) ? ? HttpSession httpSession = ? ? ? ? ? ? HttpSessionStoreManager::getInstance()->getHttpSessionStore()->getSession(request,response,true); ? ? // 先判斷是否session能獲取到 ? ? if(!httpSession.contains("username")) ? ? { ? ? ? ? // 這里沒(méi)有cookie,提交登錄則是可以保存本地cookie ? ? ? ? if(userName == "root" && password == "root123456") ? ? ? ? { ? ? ? ? ? ? httpSession.set("username" , userName); ? ? ? ? ? ? httpSession.set("logindatetime", QDateTime::currentDateTime()); ? ? ? ? ? ? httpSession.set("timeout" ?, 60); ? ? ? ? ? ? QString username = httpSession.get("username").toString(); ? ? ? ? ? ? QDateTime logindatetime = httpSession.get("logindatetime").toDateTime(); ? ? ? ? ? ? int timeout = httpSession.get("timeout").toInt(); ? ? ? ? ? ? str = QString("<!DOCTYPE html>" ? ? ? ? ? ? ? ? ? ? ? ? ? "<html lang=\"en\">" ? ? ? ? ? ? ? ? ? ? ? ? ? "<head>" ? ? ? ? ? ? ? ? ? ? ? ? ? "<meta charset=\"UTF-8\">" ? ? ? ? ? ? ? ? ? ? ? ? ? "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">" ? ? ? ? ? ? ? ? ? ? ? ? ? "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">" ? ? ? ? ? ? ? ? ? ? ? ? ? "<title>長(zhǎng)沙紅胖子Qt</title>" ? ? ? ? ? ? ? ? ? ? ? ? ? "</head>" ? ? ? ? ? ? ? ? ? ? ? ? ? "<body>" ? ? ? ? ? ? ? ? ? ? ? ? ? " ? ?<a href=\"/login/out\"><input value='注銷(xiāo)' type='reset'></a>" ? ? ? ? ? ? ? ? ? ? ? ? ? " ? ?<p>登錄賬戶(hù):%1</p>" ? ? ? ? ? ? ? ? ? ? ? ? ? " ? ?<p>登錄時(shí)間:%2</p>" ? ? ? ? ? ? ? ? ? ? ? ? ? " ? ?<p>超時(shí)時(shí)間:%3</p>" ? ? ? ? ? ? ? ? ? ? ? ? ? "</body>") ? ? ? ? ? ? ? ? ? ? .arg(username) ? ? ? ? ? ? ? ? ? ? .arg(logindatetime.toString("yyyy-MM-dd hh:mm:ss:zzz")) ? ? ? ? ? ? ? ? ? ? .arg(timeout); ? ? ? ? }else{ ? ? ? ? ? ? str = "<!DOCTYPE html>" ? ? ? ? ? ? ? ? ? "<html lang=\"en\">" ? ? ? ? ? ? ? ? ? "<head>" ? ? ? ? ? ? ? ? ? "<meta charset=\"UTF-8\">" ? ? ? ? ? ? ? ? ? "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">" ? ? ? ? ? ? ? ? ? "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">" ? ? ? ? ? ? ? ? ? "<title>長(zhǎng)沙紅胖子Qt</title>" ? ? ? ? ? ? ? ? ? "</head>" ? ? ? ? ? ? ? ? ? "<body>" ? ? ? ? ? ? ? ? ? " ? ?<a href=\"javascript:history.back(-1);\">返回上一頁(yè)</a>" ? ? ? ? ? ? ? ? ? " ? ?<form method='POST' action='/login'>" ? ? ? ? ? ? ? ? ? " ? ? ? ?<p>Please login</p>" ? ? ? ? ? ? ? ? ? " ? ? ? ?<p>Name: <input type='text' name='username'></p>" ? ? ? ? ? ? ? ? ? " ? ? ? ?<p>Password: <input type='password' name='password'></p>" ? ? ? ? ? ? ? ? ? " ? ? ? ?<p><input type='submit'></p>" ? ? ? ? ? ? ? ? ? " ? ?</form>" ? ? ? ? ? ? ? ? "</body>"; ? ? ? ? } ? ? }else { ? ? ? ? // 之前已經(jīng)登錄過(guò)了 ? ? ? ? QString username = httpSession.get("username").toString(); ? ? ? ? QDateTime logindatetime = httpSession.get("logindatetime").toDateTime(); ? ? ? ? int timeout = httpSession.get("timeout").toInt(); ? ? ? ? str = QString("<!DOCTYPE html>" ? ? ? ? ? ? ? ? ? ? ? "<html lang=\"en\">" ? ? ? ? ? ? ? ? ? ? ? "<head>" ? ? ? ? ? ? ? ? ? ? ? "<meta charset=\"UTF-8\">" ? ? ? ? ? ? ? ? ? ? ? "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">" ? ? ? ? ? ? ? ? ? ? ? "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">" ? ? ? ? ? ? ? ? ? ? ? "<title>長(zhǎng)沙紅胖子Qt</title>" ? ? ? ? ? ? ? ? ? ? ? "</head>" ? ? ? ? ? ? ? ? ? ? ? "<body>" ? ? ? ? ? ? ? ? ? ? ? " ? ?<a href=\"/login/out\"><input value='注銷(xiāo)' type='reset'></a>" ? ? ? ? ? ? ? ? ? ? ? " ? ?<p>登錄賬戶(hù):%1</p>" ? ? ? ? ? ? ? ? ? ? ? " ? ?<p>登錄時(shí)間:%2</p>" ? ? ? ? ? ? ? ? ? ? ? " ? ?<p>超時(shí)時(shí)間:%3</p>" ? ? ? ? ? ? ? ? ? ? ? "</body>") ? ? ? ? ? ? ? ? .arg(username) ? ? ? ? ? ? ? ? .arg(logindatetime.toString("yyyy-MM-dd hh:mm:ss:zzz")) ? ? ? ? ? ? ? ? .arg(timeout); ? ? } ? ? // 返回文本(需要在瀏覽器上看,所以將Qt內(nèi)部編碼都轉(zhuǎn)成GBK輸出即可,不管他本身是哪個(gè)編碼) // ? ?QByteArray byteArray = _pTextCodec->fromUnicode(str); ? ? QByteArray byteArray = str.toUtf8(); ? ? response.write(byteArray); }
工程模板v1.3.0
??
