Setting up role based security for a website is a Very Good Thing (tm)
if you require flexibility in security. Many organizations I've
come across in my career struggle with security because they use a
system based on user type instead of groups and roles. The
obvious flaw with user type is that over time you end up with a new
user that is a "hybrid" and then you either have to add a new type, or
adjust all the pages that reference the types which grants other users
priviliges they've never had.
The worst of all sins I've seen in this scenario is actually HARD
CODING the user's userid into the page logic. Messy! So I'm
going to show you a fairly simple way to handle role based security in
a Forms Authenticated application.
Step 1: Roles, Groups, and Users
First things first, we'll need a structure that allows us to define
application roles, define groups, assign roles to groups, and assign
users to groups. For the purposes of this exercise we'll set up
the database structure like so:
CREATE TABLE [dbo].[ApplicationRoles] (
[applicationRoleID] [int] IDENTITY (1, 1) NOT NULL ,
[roleName] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[roleDescr] [varchar] (200) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[SecurityGroupRoles] (
[assocID] [int] IDENTITY (1, 1) NOT NULL ,
[applicationRoleID] [int] NOT NULL ,
[securityGroupID] [int] NOT NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[SecurityGroupUsers] (
[assocID] [int] IDENTITY (1, 1) NOT NULL ,
[securityGroupID] [int] NOT NULL ,
[userID] [varchar] (25) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[SecurityGroups] (
[securityGroupID] [int] IDENTITY (1, 1) NOT NULL ,
[securityGroupName] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[securityGroupDescr] [varchar] (200) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
) ON [PRIMARY]
GO
Step 2: Setting up Forms Authentication In the Web.Config
You've seen it before, and here it is again. Simply edit your web.config as follows:
<authentication mode="Forms">
<forms name="BrilliantAuth"
loginUrl="LogIn.aspx" protection="Encryption" timeout="30" path="/"/>
</authentication>
<authorization>
<deny users="?"/>
<allow users="*"/>
</authorization>
Step 3: Edit the global.asax
Now we have to set up the application to accept a list of roles and
assign them to the current user. We do this in the
authenticate_request method like so:
Sub Application_AuthenticateRequest(ByVal sender As Object, ByVal e As EventArgs)
Dim CookieName As String = FormsAuthentication.FormsCookieName
Dim authCookie As HttpCookie = Context.Request.Cookies(CookieName)
'Check for cookie
If IsNothing(authCookie) Then Return
Dim authTicket As FormsAuthenticationTicket
Try
authTicket = FormsAuthentication.Decrypt(authCookie.Value)
Catch
Return
End Try
If IsNothing(authTicket) Then
'Cookie failed to decrypt
Return
End If
Dim roles() As String = authTicket.UserData.Split("|")
Dim id As New FormsIdentity(authTicket)
Dim principal As New System.Security.Principal.GenericPrincipal(id, roles)
Context.User = principal
End Sub
Do notice that I split authentication ticket's UserData property.
Sadly this is a string property so we can submit a collection of role
objects so in your code somewhere you'll have to combine all your roles
into a delimited string.
Step 4: Login page ticket creation
The last step is to create the forms authentication ticket for a successful login. You do this like so:
Dim authTicket As New FormsAuthenticationTicket(1, loginUser.UserID, DateTime.Now, DateTime.Now.AddMinutes(30), False, loginUser.GetRoles())
Dim encryptedTicket As String = FormsAuthentication.Encrypt(authTicket)
Dim authCookie = New HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket)
Response.Cookies.Add(authCookie)
This does require Imports System.Web.Security. Don't fret over
loginUser, it's just a class that I had created. All you have to
do is swap that out with your userID and the GetRoles() should be your
delimited role string. The SQL I use to GetRoles() is as simple
as this:
SELECT roleName from ApplicationRoles WHERE ApplicationRoleID IN
(SELECT DISTINCT ApplicationRoleID FROM SecurityGroupUsers
INNER JOIN SecurityGroups ON SecurityGroupUsers.SecurityGroupID = SecurityGroups.SecurityGroupID
INNER JOIN SecurityGroupRoles ON SecurityGroups.SecurityGroupID = SecurityGroupRoles.SecurityGroupID
WHERE SecurityGroupUsers.UserID = @UserID)
Cheers