![]() |
||||||||
|
|
||||||||
|
| ||||||||


| com exemplos em VB |
| Componente para deixar forms em Vb semelhantes às telas do winnamp |
| Componente para colocar sua aplicação VB no Systray |
| Componente para transformar sua aplicação VB em serviço |
| Ferramentas úteis para quem usa Olap Server |
| |

|
||||||||||||||||||||||||||||||||||||||||||||||||||||
Pesquisa personalizada
Truques de autenticação : Login unico e derivados O sistema de providers do ASP.NET (veja o artigo em http://www.bufaloinfo.com.br/artigos/coluna32.asp) simplificou muito o trabalho de desenvolvimento de um site, possibilitando que os desenvolvedores se preocupem com questões mais avançadas no desenvolvimento. Então vamos ver alguns truques com o sistema de autenticação do ASP.NET, em uma série de artigos sobre este tema. Neste artigo vamos ver como fazer um login único no site e alguns resultados derivados disso. Por default, o ASP.NET não controla quantas vezes o mesmo usuário loga-se no site, então uma única conta de usuário poderia ser utilizada para fazer login em diversos micros, em diversos locais e para diversas pessoas ao mesmo tempo. As vezes, porém, torna-se necessário garantir que uma conta de usuário possa logar-se apenas de um único local na aplicação. Para isso podemos utilizar alguns truques com o sistema de autenticação no ASP.NET Primeiro, precisamos contextualizar o que vamos fazer. Estamos falando do sistema de Membership que controla o cadastramento e autenticação dos usuários. Estamos falando de formsAuthentication, pois com os demais tipos de autenticação o Membership não é utilizado. O local onde estarão os dados do usuário não importa, o sistema de Membership nos permite encaixar qualquer provider, incluindo providers personalizados, independentemente do que demonstrarei no artigo. Para controlarmos o login do usuário precisamos observar cuidadosamente a forma como o usuário é identificado pelo sistema de autenticação do ASP.NET. Existem 3 identificações do usuário, 1 fixa e 2 temporárias :
Então, para começar, precisamos montar um cenário simples para fazermos nossos testes de autenticação. 1) Crie o banco de autenticação. Fica a seu critério cria-lo em um servidor ou utilizando o ASP.NET Configuration com SQL Server Express. Veja detalhes em http://www.bufaloinfo.com.br/artigos/coluna32.asp 2) Na página default.aspx, monte uma gridview simples, apenas para ilustrar a página.
3) Configure o sistema de membership no web.config <membership defaultProvider="MeuProvider" > <providers> <add name="MeuProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="ASPNETSecurity" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="true" applicationName="/" requiresUniqueEmail="false" passwordFormat="Hashed" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="7" minRequiredNonalphanumericCharacters="1" passwordAttemptWindow="10" passwordStrengthRegularExpression="" /> </providers> </membership>
<connectionStrings> <add name="ASPNETSecurity" connectionString="Data Source=.;Initial Catalog=aspnetdb;User ID=sa" providerName="System.Data.SqlClient" /> </connectionStrings>
4) Crie uma página login.aspx. Insira um webControl de login, faça um autoformat.
5) Utilize o menu website->asp.net configuration para cadastrar usuários. Cadastre pelo menos 2 usuários, "ciclano" e "fulano", "fulano" utilizado como um administrador, mas isso é opcional. 6) Faça um teste simples na aplicação
Agora que temos um site simples com autenticação, vamos começar a implementar nossa solução para manter um login único no site. A idéia é bem simples : Podemos gravar temporariamente no "campo" Comment do MembershipUser informações adicionais sobre o login do usuário, como por exemplo um código único. Ao mesmo tempo podemos gravar o código no Tiket de autenticação e a cada requisição checar se os dois, código e Ticket, conferem. Os resultados que podemos obter com isso variam de acordo com o algoritimo que implementarmos, veja algumas opções :
Vamos começar preenchendo o campo Comment e o ticket de autenticação. Isso precisa ser feito no momento em que o usuário se loga, então vamos utilizar o evento LoggedIn do WebControl Login. Protected Sub Login1_LoggedIn(ByVal sender As Object, ByVal e As System.EventArgs) Handles Login1.LoggedIn
'Alteração do cookie Dim tik2 As FormsAuthenticationTicket Dim cookie As HttpCookie = Response.Cookies(FormsAuthentication.FormsCookieName)
Dim g As Guid = Guid.NewGuid() tik2 = FormsAuthentication.Decrypt(cookie.Value)
Dim user As MembershipUser
'Atualização do usuário no banco user = Membership.GetUser(Login1.UserName)
user.Comment = "timeout;" & tik2.Expiration & "|GUID;" & g.ToString()
Membership.UpdateUser(user)
Dim tik As New FormsAuthenticationTicket(tik2.Version, tik2.Name, tik2.IssueDate, tik2.Expiration, tik2.IsPersistent, g.ToString())
Dim enctik As String = FormsAuthentication.Encrypt(tik) cookie.Value = enctik End Sub
Muitas linhas deste código merecem especial atenção, vamos analisa-las. Na linha :
Na linha : tik2 = FormsAuthentication.Decrypt(cookie.Value)
Na linha : user = Membership.GetUser(Login1.UserName)
Nas linhas : user.Comment = "timeout;" & tik2.Expiration & "|GUID;" & g.ToString()
Membership.UpdateUser(user)
Nas linhas : Dim tik As New FormsAuthenticationTicket(tik2.Version, tik2.Name, tik2.IssueDate, tik2.Expiration, tik2.IsPersistent, g.ToString())
Dim enctik As String = FormsAuthentication.Encrypt(tik)
Na linha : cookie.Value = enctik
Com o evento LoggedIn montamos a estrutura básica para controlar o login do usuário. Agora precisamos começar a utilizar estas informações para gerar o resultado que desejamos. Da forma como está o usuário consegue logar-se em diversos locais distintos, mas isso pode ser identificado. O local do último login estará identificado por um GUID no campo "comment", enquanto que os logins anteriores terão um GUID diferente guardado em seus tickets de autenticação. Então vamos começar de forma simples, identificando essa diferença nos GUIDs e invalidando os logins. Para fazer isso precisamos lidar com eventos da aplicação web. Existem duas formas distintas de fazer isso : Podemos programar o arquivo global.asax ou criar um httpModule. O global.asax é como uma versão nova do global.asa, do antigo ASP. Uma forma simples de programar eventos a nível de aplicação, para todo o site. O global.asax tem cada vez mais sido substituido pela implementação de httpModules. Um httpModule, assim como o global.asax, intercepta os eventso de uma aplicação web. A diferença é que o httpModule é criado em um projeto a parte, compilado como uma DLL e configurado no web.config. Isso torna fácil reutilizar um httpModule, simplesmente copiando sua DLL e fazendo a referência no web.config. Por isso iremos criar um projeto separado, que chamarei de LibAuth e uma classe chamada mdWeb.
Para criar um httpModule precisamos implementar a interface IHTTPModule que basicamente contém 2 métodos, Initialize e Dispose. No método Initialize recebemos como parâmetro um objeto HTTPApplication, para que possamos interceptar os eventos da aplicação. Então vamos implementar o seguinte : Se o GUID do ticket for diferente do GUID do comment eliminamos o ticket e o cookie e redirecionamos para a página de login. Desta forma apenas o login mais recente do usuário é válido, os demais serão redirecionados para a página de login - claro que é pouco, mas é apenas um primeiro passo. Public Class mdWeb Implements System.Web.IHttpModule
Public Sub Dispose() Implements System.Web.IHttpModule.Dispose
End Sub
Public Sub Init(ByVal context As System.Web.HttpApplication) Implements System.Web.IHttpModule.Init
AddHandler context.PostAuthenticateRequest, AddressOf Autenticado
End Sub
Sub Autenticado(ByVal sender As Object, ByVal e As EventArgs)
If HttpContext.Current.User.Identity.IsAuthenticated Then
Dim user As MembershipUser = Membership.GetUser()
Dim cookie As HttpCookie = HttpContext.Current.Request.Cookies(FormsAuthentication.FormsCookieName)
Dim tikcookie As FormsAuthenticationTicket tikcookie = FormsAuthentication.Decrypt(cookie.Value)
If user.Comment.Split("|")(1).Split(";")(1) <> tikcookie.UserData Then FormsAuthentication.SignOut() FormsAuthentication.RedirectToLoginPage() End If End If End Sub End Class
Vamos analisar as linhas de código de nosso módulo. Na linha : If HttpContext.Current.User.Identity.IsAuthenticated Then
Nas linhas : If user.Comment.Split("|")(1).Split(";")(1) <> tikcookie.UserData Then FormsAuthentication.SignOut() FormsAuthentication.RedirectToLoginPage() End If
Criado o projeto do httpModule com a classe, precisamos fazer os seguintes passos : 9) Fazer referência no projeto do site para o projeto do httpModule
10) Configurar o httpModule no web.config <httpModules> <add name="formssecure" type="libAuth.mdWeb"></add> </httpModules> 11) Fazer um Rebuild da solução 12) Defina a pagina default.aspx como start page Feitos esses passos você já pode realizar o teste da aplicação. Siga os seguintes passos para fazer o teste : 13) Execute a aplicação pelo Visual Studio. 14) Abra uma nova janela de browser e, copiando a URL, tente fazer uma chamada direta da página default.aspx.
15) Logue-se em uma das janelas do browser e navegue pela paginação da gridview. 16) Logue-se na outra janela do browser e navegue pela paginação da gridview. 17) Faça um refreh na primeira janela do browser. Resultado : A primeira janela do browser será redirecionada para a tela de login, pois teve seu login invalidado quando a 2a janela foi logada, demonstrando assim como o usuário será impedido de utilizar uma mesma conta simultaneamente em 2 locais. Agora vamos melhorar nosso algorítimo e impedir que o 2o login aconteça. Para isso vamos programar o evento LogginIn do webControl Login. Observe que o evento LoggedIn, que programamos antes, acontece após o login do usuário. Agora vamos programar o evento LogginIn, que acontece antes do login do usuário, para podermos validar se o login deve ou não acontecer. Veja como fica o código : Protected Sub Login1_LoggingIn(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.LoginCancelEventArgs) Handles Login1.LoggingIn
Dim user As MembershipUser user = Membership.GetUser(Login1.UserName)
If user.Comment <> String.Empty Then If DateTime.Parse(user.Comment.Split("|")(0).Split(";")(1)) < Now() Then user.Comment = String.Empty Membership.UpdateUser(user) Else e.Cancel = True Login1.InstructionText = "Login falhou. Você já encontra-se logado em outro local"
End If End If End Sub A sequencia é simples :
Faça o teste novamente. Desta vez o 2o login será impedido de ocorrer. Você só poderá testar uma vez, após a primeira vez o login ficará travado por 30 minutos devido ao timeout no comment - você teria que limpar o campo Comment na tabela aspnet_Membership para poder testar novamente.
Nosso próximo passo, portanto, é bem natural : Precisamos garantir que quando o usuário fizer logOff o campo Comment seja limpo. 1) Na página default.aspx, insira o WebControl LoginStatus 2) Programe o método LoggingOut Protected Sub LoginStatus1_LoggingOut(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.LoginCancelEventArgs) Handles LoginStatus1.LoggingOut
Dim user As MembershipUser user = Membership.GetUser() user.Comment = String.Empty Membership.UpdateUser(user) End Sub 3) Repita os testes
Segurança adicional : Com estes recursos que implementamos, podemos apenas com a adição de algumas linhas gerar uma proteção adicional contra o roubo de cookies de autenticação. Basta para isso tratar, em nosso httpModule, a possibilidade do Comment estar vazio, limpando o cookie de autenticação e negando acesso. Administração : Precisamos ter o cuidado para não travar usuários autênticos do site. Se o usuário fechar o browser sem dar logOff ficará travado por 20 minutos. Temos as seguintes soluções possíveis :
LogOff via JavaScript Para identificar o momento em que o usuário fecha a janela do browser, precisamos utilizar programação do client, por isso o javascript. No momento em que ocorrer o fechamento da janela do browser, iremos fazer o disparo de uma página que realize o logOff do usuário. O primeiro passo então fica sendo criar esta página, que chamaremos de LogOff.aspx. Esta página deverá realizar o logOff do usuário e fechar-se automaticamente. No page Load, fazemos o logOff : Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim user As MembershipUser user = Membership.GetUser() user.Comment = String.Empty Membership.UpdateUser(user) FormsAuthentication.SignOut() End Sub No javascript, implementamos o fechamento : <script language="javascript" type="text/javascript"> // <!CDATA[
function window_onload() { window.close(); }
// ]]> </script> É importante fazer isso utilizando as combos da interface, assim automaticamente é inserida a chamada deste evento : <body onload="return window_onload()">
Feito isso o próximo passo é na default.aspx implementar a chamada do LogOff no caso do fechamento da página. Veja : <script language="javascript" type="text/javascript"> // <!CDATA[
function window_onunload() {
if (window.screenTop > 10000 && window.screenLeft > 10000) { window.open("logoff.aspx"); }
}
// ]]> </script> Observe o IF fazendo teste o screenTop e screenLeft. Ocorre que o evento unload não acontece apenas no fechamento, mas também durante a navegação normal do site, quando o usuário sai de uma página para outra. O screenTop e screenLeft são os únicos atributos que podem ser utilizados para diferenciar o fechamento da navegação normal. Mais uma vez, é importante implementar o unload pela interface, usando as combos, para que a chamada do evento seja criada automaticamente : <body onunload="return window_onunload()"> Simplificando o JavaScript O javascript que acabamos de montar para a página default.aspx precisa ser inserido em todas as páginas do site. Isso é algo trabalhoso e desagradável para o desenvolvedor fazer, então precisamos criar uma forma de simplificar essa tarefa. Podemos criar uma classe que possa ser utilizada como base para as páginas web do nosso site. Então implementamos a geração do javascript nesta classe, poupando o desenvolvedor de ter este trabalho. Então vamos lá : 1) No projeto libAuth, crie uma classe chamada PaginaBase 2) Altere a herança para system.Web.UI.Page Inherits System.Web.UI.Page 3) Faça um Override no método OnPreRender
Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs) Dim sb As New System.Text.StringBuilder
sb.Append("window.onunload=window_onunload;" & vbCrLf) sb.Append("function window_onunload() {" & vbCrLf) sb.Append("if (window.screenTop > 10000 && window.screenLeft > 10000) {") sb.Append("window.open(""logoff.aspx"");}}")
Me.ClientScript.RegisterClientScriptBlock(Me.GetType(), "unload", _ "<script language=""javascript"" type=""text/javascript"">" & sb.ToString() _ & "</script>")
MyBase.OnPreRender(e) End Sub 4) Retirar o javascript que inserimos na página default.aspx 5) Altere a herança da página default.aspx para libAuth.PaginaBase Inherits libAuth.PaginaBase Implementando o desbloqueio do usuário Com a implementação do JavaScript de logOff, um usuário ficar bloqueado (com o comment preenchido e ter que esperar o timeout) será um evento incomum, mas ainda pode acontecer em algumas situações. Então para completar podemos criar uma página que permita a um administrador fazer o desbloqueio de um usuário que fique bloqueado por acidente. Vamos criar uma página chamada desbloquear.aspx para realizar este tipo de desbloqueio de usuários. 1) Insira uma dropDownList chamada ddlUsuarios 2) Insira um botão chamado cmdDesbloquear 3) Insira um label chamado lblResposta
4) Programe o load da página para carregar os usuários na ddlUsuarios Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Me.IsPostBack Then ddlUsuarios.DataSource = Membership.GetAllUsers() ddlUsuarios.DataBind() End If End Sub 5) Programe o click do botão cmdDesbloquear para fazer o desbloqueio do usuário selecionado na ddlUsuarios Protected Sub cmdDesbloquear_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cmdDesbloquear.Click
Dim user As MembershipUser user = Membership.GetUser(ddlUsuarios.SelectedItem.Text)
user.Comment = String.Empty Membership.UpdateUser(user) lblResultado.Text = "Usuário desbloqueado com sucesso" End Sub A página desbloquear.aspx precisará ser protegida para que apenas administradores tenham acesso. Esta proteção depende de como você fará a autorização em sua aplicação (veja um artigo sobre isso em http://www.devaspnet.com.br/colunas/coluna0126.aspx ), mas duas formas básicas seriam as seguintes :
Para testar, rode a aplicação e abra mais duas janelas do browser.
Simplificando a reutilização Para montar os exemplos, tivemos que codificar eventos do objeto Login e do objeto LoginStatus. Sempre que desejarmos fazer o mesmo procedimento em outro site precisaremos da mesma codificação, repetidamente. Para evitar este tipo de repetição e possíveis erros, podemos recorrer a criação de WebControls. Podemos utilizar a própria libAuth para isso. 1) Crie uma nova classe, NovoLogin 2) Defina a herança para System.Web.UI.WebControls.Login Inherits System.Web.UI.WebControls.Login 3) Faça um overrides no método onLoggedIn Observe que o código é o mesmo que utilizamos anteriormente Protected Overrides Sub OnLoggedIn(ByVal e As System.EventArgs) 'Alteração do cookie Dim tik2 As FormsAuthenticationTicket Dim cookie As HttpCookie = Current.Response.Cookies(FormsAuthentication.FormsCookieName)
Dim g As Guid = Guid.NewGuid() tik2 = FormsAuthentication.Decrypt(cookie.Value)
Dim user As MembershipUser
'Atualização do usuário no banco user = Membership.GetUser(Me.UserName)
user.Comment = "timeout;" & tik2.Expiration & "|GUID;" & g.ToString() Membership.UpdateUser(user)
Dim tik As New FormsAuthenticationTicket(tik2.Version, tik2.Name, tik2.IssueDate, tik2.Expiration, tik2.IsPersistent, g.ToString())
Dim enctik As String = FormsAuthentication.Encrypt(tik) cookie.Value = enctik
MyBase.OnLoggedIn(e) End Sub 4) Faça os seguintes imports : Imports System.Web.Security Imports System.Web Imports System.Web.HttpContext 5) Faça um overrides no método OnLogginIn Protected Overrides Sub OnLoggingIn(ByVal e As System.Web.UI.WebControls.LoginCancelEventArgs)
Dim user As MembershipUser user = Membership.GetUser(Me.UserName)
If user.Comment <> String.Empty Then If DateTime.Parse(user.Comment.Split("|")(0).Split(";")(1)) < Now() Then user.Comment = String.Empty Membership.UpdateUser(user) Else e.Cancel = True Me.InstructionText = "Login falhou. Você já encontra-se logado em outro local"
End If End If MyBase.OnLoggingIn(e) End Sub 6) Crie uma nova classe chamada NovoLogOut 7) Defina a herança para System.Web.UI.WebControls.LoginStatus Inherits System.Web.UI.WebControls.LoginStatus 8) Faça um overrides no método OnLoggedOut Protected Overrides Sub OnLoggedOut(ByVal e As System.EventArgs) Dim user As MembershipUser user = Membership.GetUser() user.Comment = String.Empty Membership.UpdateUser(user) MyBase.OnLoggedOut(e) End Sub 9) Na página Login.aspx, comente o código e substitua o objeto Login pelo NovoLogin 10) Na página default.aspx comente o código do evento LoggedOut e substitua o LoginStatus pelo NovoLogOut
11) Teste a aplicação. O funcionamento deverá permanecer o mesmo Personalização adicional : Uma opção a mais seria adicionar uma propriedade "LoginUnico" do tipo boolean a esses webControls e adicionar uma lógica de forma a só implementar essas novas funcionalidades se a propriedade estiver como true. Desta forma deixariamos os novos controles configuráveis. Resumo Criamos novos webControls de Login e LogOut, criamos um httpModule e uma classe PaginaBase. Fazendo uso destes recursos em seu projeto, poderá facilmente implementar login único, acrescentando apenas a página de logOff. Dennes Torres |
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Veja abaixo os comentários já enviados :
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Quer
saber mais?
Faça um curso na Búfalo Informática, Treinamento e Consultoria e Prepare-se para o Mercado! Veja o que a Búfalo tem para você. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
� Búfalo Informática,
Treinamento e Consultoria -
Rua Álvaro Alvim, 37 Sala 920 - Cinelândia - Rio de Janeiro / RJ
Tel.: (21)2262-1368 (21) 9240-5134 (21) 9240-7281 e-Mail: contato@bufaloinfo.com.br