Difference between revisions of "TutWebAppServer Lesson 6 - Simple login and session data"
m (Fixed a few typos) |
|||
(One intermediate revision by one other user not shown) | |||
Line 1: | Line 1: | ||
− | During this lesson, you will learn how to handle session data | + | During this lesson, you will learn how to handle session data. |
− | The application programmer has the need to have a data structure, at server side, with values private to a given user for the duration of his activity with the application. Of course since we are building a server application, we can have many users connected at the same time. The | + | As you probably know, HTTP is a stateless protocol. This means that each request is totally indepedent from each other as far as HTTP protocol is concerned. A web application is made of a lot of HTTP requests as the user interacts with the application. Because of the stateless nature of the HTTP protocol, your web application cannot rely on HTTP to have each page of the application know it is part of a single application used by a particular user at a given time. It is the application programmer's responsibility to make the application pages seen as a whole application, that is having a page content or behaviour affected by what the user has done previously with the application. |
+ | |||
+ | The application programmer has the need to have a data structure, at server side, with values private to a given user for the duration of his activity with the application. Of course since we are building a server application, we can have many users connected at the same time. The developer need a different data instance for each user. We will refer to this kind of data as "session data" in the remaining lessons. | ||
Don't be scary, ICS HttpAppServer component and the user browser will help you achieve that goal. | Don't be scary, ICS HttpAppServer component and the user browser will help you achieve that goal. | ||
Line 16: | Line 18: | ||
<li> Create a new template containing the login form. We do a dynamic page because frequently login is done from the main page which almost contain dynamic data. For simplicity here our page will only show the current time at server side. Enter the following code into the body part of the document: | <li> Create a new template containing the login form. We do a dynamic page because frequently login is done from the main page which almost contain dynamic data. For simplicity here our page will only show the current time at server side. Enter the following code into the body part of the document: | ||
<tt> | <tt> | ||
− | + | : <form action="/DoSimpleLogin.html" | |
− | + | :::: method="GET" | |
− | + | :::: enctype="application/x-www-form-urlencoded"> | |
− | + | :: Usercode <input type="TEXT" name="UserCode"><br> | |
− | + | :: Password <input type="PASSWORD" name="Password"><br> | |
− | + | :: <input type="SUBMIT" value="Login"><br> | |
− | + | :: <br> | |
− | + | :: <br>Server time is <#ServerTime> | |
− | + | : </form> | |
</tt> | </tt> | ||
Save the HTML file in the templates folder under the name "SimpleLogin.html".</li> | Save the HTML file in the templates folder under the name "SimpleLogin.html".</li> | ||
<li> Create a new template containing the simple page accessible only after login and showing some session data. Here after is the HTML code you have to enter into the body part of the document: | <li> Create a new template containing the simple page accessible only after login and showing some session data. Here after is the HTML code you have to enter into the body part of the document: | ||
<tt> | <tt> | ||
− | + | : If you see this page, then you have a valid session.<br> | |
− | + | : You logged at <#LoginTime><br> | |
− | + | : Your user code is <#UserCode><br> | |
− | + | : You already saw this page <#PageCount> times since you logged in.<br> | |
</tt> | </tt> | ||
Save the HTML file in the templates folder under the name "SimpleMainPage.html".</li> | Save the HTML file in the templates folder under the name "SimpleMainPage.html".</li> | ||
+ | <li> Create a new Delphi unit "OverbyteIcsTutWebAppServerSimpleLogin.pas". You will write the classes TUrlHandlerSimpleLogin and TUrlHandlerDoSimpleLogin containing the code to handle the login process: one class for the login form and one class for the login form action. You will also write the class TUrlHandlerSimpleMainPage to handle the main page, that is the result page shown when login is OK. | ||
</li> | </li> | ||
− | + | <li> Map the classes to the URLs. In TTutWebAppServerMainForm.FormShow, add the following lines after the similar already there: | |
− | |||
− | <li> Map the classes to the | ||
<tt> | <tt> | ||
− | + | : HttpAppSrv1.AddGetHandler('/SimpleLogin.Html', TUrlHandlerSimpleLogin); | |
− | + | : HttpAppSrv1.AddGetHandler('/DoSimpleLogin.html', TUrlHandlerDoSimpleLogin); | |
− | + | : HttpAppSrv1.AddGetHandler('/SimpleMainPage.html', TUrlHandlerSimpleMainPage); | |
</tt> | </tt> | ||
</li> | </li> | ||
<li> Create the supporting 3 classes. For now, we just build basic code which doesn't do an actual login. This is just to verify that everything is OK until now. The class declaration part of unit "OverbyteIcsTutWebAppServerSimpleLogin.pas" looks like: | <li> Create the supporting 3 classes. For now, we just build basic code which doesn't do an actual login. This is just to verify that everything is OK until now. The class declaration part of unit "OverbyteIcsTutWebAppServerSimpleLogin.pas" looks like: | ||
<tt> | <tt> | ||
− | + | : TUrlHandlerSimpleLogin = '''class'''(TUrlHandler) | |
− | + | : '''public''' | |
− | + | :: '''procedure''' Execute; '''override;''' | |
− | + | : '''end;''' | |
− | + | :<br> | |
− | + | : TUrlHandlerDoSimpleLogin = '''class'''(TUrlHandler) | |
− | + | : '''public''' | |
− | + | :: '''procedure''' Execute; '''override;''' | |
− | + | : '''end;''' | |
− | + | :<br> | |
− | + | : TUrlHandlerSimpleMainPage = '''class'''(TUrlHandler) | |
− | + | : '''public''' | |
− | + | :: '''procedure''' Execute; '''override;''' | |
− | + | : '''end;''' | |
</tt> | </tt> | ||
And the implementation looks like: | And the implementation looks like: | ||
<tt> | <tt> | ||
− | + | :'''procedure''' TUrlHandlerSimpleLogin.Execute; | |
− | + | :'''begin''' | |
− | + | :: AnswerPage(’’, NO_CACHE, 'SimpleLogin.html', nil, | |
− | + | :::: ['ServerTime', DateTimeToStr(Now)]); | |
− | + | :: Finish; | |
− | + | :'''end;''' | |
− | + | :<br> | |
− | + | :'''procedure''' TUrlHandlerDoSimpleLogin.Execute; | |
− | + | :'''var''' | |
− | + | :: Location : '''String;''' | |
− | + | :'''begin''' | |
− | + | :: Location := '/SimpleMainPage.html'; | |
− | + | :: AnswerString('302 moved', ’’, | |
− | + | :::: 'Location: ' + Location + #13#10 + NO_CACHE, | |
− | + | :::: '<A HREF="' + Location + '">Click here</A>'); | |
− | + | :: Finish; | |
− | + | :'''end;''' | |
− | + | :<br> | |
− | + | :'''procedure''' TUrlHandlerSimpleMainPage.Execute; | |
− | + | :'''begin''' | |
− | + | :: AnswerPage(’’, NO_CACHE, 'SimpleMainPage.html', nil, []); | |
− | + | :: Finish; | |
− | + | :'''end;''' | |
</tt> | </tt> | ||
− | Now you can hit F9 to compile and run the server. | + | Now you can hit F9 to compile and run the server. Point your browser to http://127.0.0.1:20080/SimpleLogin.html. You should see the login page displayed in the browser with the server time properly displayed. Enter any value in usercode and password fields (we don't check those values yet) and click the login button. You should see the SimpleMainPage displayed.</li> |
<li>Since the login is not working yet, you can verify that you have an unrestricted access to SimpleMainPage.html: Restart your browser and enter http://127.0.0.1:20080/SimpleMainPage.html. You can see that page normally. This is the expected behaviour now. We will change this to protect the page by the login: the user will be forced to pass by the login process to successfuly access the page. | <li>Since the login is not working yet, you can verify that you have an unrestricted access to SimpleMainPage.html: Restart your browser and enter http://127.0.0.1:20080/SimpleMainPage.html. You can see that page normally. This is the expected behaviour now. We will change this to protect the page by the login: the user will be forced to pass by the login process to successfuly access the page. | ||
</li> | </li> | ||
Line 94: | Line 95: | ||
<li>For now, we will use very simple session data: we will keep usercode and login time so that it is available during the whole session. Create a class like this: | <li>For now, we will use very simple session data: we will keep usercode and login time so that it is available during the whole session. Create a class like this: | ||
<tt> | <tt> | ||
− | + | : TAppSrvSessionData = '''class'''(TWebSessionData) | |
− | + | : '''private''' | |
− | + | :: FUserCode: '''String;''' | |
− | + | :: FLoginTime: TDateTime; | |
− | + | :: FLoggedOn: Boolean; | |
− | + | : '''published''' | |
− | + | :: '''property''' UserCode : '''String''' '''read''' FUserCode '''write''' FUserCode; | |
− | + | :: '''property''' LoginTime : TDateTime '''read''' FLoginTime '''write''' FLoginTime; | |
− | + | :: '''property''' LoggedOn : Boolean '''read''' FLoggedOn '''write''' FLoggedOn; | |
− | + | : '''end;''' | |
</tt> | </tt> | ||
Every data you want in your session data must be declared as published. This is fundamental to have persistance. | Every data you want in your session data must be declared as published. This is fundamental to have persistance. | ||
Line 109: | Line 110: | ||
<li>Register your session data class. Add this code at the end of the unit, just before the final "end.". | <li>Register your session data class. Add this code at the end of the unit, just before the final "end.". | ||
<tt> | <tt> | ||
− | + | :'''initialization''' | |
− | + | :: RegisterClass(TAppSrvSessionData); | |
</tt></li> | </tt></li> | ||
− | <li> Add OverbyteIcsTutWebAppServerSessionData unit to OverbyteIcsTutWebAppServerSimpleLogin. | + | <li> Add OverbyteIcsTutWebAppServerSessionData unit to OverbyteIcsTutWebAppServerSimpleLogin.pas uses clause.</li> |
<li>Change TUrlHandlerSimpleLogin.Execute to create the session when the user request the login page. | <li>Change TUrlHandlerSimpleLogin.Execute to create the session when the user request the login page. | ||
<tt> | <tt> | ||
− | + | :'''procedure''' TUrlHandlerSimpleLogin.Execute; | |
− | + | :'''var''' | |
− | + | :: MySessionData : TAppSrvSessionData; | |
− | + | :: Headers : '''String;''' | |
− | + | :'''begin''' | |
− | + | :: '''if not''' ValidateSession '''then begin''' | |
− | + | ::: Inc(GSessionDataCount); | |
− | + | ::: MySessionData := TAppSrvSessionData.Create(nil); | |
− | + | ::: MySessionData.Name := 'MySessionData' + IntToStr(GSessionDataCount); | |
− | + | ::: Headers := NO_CACHE + CreateSession(’’, 0, MySessionData); | |
− | + | :: '''end''' | |
− | + | :: '''else begin''' | |
− | + | ::: MySessionData := WSession.SessionData as TAppSrvSessionData; | |
− | + | ::: Headers := NO_CACHE; | |
− | + | :: '''end;''' | |
− | + | :: MySessionData.LoggedOn := FALSE; | |
− | + | :<br> | |
− | + | :: AnswerPage(’’, Headers, 'SimpleLogin.html', nil, | |
− | + | :::: ['ServerTime', DateTimeToStr(Now)]); | |
− | + | :: Finish; | |
− | + | :'''end;''' | |
</tt> | </tt> | ||
The code above check for a valid session. If a valid session exists, then it is reused, otherwise a new one is created with new session data. Note that the session must be given a unique name. That's why we used the global integer variable GSessionDataCount which is incremented when a new session is created. | The code above check for a valid session. If a valid session exists, then it is reused, otherwise a new one is created with new session data. Note that the session must be given a unique name. That's why we used the global integer variable GSessionDataCount which is incremented when a new session is created. | ||
Line 141: | Line 142: | ||
<li>Create a global variable like this: | <li>Create a global variable like this: | ||
<tt> | <tt> | ||
− | + | :'''var''' | |
− | + | :: GSessionDataCount : Integer; | |
</tt> | </tt> | ||
</li> | </li> | ||
− | <li> Modify the code in TUrlHandlerDoSimpleLogin to extract usercode and password, and verify them against the user database. Since we are in a simple tutorial, we won't use a database but simply hard code a test for usercode "root" and password "admin". You'll easily adapt the code to fetch | + | <li> Modify the code in TUrlHandlerDoSimpleLogin to extract usercode and password, and verify them against the user database. Since we are in a simple tutorial, we won't use a database but simply hard code a test for usercode "root" and password "admin". You'll easily adapt the code to fetch password from any database and verify validity more realisticly. The code is like this: |
<tt> | <tt> | ||
− | + | :'''procedure''' TUrlHandlerDoSimpleLogin.Execute; | |
− | + | :'''var''' | |
− | + | :: UserCode : '''String;''' | |
− | + | :: Password : '''String;''' | |
− | + | :: Location : '''String;''' | |
− | + | :: SessionData : TAppSrvSessionData; | |
− | + | :'''begin''' | |
− | + | :: '''if not''' ValidateSession '''then''' | |
− | + | ::: Location := '/SimpleLogin.html' | |
− | + | :: '''else begin''' | |
− | + | ::: SessionData := WSession.SessionData '''as''' TAppSrvSessionData; | |
− | + | ::: ExtractURLEncodedValue(Params, 'Password', Password); | |
− | + | ::: ExtractURLEncodedValue(Params, 'UserCode', UserCode); | |
− | + | :<br> | |
− | + | ::: '''if not''' (SameText(UserCode, 'root') '''and''' SameText(Password, 'admin')) '''then begin''' | |
− | + | :::: DeleteSession; | |
− | + | :::: Location := '/SimpleLogin.html'; | |
− | + | ::: '''end''' | |
− | + | ::: '''else begin''' | |
− | + | :::: SessionData.LoginTime := Now; | |
− | + | :::: SessionData.UserCode := UserCode; | |
− | + | :::: SessionData.LoggedOn := TRUE; | |
− | + | :::: Location := '/SimpleMainPage.html'; | |
− | + | ::: '''end;''' | |
− | + | :: '''end;''' | |
− | + | :: AnswerString('302 moved', ’’, | |
− | + | :::: 'Location: ' + Location + #13#10 + NO_CACHE, | |
− | + | :::: '<A HREF="' + Location + '">Click here</A>'); | |
− | + | :: Finish; | |
− | + | :'''end;''' | |
− | |||
</tt> | </tt> | ||
In the above code, we do a serie of checks and then redirect to some page according to the check results. For simplicity we redirect to the SimpleLogin page if anything is wrong or missing and to SimpleMainPage if usercode and password are OK.<br> | In the above code, we do a serie of checks and then redirect to some page according to the check results. For simplicity we redirect to the SimpleLogin page if anything is wrong or missing and to SimpleMainPage if usercode and password are OK.<br> | ||
− | We first check for a valid session. We have a valid session if the user already loaded the SimpleLogin page because we | + | We first check for a valid session. We have a valid session if the user already loaded the SimpleLogin page because we created the session there.<br> |
Then we extract usercode and password from the request parameters (they are comming from the HTML form) and verify their values. If not OK, we redirect to SimpleLogin. If correct, we save UserCode and Password into session data and we set the LoggedOn flag in the session data.<br> | Then we extract usercode and password from the request parameters (they are comming from the HTML form) and verify their values. If not OK, we redirect to SimpleLogin. If correct, we save UserCode and Password into session data and we set the LoggedOn flag in the session data.<br> | ||
The idea behing this LoggedOn flag is that we can have a valid session because the user has seen the login page, but not authenticated the user since the user has not given his usercode and password. In this tutorial, we only create the session from the login page, but as we have seen above, the session could be created anywhere in the application. Probably at many places, we don't care about who is the user and at some place we need to authenticate the user before continuing. We have to make distinction between having a valid session and having an authenticated user. This is common is eCommerce website. We need to authenticate the user only to checkout his shopping cart. The process of shopping can be conduced without actually knowing who is the user. The shopping cart is just part of the session data.<br> | The idea behing this LoggedOn flag is that we can have a valid session because the user has seen the login page, but not authenticated the user since the user has not given his usercode and password. In this tutorial, we only create the session from the login page, but as we have seen above, the session could be created anywhere in the application. Probably at many places, we don't care about who is the user and at some place we need to authenticate the user before continuing. We have to make distinction between having a valid session and having an authenticated user. This is common is eCommerce website. We need to authenticate the user only to checkout his shopping cart. The process of shopping can be conduced without actually knowing who is the user. The shopping cart is just part of the session data.<br> | ||
− | You can hit F9 to compile and run the server. Point you browser to http://127.0.0.1:20080/SimpleLogin.html, enter the usercode "john" and password "abc" | + | You can hit F9 to compile and run the server. Point you browser to http://127.0.0.1:20080/SimpleLogin.html, enter the usercode "john" and password "abc" and click login. You should get back to the SimpleLogin page since the usercode and password is invalid. Try with "root" and "admin". You should see SimpleMainPage. |
</li> | </li> | ||
<li>Now that we have our login process operational, we will protect SimpleMainPage from anonymous access. That is we will force the user to be logged on before showing the actual page. To do that, we simply have to validate the session and check the LoggedOn flag. If not OK, we will redirect the user to the login page.<br> | <li>Now that we have our login process operational, we will protect SimpleMainPage from anonymous access. That is we will force the user to be logged on before showing the actual page. To do that, we simply have to validate the session and check the LoggedOn flag. If not OK, we will redirect the user to the login page.<br> | ||
Modify the code in TUrlHandlerSimpleMainPage.Execute so that it looks like: | Modify the code in TUrlHandlerSimpleMainPage.Execute so that it looks like: | ||
<tt> | <tt> | ||
− | + | :'''procedure''' TUrlHandlerSimpleMainPage.Execute; | |
− | + | :'''var''' | |
− | + | :: Location : '''String;''' | |
− | + | :: SessionData : TAppSrvSessionData; | |
− | + | :'''begin''' | |
− | + | :: Location := ’’; | |
− | + | :: SessionData := nil; | |
− | + | :: '''if not''' ValidateSession '''then''' | |
− | + | ::: Location := '/SimpleLogin.html' | |
− | + | :: '''else begin''' | |
− | + | ::: SessionData := WSession.SessionData '''as''' TAppSrvSessionData; | |
− | + | ::: '''if not''' SessionData.LoggedOn '''then''' | |
− | + | :::: Location := '/SimpleLogin.html'; | |
− | + | :: '''end;''' | |
− | + | :<br> | |
− | + | :: '''if''' Location = ’’ '''then''' | |
− | + | ::: AnswerPage(’’, NO_CACHE, 'SimpleMainPage.html', nil, | |
− | + | ::::: ['UserCode', SessionData.UserCode, | |
− | + | ::::: 'LoginTime', DateTimeToStr(SessionData.LoginTime)]) | |
− | + | :: '''else''' | |
− | + | ::: AnswerString('302 moved', ’’, | |
− | + | ::::: 'Location: ' + Location + #13#10 + NO_CACHE, | |
− | + | ::::: '<A HREF="' + Location + '">Click here</A>'); | |
− | + | :: '''Finish'''; | |
− | + | :end; | |
</tt> | </tt> | ||
− | Hit F9 to compile and run the server.Point | + | Hit F9 to compile and run the server. Point your browser to http://127.0.0.1:20080/SimpleMainPage.html. You should be redirected to SimpleLogin page since you have not yet logged on. Enter a "root" and "admin" as usercode and password. You should see the SimpleMainPage. Navigate somewhere else without closing your browser and then enter http://127.0.0.1:20080/SimpleMainPage.html. This time you should see the page immediately since you already logged in. If you close your browser or close the server program, you'll be again redirected to the login page. Our goal is reached: SimpleMainPage is protected by the login. |
</li> | </li> | ||
<li>There is one more step in the lesson showing how to use session data from a webpage. For simplicity, we will simple count the number of times the user has seen the SimpleMainPage since he logged in.<br> | <li>There is one more step in the lesson showing how to use session data from a webpage. For simplicity, we will simple count the number of times the user has seen the SimpleMainPage since he logged in.<br> | ||
Create a new property MainPageCount in TAppSrvSessionData class: | Create a new property MainPageCount in TAppSrvSessionData class: | ||
<tt> | <tt> | ||
− | + | : '''private''' | |
− | + | :: FMainPageCount : Integer; | |
− | + | : '''published''' | |
− | + | :: '''property''' MainPageCount : Integer '''read''' FMainPageCount '''write''' FMainPageCount; | |
</tt> | </tt> | ||
</li> | </li> | ||
<li>Using the new MainPageCount property is easy. You have direct access to it and do whatever you like. Any change is persistent as long as the session exists. Change the code in TUrlHandlerSimpleMainPage.Execute so that it looks like: | <li>Using the new MainPageCount property is easy. You have direct access to it and do whatever you like. Any change is persistent as long as the session exists. Change the code in TUrlHandlerSimpleMainPage.Execute so that it looks like: | ||
<tt> | <tt> | ||
− | + | : '''if''' Location = ’’ '''then begin''' | |
− | + | :: SessionData.MainPageCount := SessionData.MainPageCount + 1; | |
− | + | :: AnswerPage(’’, NO_CACHE, 'SimpleMainPage.html', nil, | |
− | + | :::: ['UserCode', SessionData.UserCode, | |
− | + | :::: 'LoginTime', DateTimeToStr(SessionData.LoginTime), | |
− | + | :::: 'PageCount', SessionData.MainPageCount]) | |
− | + | : '''end''' | |
</tt> | </tt> | ||
</li> | </li> | ||
− | /ol> | + | </ol> |
<br><br><br> | <br><br><br> | ||
==== Summary ==== | ==== Summary ==== |
Latest revision as of 14:47, 17 May 2009
During this lesson, you will learn how to handle session data.
As you probably know, HTTP is a stateless protocol. This means that each request is totally indepedent from each other as far as HTTP protocol is concerned. A web application is made of a lot of HTTP requests as the user interacts with the application. Because of the stateless nature of the HTTP protocol, your web application cannot rely on HTTP to have each page of the application know it is part of a single application used by a particular user at a given time. It is the application programmer's responsibility to make the application pages seen as a whole application, that is having a page content or behaviour affected by what the user has done previously with the application.
The application programmer has the need to have a data structure, at server side, with values private to a given user for the duration of his activity with the application. Of course since we are building a server application, we can have many users connected at the same time. The developer need a different data instance for each user. We will refer to this kind of data as "session data" in the remaining lessons.
Don't be scary, ICS HttpAppServer component and the user browser will help you achieve that goal.
From the user perspective, a session begin when he starts working with the application and ends when he stop working with the application. Usually the user is presented a login screen at the start of his working session, and simply close his browser or select some "logout" link to end his session.
Login screen is not mandatory. You may want to begin a session with any page from the application and create the session from there without ever asking the user to identify itself. Consider this as a kind of login where actually no usercode/password is checked but everything else is done.
In this lesson we will create a simple login page, create a data structure to hold data for the user session, create a simple dynamic page protected by the login and showing data saved in the session. The later will be the page shown immediately after login.
Steps
- Create a new template containing the login form. We do a dynamic page because frequently login is done from the main page which almost contain dynamic data. For simplicity here our page will only show the current time at server side. Enter the following code into the body part of the document:
- <form action="/DoSimpleLogin.html"
- method="GET"
- enctype="application/x-www-form-urlencoded">
- Usercode <input type="TEXT" name="UserCode"><br>
- Password <input type="PASSWORD" name="Password"><br>
- <input type="SUBMIT" value="Login"><br>
- <br>
- <br>Server time is <#ServerTime>
- </form>
- <form action="/DoSimpleLogin.html"
- Create a new template containing the simple page accessible only after login and showing some session data. Here after is the HTML code you have to enter into the body part of the document:
- If you see this page, then you have a valid session.<br>
- You logged at <#LoginTime><br>
- Your user code is <#UserCode><br>
- You already saw this page <#PageCount> times since you logged in.<br>
- Create a new Delphi unit "OverbyteIcsTutWebAppServerSimpleLogin.pas". You will write the classes TUrlHandlerSimpleLogin and TUrlHandlerDoSimpleLogin containing the code to handle the login process: one class for the login form and one class for the login form action. You will also write the class TUrlHandlerSimpleMainPage to handle the main page, that is the result page shown when login is OK.
- Map the classes to the URLs. In TTutWebAppServerMainForm.FormShow, add the following lines after the similar already there:
- HttpAppSrv1.AddGetHandler('/SimpleLogin.Html', TUrlHandlerSimpleLogin);
- HttpAppSrv1.AddGetHandler('/DoSimpleLogin.html', TUrlHandlerDoSimpleLogin);
- HttpAppSrv1.AddGetHandler('/SimpleMainPage.html', TUrlHandlerSimpleMainPage);
- Create the supporting 3 classes. For now, we just build basic code which doesn't do an actual login. This is just to verify that everything is OK until now. The class declaration part of unit "OverbyteIcsTutWebAppServerSimpleLogin.pas" looks like:
- TUrlHandlerSimpleLogin = class(TUrlHandler)
- public
- procedure Execute; override;
- end;
- TUrlHandlerDoSimpleLogin = class(TUrlHandler)
- public
- procedure Execute; override;
- end;
- TUrlHandlerSimpleMainPage = class(TUrlHandler)
- public
- procedure Execute; override;
- end;
- procedure TUrlHandlerSimpleLogin.Execute;
- begin
- AnswerPage(’’, NO_CACHE, 'SimpleLogin.html', nil,
- ['ServerTime', DateTimeToStr(Now)]);
- Finish;
- AnswerPage(’’, NO_CACHE, 'SimpleLogin.html', nil,
- end;
- procedure TUrlHandlerDoSimpleLogin.Execute;
- var
- Location : String;
- begin
- Location := '/SimpleMainPage.html';
- AnswerString('302 moved', ’’,
- 'Location: ' + Location + #13#10 + NO_CACHE,
- '<A HREF="' + Location + '">Click here</A>');
- Finish;
- end;
- procedure TUrlHandlerSimpleMainPage.Execute;
- begin
- AnswerPage(’’, NO_CACHE, 'SimpleMainPage.html', nil, []);
- Finish;
- end;
- Since the login is not working yet, you can verify that you have an unrestricted access to SimpleMainPage.html: Restart your browser and enter http://127.0.0.1:20080/SimpleMainPage.html. You can see that page normally. This is the expected behaviour now. We will change this to protect the page by the login: the user will be forced to pass by the login process to successfuly access the page.
- Now we will create a new class in a new Delphi unit. This class will be our session data. Do File / New / Other / Delphi Project / Delphi Files / Unit. Save it under the name OverbyteIcsTutWebAppServerSessionData.pas.
- For now, we will use very simple session data: we will keep usercode and login time so that it is available during the whole session. Create a class like this:
- TAppSrvSessionData = class(TWebSessionData)
- private
- FUserCode: String;
- FLoginTime: TDateTime;
- FLoggedOn: Boolean;
- published
- property UserCode : String read FUserCode write FUserCode;
- property LoginTime : TDateTime read FLoginTime write FLoginTime;
- property LoggedOn : Boolean read FLoggedOn write FLoggedOn;
- end;
- Register your session data class. Add this code at the end of the unit, just before the final "end.".
- initialization
- RegisterClass(TAppSrvSessionData);
- initialization
- Add OverbyteIcsTutWebAppServerSessionData unit to OverbyteIcsTutWebAppServerSimpleLogin.pas uses clause.
- Change TUrlHandlerSimpleLogin.Execute to create the session when the user request the login page.
- procedure TUrlHandlerSimpleLogin.Execute;
- var
- MySessionData : TAppSrvSessionData;
- Headers : String;
- begin
- if not ValidateSession then begin
- Inc(GSessionDataCount);
- MySessionData := TAppSrvSessionData.Create(nil);
- MySessionData.Name := 'MySessionData' + IntToStr(GSessionDataCount);
- Headers := NO_CACHE + CreateSession(’’, 0, MySessionData);
- end
- else begin
- MySessionData := WSession.SessionData as TAppSrvSessionData;
- Headers := NO_CACHE;
- end;
- MySessionData.LoggedOn := FALSE;
- if not ValidateSession then begin
- AnswerPage(’’, Headers, 'SimpleLogin.html', nil,
- ['ServerTime', DateTimeToStr(Now)]);
- Finish;
- AnswerPage(’’, Headers, 'SimpleLogin.html', nil,
- end;
- Create a global variable like this:
- var
- GSessionDataCount : Integer;
- var
- Modify the code in TUrlHandlerDoSimpleLogin to extract usercode and password, and verify them against the user database. Since we are in a simple tutorial, we won't use a database but simply hard code a test for usercode "root" and password "admin". You'll easily adapt the code to fetch password from any database and verify validity more realisticly. The code is like this:
- procedure TUrlHandlerDoSimpleLogin.Execute;
- var
- UserCode : String;
- Password : String;
- Location : String;
- SessionData : TAppSrvSessionData;
- begin
- if not ValidateSession then
- Location := '/SimpleLogin.html'
- else begin
- SessionData := WSession.SessionData as TAppSrvSessionData;
- ExtractURLEncodedValue(Params, 'Password', Password);
- ExtractURLEncodedValue(Params, 'UserCode', UserCode);
- if not ValidateSession then
- if not (SameText(UserCode, 'root') and SameText(Password, 'admin')) then begin
- DeleteSession;
- Location := '/SimpleLogin.html';
- end
- else begin
- SessionData.LoginTime := Now;
- SessionData.UserCode := UserCode;
- SessionData.LoggedOn := TRUE;
- Location := '/SimpleMainPage.html';
- end;
- if not (SameText(UserCode, 'root') and SameText(Password, 'admin')) then begin
- end;
- AnswerString('302 moved', ’’,
- 'Location: ' + Location + #13#10 + NO_CACHE,
- '<A HREF="' + Location + '">Click here</A>');
- Finish;
- end;
We first check for a valid session. We have a valid session if the user already loaded the SimpleLogin page because we created the session there.
Then we extract usercode and password from the request parameters (they are comming from the HTML form) and verify their values. If not OK, we redirect to SimpleLogin. If correct, we save UserCode and Password into session data and we set the LoggedOn flag in the session data.
The idea behing this LoggedOn flag is that we can have a valid session because the user has seen the login page, but not authenticated the user since the user has not given his usercode and password. In this tutorial, we only create the session from the login page, but as we have seen above, the session could be created anywhere in the application. Probably at many places, we don't care about who is the user and at some place we need to authenticate the user before continuing. We have to make distinction between having a valid session and having an authenticated user. This is common is eCommerce website. We need to authenticate the user only to checkout his shopping cart. The process of shopping can be conduced without actually knowing who is the user. The shopping cart is just part of the session data.
You can hit F9 to compile and run the server. Point you browser to http://127.0.0.1:20080/SimpleLogin.html, enter the usercode "john" and password "abc" and click login. You should get back to the SimpleLogin page since the usercode and password is invalid. Try with "root" and "admin". You should see SimpleMainPage. - Now that we have our login process operational, we will protect SimpleMainPage from anonymous access. That is we will force the user to be logged on before showing the actual page. To do that, we simply have to validate the session and check the LoggedOn flag. If not OK, we will redirect the user to the login page.
Modify the code in TUrlHandlerSimpleMainPage.Execute so that it looks like:- procedure TUrlHandlerSimpleMainPage.Execute;
- var
- Location : String;
- SessionData : TAppSrvSessionData;
- begin
- Location := ’’;
- SessionData := nil;
- if not ValidateSession then
- Location := '/SimpleLogin.html'
- else begin
- SessionData := WSession.SessionData as TAppSrvSessionData;
- if not SessionData.LoggedOn then
- Location := '/SimpleLogin.html';
- end;
- if Location = ’’ then
- AnswerPage(’’, NO_CACHE, 'SimpleMainPage.html', nil,
- ['UserCode', SessionData.UserCode,
- 'LoginTime', DateTimeToStr(SessionData.LoginTime)])
- AnswerPage(’’, NO_CACHE, 'SimpleMainPage.html', nil,
- else
- AnswerString('302 moved', ’’,
- 'Location: ' + Location + #13#10 + NO_CACHE,
- '<A HREF="' + Location + '">Click here</A>');
- AnswerString('302 moved', ’’,
- Finish;
- if Location = ’’ then
- end;
- There is one more step in the lesson showing how to use session data from a webpage. For simplicity, we will simple count the number of times the user has seen the SimpleMainPage since he logged in.
Create a new property MainPageCount in TAppSrvSessionData class:- private
- FMainPageCount : Integer;
- published
- property MainPageCount : Integer read FMainPageCount write FMainPageCount;
- private
- Using the new MainPageCount property is easy. You have direct access to it and do whatever you like. Any change is persistent as long as the session exists. Change the code in TUrlHandlerSimpleMainPage.Execute so that it looks like:
- if Location = ’’ then begin
- SessionData.MainPageCount := SessionData.MainPageCount + 1;
- AnswerPage(’’, NO_CACHE, 'SimpleMainPage.html', nil,
- ['UserCode', SessionData.UserCode,
- 'LoginTime', DateTimeToStr(SessionData.LoginTime),
- 'PageCount', SessionData.MainPageCount])
- end
- if Location = ’’ then begin
Summary
In this lesson you learned that session data is persistent data during the time the user intercat with your website. You learend how to create a class to define and store your session data. You learned how to create a session and use it to remember a user has been authenticated. You also used it to count the number of access for a given user.
Next lesson: Dynamic images
Previous lesson: Relocation
Tutorial presentation: TutWebAppServer