Visit our Sponsor   Visit our Sponsor
delphi3000.com - the free delphi knowledge platform
delphi3000.com - the free delphi knowledge platform
500 Users Online NOW
Have a look at your member-status

connecting people's knowledge


  - Recent ArticlesRSS feed for Recent Articles on delphi3000.com
  - List of All Articles
  - Top Viewed Articles
  - Articles (+Attachem.)
  - Articles Of Interest
  - Categories
  - Top Uploader
  - Search
  - Index

  - My Home
  - Submit an Article
  - My Articles
  - My Personal Data
  - My Bookmarks
  - Activities
  - Login/Logout

  - Sign Up
  - Why Sign Up
  - Newsletter

  - Press
  - Advertise

  - Contact
  - Feedback





Community
Borland
ClubeDelphi
Dr. Bob
UK-BUG
Delphi Meetings
Planeta Delphi







Startblatt.de






Share this article with friendsShare this article with friends
Rate this articleRate this article - to keep the quality of delphi3000.com !
Comment this article or read through previous comments (4)


Building Real-Life Web ServicesFormat this article printer-friendly!Bookmark function is only available for registered users!
Product:
Delphi all versions
Category:
Internet / Web
Skill Level:
Scoring:
Last Update:
12/09/2002
Search Keys:
delphi delphi3000 article borland vcl code-snippet Web-Service Cipher Encryption HTTP Internet Intranet XML SOAP Stateless Statefull Authorisation Logon Logoff
Times Scored:
3
Visits:
10032
Uploader: Marc Hoffmann
Company:
Reference: N/A
 
Question/Problem/Abstract:
How to build a 'real-world' Web-Service with mixed method calls, data-aware connections, user-authorisation and session support
Answer:



We all knowing about the new technique hype called "SOAP" (Simple Object
Acess Protocal) and "Web-Services". If you are not familiar with this
technologies, please refer to the following Delphi3000 articles:

SOAP:        
http://www.delphi3000.com/articles/article_2985.asp

WebServices:
http://www.delphi3000.com/articles/article_2301.asp

There are several sites available with a lot of 'ready-to-use' Web-Services
(e.g. http://www.xmethods.org/) which are simple to include in your own
applications. Most of these services have only one to a few methods to call
and all are stateless and need no authorisation. That's fine for the purposes
they are required, but what's if you plan to create a Client-Server (i.e.
n-tier) application using the newest technique of Web-Services? Well, you're
right if you say "Why should I use a Web-Service, I can do the same using
DCOM or by creating a Data/WebSnap application!". That's the same question
I thought about, but i found the following advantages using Web-Services
instead:

1. I have a 'well-designed' interface (WSDL), which can be directly used in
   the application (e.g. (RIA as IMyWebService).GetEmployees; ) instead of
   the abstract 'IAppServer' interface of DCOM applications.
  
2. I'm not settled on using Delphi, if I want to talk with the service. It's
   also possible to use Python, Perl, C# or any other programming language
   which supports Web-Services (e.g. a HTML Page).
  
3. I don't need to create a complex server application which must handle
   my calls (thread-safe) but can use commercial servers like Apache or
   Microsoft's IIS.
  
4. Computers running the client software don't need special component updates
   (like DCOM) except a simple TCP/IP connection.
  
5. If you plan to make your Server (= Web-Service) World-Wide available, just
   send it to your Internet Provider. You don't need to install special proto-
   cols except HTTP.
  
6. It's very simple to encrypt your data send over the LAN/W-LAN/WWW. You only
   need a SSL certificate (over HTTPS)!
  
As I mentiod earlier, there are two critical disadvantages using Web-Services:

1. Web-Services are stateless.
2. Web-Services are authorisation free.

An other major disadvantage of Web-Services is that you are not able to mix
method calls (knowing from 'IAppServer' (DCOM) applications) and data aware
connections (using the 'TSOAPConnection' component).

In this article I'll try to show you some workarounds and techniques you
can use to create statefull Web-Services with an authorisation support.

Scenario

You want to create a (new) Client-Server application using a Web-Service
where the client can call several (data-aware) methods implemented by the
server. In this case, the server is the Apache or IIS for example and the
client is a native executeable (.exe). The Web-Service itself is attached
to the Server as a dynamic-link-library (.dll) for best performance.

Tip: While developing the service, it's also possible to create a standalone
     or WebApp executeable for better debugging purposes!
    
   o--------o           o------------o    o-------------o    o----------o
   | Client |-->[WWW]-->| Web-Server |<-->| Web-Service |<-->| Database |
   o--------o           o------------o    o-------------o    o----------o
                                              

Security

Before a client can call a method implemented by the Web-Service, a valid
user should be authorized by the system. For a simple Web-Service this
could be ignored but if you plan to create a complex Client-Server
application where critical data were present, an authorisation mechanism is
indispensable. If the user was successfully logged on, the Web-Service method
returns a so-called 'ticket'. The ticket is a simple encrypted string
containing the following data:

  - Sign (simple ticket header)
  - UserId (identifies the user in the database)    
  - ApplicationId (should be used to identifies various client applications)
  - TimeStamp (actual date and time when the ticket was created)

Each Web-Service method must pass this ticket in one of his calling parameters:

  Example:
    function CountRegisteredUsers(var Ticket: string): Integer; stdcall;
    
All available methods validates the passed ticket and will raise an exception
if the ticket is invalid. Otherwise, a new ticket is created, registered and
returned. A ticket is invalid in the following cases:

  1. The ticket is not registered as the next valid one.
  2. The ticket is older than two hours.
  
Because a ticket is only valid for one (the next) transaction, it is compareable
with a TAN used by Online-Banking.

   |Client|->[Logon: string]->{Ticket}
                                 |
   |Client|->[GetEmployees(var Ticket): string]
                                 |
   ......|Client|->[Logoff(var Ticket): Boolean]
  

Web-Service Interface (Extract)

  { IMyAuthorisationService }

    IMyAuthorisationService = interface(IInvokable)
    ['{C21C8D28-4C9A-488B-AC36-FDCD54846D1C}']
      function Logon(const AppId, UserName, UserPwd: string;
        var Ticket: string): Boolean; stdcall;
      function Logoff(var Ticket: string): Boolean; stdcall;
      function AddData(var Ticket: string;
        const Key: string; const Data: Variant): Boolean; stdcall;
      function DelData(var Ticket, Key: string): Boolean; stdcall;
      function GetData(var Ticket, Key: string): Variant; stdcall;
    end;

  { IMyDataService }

    IMyDataService = interface(IInvokable)
    ['{1C420CF6-07A3-4430-9227-068EC39C1702}']
      function GetEmployees(var Ticket: string): string; stdcall;
    end;

    
The 'IMyAuthorisationService' manages the login/logoff mechanism and calls
private methods to create and check the tickets. The service also implements
the session support (AddData, DelData, GetData) which simply uses the 'AppId'
included in the ticket to locate the database rows containing the data.
The 'IMyDataService' manages all data-aware connections to the database like
'GetEmployees' and uses a local 'IMyAuthorisation' instance to validate the
passed tickets.

Web-Service Implementation (Extract)

CreateTicket:

  function TMyAuthorisationService.CreateTicket(const AppId,
    UserID: string): string;
  var
    Cipher: TCipher_Blowfish;
  begin
    { create cipher }
    Cipher := TCipher_Blowfish.Create(THash_MD5.CalcString(CipherPwd, nil,
      fmtMIME64), nil);

    { set cipher mode }
    Cipher.Mode := cmCTS;

    { return encoded Ticket

      Ticket-format: "sXp", "","",""

                        sXp - sign
                        xxx - actual timestamp
                        yyy - application id
                        zzz - user id           }
    Result := Cipher.CodeString(Format('"sXp", "%g", "%s", "%s"',
      [Now, AppId, UserId]), paEncode, fmtMIME64);
  end;

  
The 'CreateTicket' method simply creates the encrypted ticket. I have used
the cipher units from Hagen Reddmann (Version 3.0).

Logon:

  function TSXPAuthorisationService.Logon(const AppId, UserName, UserPwd: string;
    var Ticket: string): Boolean;
  var
    UserId: string;
  begin
    { set empty Ticket as default }
    Ticket := '';
    
    { check 'UserName' and 'UserPwd' and return valud 'UserId' }
    Result := CheckUser(UserName, UserPwd, UserId);
    if not Result then
      Exit;  // UserId not found = UserName or UserPwd invalid!!!

    { create first Ticket from UserId }
    Ticket := CreateTicket(AppId, UserId);
  end;

  
This simple logon method doesn't include the database logic which should
be implemented by yourself.

GetEmployees:

  function TMyDataService.GetEmployees(var Ticket: string): string;
  const
    FileName =
      'C:\Employees.xml';
  var
    Line: string;
    Stream: TMemoryStream;
  begin
    { set default }
    Result := '';

    { check Ticket and create new one on success }
    if not AuthorisationService.CheckTicket(Ticket) then
      raise Exception.CreateFmt('GetEmployees-Error: Invalid Ticket(%s)', [Ticket]);

    { simply load a xml file from disk. in real-life application this should
      come from a database! }
    Stream := TMemoryStream.Create;
    try
      Stream.LoadFromFile(FileName);
      Line := PChar(Stream.Memory);
      Result := MimeEncodeString(Line);
    finally
      Stream.Free;
    end;
  end;

  
This method demonstrates a possible solutions on how to mix method calls
with data-aware connections. As I mentiod earlier, one advantage of Web-
Services is the independence of the programming language. If you use the
DataSnap technologie, you have to use the TSOAPConnection component and
Data-Providers which are only compatible with Delphi, limits the possible
DataModules to only one instance and don't allow mixed method calls except
the damn IAppServer interface. If you implement data-aware methods which
send it's datapackets in an embedded XML string, you can use all benefits
of Web-Services. To translate a datapacket to and from a XML string, you can
simply use a tool coming with Delphi called 'XMLMapper' (look at Delphi's
online help to get further informations).

Client (Extract)

  procedure TfrmMain.Button1Click(Sender: TObject);
  var
    EmployeesXML: string;
  begin
    { logon user }
    if not (RIOAs as IMyAuthorisationService).Logon(
      '1234',             // Unique ApplicationId
      edtUserName.Text,   // User Name
      edtUserPwd.Text,    // User Password
      FTicket ) then      // Global defined Ticket variable
    Exit;

    { get Employees }
    EmployeesXML := (RIODs as IMyDataService).GetEmployees(FTicket);
    
    ...tranform XML to datapackets...
  end;

  

Conclusion

Web-Services are a way to create new Client-Server applications with a lot
of benefits. Delphi itself comes with a wide range of development tools
and components to make us (the developers) the way of SOAP as easy as
possible. Because SOAP and Web-Services are to be in its infancy I'm taut
what happens in the future.





Please rate this article!
Skill level:
BeginnerExpert

Useful:
No!Very!

Overall rating:
PoorExcellent



Comments to this article
Write a new comment
Using Delphi components
    R. Aguilo (Dec 16 2002 5:39PM)

Great article!

  Tha's all ok. But how would be to pass the "Ticket" parameter when oppening a ClientDataset connected with a SoapServer Datamodule?
  This communication is made by the Delphi components and we cannot interfere with it, so where goes the Ticket here?

Respond

RE: Using Delphi components
Marc Hoffmann (Dec 17 2002 10:21AM)

You're right - that's the major disadvantage of WebServices under Delphi currently: It's not possible to mix TSoapConnections (dealing with TDataSet components) with normal method calls bundled with session storing and/or authorisation mechanism. That's why I have demonstrated the use of XML instead (with the help of XMLMapper) - so you're free to use any other language to communicate with database rows.
Respond

RE: RE: Using Delphi components
Marc Hoffmann (Dec 20 2002 10:50AM)

Hi again!

I've taken a look on what's new under Delphi7 in terms of WebServices but there is nothing special! The only new technologie you can use to make authorisations or session management are so called SOAP Headers. You may put additional informations (like UserName and Password) to the header, but the major disadvantage is, that every client call must read and verify this header (~ 10 lines of code for every call) 8-((

BUT: Microsoft has released a new security tool (WSE 1.0 = WebServices Enhancements) for Visual Studio.NET. Perhaps, this service will be available for Delphi.NET (Gallileo) next year!!
Respond

Using Delphi components
alexander shagin (Sep 21 2006 2:42PM)

The decision of this problem can be found in http://www.vspu.ru/is/sites/wss/wsdelphi.htm.
Respond














 
Sign up to consume product discounts for Bronze memberships !

read more


  Visit our Sponsor

 

  Community Ad of
Peganza
 
   














 







     
  Copyright © 2000 - 2007 delphi3000.com - All rights reserved. Terms of use. || Privacy
delphi3000.com is a service by bluestep.com IT-Services GmbH (Vienna)