Overview

 


 

A common question that is often asked in VB forums is how to create a LogIn system where my users can Log in to my application. This should include help for forgotten passwords, and the ability to add a new user account to the database.

In this example, I have encapsulated all of those requirements. Figure 1 shows the main LogIn screen.

 

 

Figure 1. The LogIn screen


 

Figure 2 shows the Password reminder screen, where you can get a Password reminder hint.

 

 

Figure 2. Password hint screen.

 

The ability to add new User Accounts in a LogIn system is often a useful feature. Figure 3 shows the Add New screen with Password strength meter.

 

 

Figure 3. Password strength meter.

 

In general use within a application, you would want to progress to another form after logging in, but this example just progresses to another Panel. (See Figure 4).

 All of the screenshots in this article show Panels piled up one above another, and brought to the top of the z-order as necessary.

 

 

Figure 4. Success

 

For data access in this example I used an Access 2007 database, and all of the database interaction takes place in the dataLayer class:

 

 


The dataLayer class


 

 

Imports System.Data.OleDb

 

Public Class dataLayer 

   
Private Shared conn As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Login.accdb;Persist Security Info=False;") 

    Private Shared Sub connect()
        conn.Open()
    End Sub    

    Private Shared Sub disconnect()
        conn.Close()
    End Sub  

    Public Shared Function verify(username As String, password As String) As Boolean
        connect()
        Dim cmd As New OleDbCommand("SELECT COUNT(*) FROM tblAccounts WHERE username=@username AND password=@password", conn)
        cmd.Parameters.AddWithValue("@username", username)
        cmd.Parameters.AddWithValue("@password", password)
        Dim o As Object = cmd.ExecuteScalar
        disconnect()
        Return CBool(o)
    End Function  

    Public Shared Sub addNew(username As String, password As String, hint As String)
        connect()
        Dim cmd As New OleDbCommand("INSERT INTO tblAccounts ([username], [password], [hint]) VALUES(@username, @password, @hint)",
conn)

        cmd.Parameters.AddWithValue("@username", username)
        cmd.Parameters.AddWithValue("@password", password)
        cmd.Parameters.AddWithValue("@hint", hint)
        cmd.ExecuteNonQuery()
        disconnect()
    End Sub  

    Public Shared Function getHint(username As String) As String 
        connect()
        Dim cmd As New OleDbCommand("SELECT hint FROM tblAccounts WHERE username=@username", conn)
        cmd.Parameters.AddWithValue("@username", username)
        Dim o As Object = cmd.ExecuteScalar
        disconnect()
        If o IsNot Nothing Then 
            Return o.ToString
        Else
            Return ("User doesn't exist.")
        End If 
    End Function  

End Class

 



There are three Public methods, two of which are Functions, and the other is a Sub-Procedure.

The first Function verify checks if a user with the supplied name and password exists in the database, returning a Boolean True or False value.

The second function getHint queries the database and returns a password reminder for a given username.

The Sub-Procedure addNew adds a new user to the database.

In a real world application, I wouldn't recommend saving passwords as plaintext. There are encryption methods available which would enable you to mask passwords in the database. Here are several options:

http://msdn.microsoft.com/en-us/library/system.security.cryptography.hashalgorithm(v=vs.80).aspx
http://msdn.microsoft.com/en-us/library/as0w18af(v=vs.110).aspx
http://www.scproject.biz/secureCipher.php

In addition you may wish to only allow an administrator to create new accounts.
 


The Password strength meter class



       

 

Often on official websites when creating a password, you’ll be shown a meter that gauges the strength of your password. There are also sites where you can test a password, such as The Password Meter at: http://www.passwordmeter.com/. This is my version of that meter.

The meter is a custom control created by extending the Label control.


 

Imports System.Text.RegularExpressions

 

Public Class passwordMeter 
    Inherits Label  

    Private StrengthRatings() As String = {"Extremely weak", "Very Weak", "Weak", "Medium", "Strong", "Very Strong"}
    Private associatedEditControl As TextBox 
    Private password As String  

    Private _score As Integer = 0
    Public Property score() As Integer 
        Get
            Return _score
        End Get 
        Set(ByVal value As Integer)
            _score = value
            'repaint control
            MyBase.Invalidate()
        End Set 
    End Property  

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs) 
      
If score > 0 Then
            e.Graphics.FillRectangle(New SolidBrush(GetColor(score)), New Rectangle(0, 0, score * 2, MyBase.ClientSize.Height))
        End If
        MyBase.OnPaint(e)
    End Sub  

    Private Function GetColor(ByVal score As Integer) As Color 
        Select Case score
            Case 0
                Return Color.FromArgb(220, 150, 150)
            Case 10
                Return Color.FromArgb(230, 185, 185)
            Case 20
                Return Color.FromArgb(200, 190, 220)
            Case 30
                Return Color.FromArgb(235, 240, 220)
            Case 40
                Return Color.FromArgb(215, 230, 190)
            Case 50
                Return Color.FromArgb(195, 215, 155)
        End Select 
    End Function  

    'setup handler
    Public Sub bindTextbox(ByVal tb As TextBox)
        associatedEditControl = tb
        AddHandler associatedEditControl.KeyUp, AddressOf tb_KeyUp
    End Sub  

    Private Sub tb_KeyUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEventArgs)
        password = associatedEditControl.Text
        CalculateMeter()
    End Sub  

    Private Sub CalculateMeter()
        Dim passwordScore As Integer = 50 

        'Length more than 8?
        If (password.Length < 8) Then passwordScore -= 10 

        'all lower case?
        If Regex.IsMatch(password, "^[a-z]+$") Then
            passwordScore -= 10
        End If  

        'all upper case?
        If Regex.IsMatch(password, "^[A-Z]+$") Then
            passwordScore -= 10
        End If  

        'all digits?
        If Regex.IsMatch(password, "^\d+$") Then
            passwordScore -= 10
        End If  

        'contains digit?
        If Not Regex.IsMatch(password, "\d") Then
            passwordScore -= 10
        End If  

        'contains lower case char?
        If Not Regex.IsMatch(password, "[a-z]") Then
            passwordScore -= 10
        End If  

        'contains upper case char?
        If Not Regex.IsMatch(password, "[A-Z]") Then
            passwordScore -= 10
        End If  

        'contains special char?
        If Not Regex.IsMatch(password, "[!,@,#,$,%,^,&,*,?,_,~,-,/""]") Then
            passwordScore -= 10
        End If  

        'Length more than 4?
        If (password.Length < 5) Then passwordScore -= 10 

        passwordScore = Math.Max(passwordScore, 0)

        'get strength rating from array 
        MyBase.Text = If(associatedEditControl.Text.Trim <> "", StrengthRatings(passwordScore \ 10), "") 
       
'change score property
        score = passwordScore 

    End Sub  

End Class

 


This control is always used in association with a TextBox and has a bindTextBox method to establish that connection, which it uses in an encapsulated handler when the TextBox KeyUp message is received.

As a key is released while entering a password, the strength of the password is calculated in the CalculateMeter method and the strength rating is shown in the Label text, and also the BackColor of the Label control is painted with a color dependent on the score property for a percentage of its width.

       


 The Form

 

The Form in this example uses many more controls than it would appear at a first glance. This is because the controls are all contained in four Panels, three of which are always hidden behind the topmost Panel.

The code used in the Form is very simple.

 

Public Class frmLogin 

    Private Sub frmLogin_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        PasswordMeter1.bindTextbox(pnlNew_txtPassword)
    End Sub  

    Private Sub pnlLogIn_btnNew_Click(sender As Object, e As EventArgs) Handles pnlLogIn_btnNew.Click
        'new
        clear_pnlNew()
        pnlNew.BringToFront()
        'username
        pnlNew_txtUserName.Focus()
    End Sub  

    Private Sub pnlLogIn_btnForgot_Click(sender As Object, e As EventArgs) Handles pnlLogIn_btnForgot.Click
        'forgot
        clear_pnlHint()
        pnlHint.BringToFront()
        'username
        pnlHint_txtUserName.Focus()
    End Sub  

    Private Sub pnlSuccess_btnLogOut_Click(sender As Object, e As EventArgs) Handles pnlSuccess_btnLogOut.Click
        'logout
        clear_pnlLogIn()
        pnlLogIn.BringToFront()
        'username
        pnlLogIn_txtUserName.Focus()
    End Sub  

    Private Sub pnlLogIn_btnLogIn_Click(sender As Object, e As EventArgs) Handles pnlLogIn_btnLogIn.Click
        If dataLayer.verify(pnlLogIn_txtUserName.Text, pnlLogIn_txtPassword.Text) Then
            showUserName(pnlLogIn_txtUserName.Text)
            pnlSuccess.BringToFront()
            'logout
            pnlSuccess_btnLogOut.Focus()
        Else
            MsgBox("Invalid UserName or Password")
        End If 
    End Sub  

    Private Sub pnlHint_btnHint_Click(sender As Object, e As EventArgs) Handles pnlHint_btnHint.Click
        Dim hint As String = dataLayer.getHint(pnlHint_txtUserName.Text)
        If hint <> "User doesn't exist." Then 
            lblHint.Text = hint
            pnlHint_txtPassword.Focus()
        Else
            MsgBox(hint)
        End If 
    End Sub  

    Private Sub pnlHint_btnLogIn_Click(sender As Object, e As EventArgs) Handles pnlHint_btnLogIn.Click
        If dataLayer.verify(pnlHint_txtUserName.Text, pnlHint_txtPassword.Text) Then
            showUserName(pnlHint_txtUserName.Text)
            pnlSuccess.BringToFront()
            'logout  
            pnlSuccess_btnLogOut.Focus()
        Else
            MsgBox("Invalid UserName or Password")
        End If 
    End Sub  

    Private Sub pnlNew_btnLogIn_Click(sender As Object, e As EventArgs) Handles pnlNew_btnLogIn.Click
        'addNew
        If pnlNew_txtUserName.Text.Trim <> "" AndAlso pnlNew_txtPassword.Text.Trim <> "" AndAlso pnlNew_txtReenter.Text.Trim <> "" AndAlso pnlNew_txtPassword.Text = pnlNew_txtReenter.Text Then
            dataLayer.addNew(pnlNew_txtUserName.Text, pnlNew_txtPassword.Text, pnlNew_txtHint.Text)
            showUserName(pnlNew_txtUserName.Text)
            pnlSuccess.BringToFront()
            'logout
            pnlSuccess_btnLogOut.Focus()
        End If 
    End Sub  

    Private Sub clear_pnlHint()
        pnlHint_txtUserName.Clear()
        pnlHint_txtPassword.Clear()
        lblHint.Text = ""
    End Sub 

    Private Sub clear_pnlLogIn()
        pnlLogIn_txtUserName.Clear()
        pnlLogIn_txtPassword.Clear()
    End Sub  

    Private Sub clear_pnlNew()
        pnlNew_txtUserName.Clear()
        pnlNew_txtPassword.Clear()
        pnlNew_txtReenter.Clear()
        pnlNew_txtHint.Clear()
    End Sub 

    Private Sub showUserName(username As String)
        lblLoggedInAs.Text = "Logged in as: " & username
        lblLoggedInAs.Left = (pnlSuccess.ClientSize.Width - lblLoggedInAs.Width) \ 2
    End Sub  

End Class

 


As some of the Buttons and TextBoxes used across the four Panels have duplicated or similar purposes i’ve used a naming convention where controls are named [Panel name]_[Control name].       


See also

 

 

Download here (VB2013)