Salve, salve Delphianos!
Neste pequeno artigo demonstrarei algumas técnicas para criação de Sessions para aplicações baseadas em WebBroker.
Mas para que servem as Sessions? Uma resposta curta e grossa: serializar usuários em aplicações Web! Xiiiiiii Facunte, agora piorou, explique melhor!?
Vejamos dois exemplos:
e-Commerce
Serializamos clientes através de Sessions, onde todas as ações devem ser controladas, como adição de produtos no carrinho de compras, processo de identificação e pagamento.
Veja o esquema:
- Internauta entra na loja virtual e recebe uma sessão única: A01-FF1;
- Internauta escolhe produto e coloca na sua cesta. Sua cesta possui a mesma identificação da sessão: A01-FF1;
- Internauta entra na área de identificação e o sistema vincula seu cadastro à sessão: A01-FF1;
- Internauta finaliza processo de compra, e o sistema identifica suas opções através da sessão: A01-FF1.
iBanking
Utilizada no processo de identificação do cliente, bem como em todas as ações realizadas durante a sessão (transferência entre contas, pagamentos, extrato, etc).
- Cliente entra no site do banco e recebe uma sessão única: BBC-AAX;
- Cliente informa dados de sua conta e realiza login do iBanking. O sistema vincula a conta logada na sessão BBC-AAX. Para maior segurança, além de um servidor seguro, a sessão criada possui algumas informações do internauta, como por exemplo, o número do IP (obviamente criptografado);
- Cliente realiza todas as transações de maneira segura, onde o sistema sempre verifica as informações da sessão BBC-AAX, e da máquina do usuário;
- Cliente finaliza processo, e o sistema libera a sessão BBC-AAX.
Mas chega de apresentações! Chegou a hora de por a mão na massa.
Listagem 1: Função randômica para criação de sessões
function NewIDRamdom:String;
begin
Result:= IntToHex(Random($ffffffff),8)+'-'+
IntToHex(Random($ffffffff),8)+'-'+
IntToHex(Random($ffffffff),8)+'-'+
IntToHex(Random($ffffffff),8);
end;
Listagem 2: Função que utiliza diversos dados, como data e hora
function NewIDPlus:String;
var
iAno, iMes, iDia, iHora, iMinuto, iSegundo, iMSegundo: Word;
begin
DecodeDate(Now, iAno, iMes, iDia);
DecodeTime(Now, iHor, iMin, iSeg, iMSeg);
Result:= IntToHex(iAno,3)+'-'+
IntToHex(iMes,2)+'-'+
IntToHex(iDia,2)+'-'+
IntToHex(iMSegundo,3)+'-'+
IntToHex(iSegundo,2)+'-'+
IntToHex(iMinuto,2)+'-'+
IntToHex(iHora,2);
end;
Listagem 3: Função que retorna uma GUID
function NewIdGuid:String;
var
MinhaGUID:TGUID;
begin
CoCreateGuid(MinhaGuid);
Result:=GUIDToString(MinhaGuid);
end;
Nesse exemplo, necessitamos de duas units: ActiveX e COMOBJ.
Exemplos de utilização:
Variável de sessão:
var
IDSession: String;
Variável Internet, que identifica o usuário:
<input type=”hidden” name=”ID” value=”...>
http://servidor/aplicacao.dll/login?ID=....
No evento OnBeforeDispatch insira o código que segue:
begin
if Request.ContentFields.values[‘ID’]=’’ then
IDSession := NewIdGuid;
end;
E no parser do seu Producer (onHTMLTag), coloque o código que segue:
begin
if TagString=’ID’ then
ReplaceText:=IDSession;
end;
Em todas as páginas você deverá inserir a Tag transparente <#ID>, exemplo:
<input type=”hidden” name=”ID” value=”<#ID>”>
http://servidor/aplicacao.dll/login?ID=<#ID>
<form name=”cliente” action=”/apl.dll/confirma?ID=<#ID>”>
A seguir demonstrarei como criar e administrar sessions para o saudoso WebBroker. Crie uma nova aplicação baseada na tecnologia WebBroker, através das opções File/New.../WebServer Application. Selecione qualquer uma das opções (CGI, ISAPI, Apache...). Salve o projeto com o nome Sessoes.DPR e a unit como un_sessoes.pas. Com o objetivo de criar um projeto profissional, estaremos utilizando um banco de dados para armazenar informações sobre as sessions.
Veja a estrutura das tabelas:
Tabela de Sessão | ||
Campo | Tipo | |
SSS_ID | Varchar | PK |
SSS_USU_Id | Int | |
SSS_DataHoraUltAtualizacao | DateTime | |
SSS_IP | Varchar |
Tabela Usuário | ||
Campo | Tipo | |
USU_ID | Int | PK |
USU_Nome | Varchar | |
USU_Senha | Varchar |
A estrutura modelo é baseada em SQL Server, mas você poderá adaptar facilmente em qualquer SGDB. Em nosso projeto utilizaremos ADO, mas você poderá utilizar a tecnologia dbExpress, ou até mesmo o BDE. Prosseguindo com o projeto, insira os seguintes objetos.
AdoConnection | |
Name | dbconexao |
LoginPrompt | False |
ConnectionString | Faça a conexão com a sua base de dados. |
AdoDataSet | |
Name | QrySessao |
Connection | dbConexao |
Neste ponto implementaremos as rotinas comuns para o tratamento de sessions. Na seção public, declare a função NewIdGuid (responsável pela criação de um novo ID), e faça a implementação como segue:
function NewIdGuid:String;
var
MinhaGUID:TGUID;
sIDInterno:String;
begin
CoCreateGuid(MinhaGuid);
sIDInterno:= GUIDToString(MinhaGuid);
// Adiciona a sessão no banco de dados
dbConexao.Execute(‘Insert into Sessao values(‘+
QuotedStr(sIDInterno)+’,’+
‘0,’+
QuotedStr(DateTimetoStr(Now))+’,’+
QuotedStr(Request.RemoteAddr)+’)’,
cmdText, [eoExecuteNoRecords]);
Result:= sIDInterno;
end;
Vamos analisar o código:
Criamos uma chave única (ID) e setamos para sIDInterno. CoCreateGuid(MinhaGuid);
sIDInterno:= GUIDToString(MinhaGuid);
Inserimos o novo ID no banco de dados, e transmitimos os seguintes parâmetros:
dbConexao.Execute(‘Insert into Sessao values(‘+
novo ID, QuotedStr(sIDInterno)+’,’+
Usuário = 0. Isso indica que ainda não efetuou login.
‘0,’+
Data e Hora Atual.
QuotedStr(DateTimetoStr(Now))+’,’+
E o IP do usuário.
QuotedStr(Request.RemoteAddr)+’)’,
Com isso apenas adicionamos a chave no banco de dados. Na cláusula uses, acrescente as units ComObj e ActiveX.
Na seção protected, declare a seguinte variável:
protected
sID: String;
A variável sID será responsável pelo controle do session ID. Neste ponto criaremos a função que irá checar a existência de um ID e retornar o valor do mesmo.
function ChecaId:String;
var
sIDinterno:string;
begin
// verifica se existe ID (post)
sIDInterno:=Request.ContentFields.Values[‘ID’];
if sIDInterno=’’ then
begin
// verifica se existe ID (get)
sIDInterno:=Request.QueryFields.Values[‘ID’];
if sIDInterno=’’ then
begin
// não existe ID, cria um novo;
sIDInterno:=NewIdGuid;
end;
end;
// Retorna ID
Result:=sIDInterno;
end;
Vamos analisar a lógica da função:
Neste ponto verificamos a existência do campo ID num FORM com método POST.
// verifica se existe ID (post)
sIDInterno:=Request.ContentFields.Values[‘ID’];
if sIDInterno=’’ then
begin
Em caso negativo, verifica-se a existência do campo ID num método GET, e se persistir, criamos um novo ID.
Este é ponto mais importante da sua aplicação. Necessitamos transmitir o campo ID em todas as ações, seja dentro de um form ou através de uma URL, exemplos:
1. http://servidor/aplicacao.dll?ID=<#ID>
2. <form method=post action=”aplicação.dll”>
<input type=hidden name=”ID” value=”<#ID>”>
Repare que em ambos os casos utilizamos a tag transparente . Com isso deveremos realizar o parser em todos os objetos Producers através do evento OnHtmlTag;
Amigos, em todos os producers (normalmente centralizo minhas aplicações num único Producer), vocês deverão realizar o parser. Veja o exemplo:
Evento OnHtmlTag.
If TagString=’ID’ then
begin
ReplaceText:=ChecaId;
end;
Neste ponto criaremos as funções para efetuar LogIn e LogOut além de uma função que verifica se o usuário está logado no sistema. Como exemplo, crie um HTML com os campos: usuario e senha:
<html>
<body>
<form method=”post” action=”login”>
<input type=”HIDDEN” name=”ID” value=”<#ID>”
Usuario <input type=TEXT name=usuario><BR>
Senha <input type=password name=senha><BR>
<input type=submit value=”confirma”>
</form>
</body>
</html>
A função Login retorna uma expressão lógica com o resultado da operação.
function Login:boolean;
var
sUsuario, sSenha, sURL:string;
begin
sID:=ChecaId;
sUsuario:=Request.ContentFields.Values[‘usuario’];
sSenha:=Request.ContentFields.Values[‘senha’];
with QrySessao do
begin
Close;
CommandText:=’Select usu_id, usu_nome, usu_senha from usuario whereUSU_NOME=’+QuotedStr(sUsuario);
Open;
If Empty or FieldByName(‘usu_senha’).AsString<>sSenha then
begin
Result:=False;
end
Else
begin
// Atualiza
dbConexao.Execute(‘UpDate Sessao set ‘+ ‘SSS_Usu_id=’+FieldByName(‘usu_id’).AsString+’, ‘+ ‘SSS_DataUltAtualizacao=’+QuotedStr(DateTimetoStr(Now))+
‘,’+ QuotedStr(Request.RemoteAddr)+’)’,
cmdText, [eoExecuteNoRecords]);
Result:=True;
End;
Close;
end;
end;
A função Login, verifica a existência do usuário e a validade da senha. Em caso positivo, a tabela Sessao é atualizada com os dados do usuário(ID, Código do Usuário e Data/Hora atualização). Com isso autenticamos o usuário em nossa base.Além disso utilizamos a variável sURL que contém a URL de destino após o usuário efetuar o Login no sistema. Exemplo:
- Usuário tenta acessar uma área restrita
Função LogOut
A função LogOut retira o vínculo do usuário na tabela Sessão. Devemos passar o parâmetro ID para que a operação seja realizadda.
function Logout(ID:String):boolean;
begin
try
dbConexao.Execute(‘Update Sessao set SSS_Usu_id=0 where SSS_ID = ‘+ID,cmdText, [eoExecuteNoRecords]);
Result:=True;
except
Result:=False;
end;
end;
Função VerificaLogin
Esta função verifica se o usuário está logado no sistema através da autenticação e do tempo. Estou prevendo 1 dia, mas você poderá configurar de acordo com a sua necessidade (1 hora, 30 minutos, 10 minutos, etc).
function VerificaLogin(ID):boolean;
begin
with QrySessao do
begin
Close;
CommandText:=’select SSS_USU_ID, SSS_DataHoraUltAtualizacao from Sessao where SSS_ID=’+ID;
Open;
If (Empty) or
(FieldByName(‘SSS_USU_ID’).AsInteger=0) or
(FieldByName(‘SSS_DataHoraUltAtualizacao’).Value<Now-1
then
Result:=False
Else
Result:=True;
end;
end;
Utilizando as funções
Em todas as ações que necessitam de Login, você deverá inserir o código abaixo:
if not(VerificaLogin(sID)) then
begin
// coloque aqui o código que chama a sua tela de LOGIN
// exemplo:
Response.Content:=’’;
end;
Sugiro a criação de uma rotina de expurgo para eliminar todos os registros da tabela Sessao com atualização inferior a 2 dias.
Espero que tenham compreendido o artigo e que possam aproveitá-lo em seus sistemas.
Forte abraço.