Role-based Forms Authentication

only for RuBoard

The .NET Framework enables role-based authentication; users can be assigned to roles and allowed access to resources based on those roles. Role access privileges are defined in the configuration file, either the root configuration file using a <location> element, or a configuration file in a subdirectory.

In Listing 15.10 you create a web.config file to be placed in a subdirectory called /adminonly .

Listing 15.10 Restricting Access by User Roles
 [VB & C#] 01: <?xml version="1.0" encoding="utf-8" ?> 02: <configuration> 03:  <system.web> 04:   <authorization> 05:    <allow roles="Administrator" /> 06:    <deny users="*" /> 07:   </authorization> 08:  </system.web> 09: </configuration> 

In Listing 15.10 you create a web.config configuration file in a subdirectory of your application. On lines 4 “7 you override the application authorization settings for this subdirectory, and any subdirectories under this one. On line 5 you use the <allow> element to specify that users in the role "Administrators" are allowed access, and on line 6 you deny all other users.

To enable role-based authentication you must assign the user roles to the current principal object. The principal object represents the security context under which the current code is being executed. To create a new principal object you must create an instance of a class that implements the IPrincipal interface and set its identity and roles arguments. The System.Security.Principal.GenericPrincipal class does just that. The GenericPrincipal class is constructed with a GenericIdentity object (which implements IIdentity ), and a string array of roles. The GenericIdentity class is used to specify the username and the authentication type that is being used.

In Listing 15.11 you create a login form that retrieves user roles from the Samples database, and issues an authentication ticket and cookie to the client.

Listing 15.11 Authenticating Users With Roles
 [VB] 01: <%@ Import Namespace="System.Data" %> 02: <%@ Import Namespace="System.Data.SqlClient" %> 03: <%@ Import Namespace="System.Text" %> 04: <%@ Import Namespace="System.Security.Principal" %> 05: <script runat="server" language="VB"> 06:  Protected Sub Login_OnClick(Sender as Object, E As EventArgs)  07:   Dim context As HttpContext = HttpContext.Current 08: 09:   Dim SqlStmt As String = "SELECT [Roles].[RoleName] FROM [Users_to_Roles] " & _ 10:    "INNER JOIN [Users] ON [Users].[UserID] = [Users_to_Roles].[UserID] " & _ 11:    "INNER JOIN [Roles] ON [Users_to_Roles].[RoleID] = [Roles].[RoleID] " & _ 12:    "WHERE [Users].[Email] = '" & UserName.Text & "' AND " & _ 13:    "[Users].[Password] = '" & Password.Text & "'" 14: 15:   Dim reader As SqlDataReader = Nothing 16:   Dim con As SqlConnection = New graphics/ccc.gif SqlConnection("server=localhost;database=samples;uid=sa;pwd=;") 17:   Dim cmd As SqlCommand = New SqlCommand(SqlStmt, con) 18: 19:   Try 20:    con.Open() 21:    reader = cmd.ExecuteReader() 22: 23:    If reader.FieldCount > 0 Then 24:     Dim cookieRoles As New StringBuilder 25:     While reader.Read() 26: cookieRoles.Append(reader("RoleName").ToString()) 27:      cookieRoles.Append(".") 28:     End While 29: 30:      ' Save the Roles in a client Cookie for future requests 31:      Dim RoleCookie As HttpCookie = New HttpCookie("Roles") 32:      RoleCookie.Value = cookieRoles.ToString() 33:      Response.Cookies.Add(RoleCookie) 34: 35: FormsAuthentication.RedirectFromLoginPage(UserName.Text, PersistCookie.Checked) 36:  37:    Else 38:     ErrorMsg.Text = "User not found, or bad credentials - try again!<br>" 39:    End If 40: 41: 42:    Catch ex As Exception 43:     ErrorMsg.Text = ex.ToString() + "<br>" 44:   End Try 45:  End Sub 46:  47:  Protected Sub Signout_OnClick(Sender as Object, E As EventArgs) 48:   Dim context As HttpContext = HttpContext.Current 49:   FormsAuthentication.SignOut() 50:   context.Response.Cookies.Remove("Roles") 51:  End Sub 52: </script> [C#] 01: <%@ Import Namespace="System.Data" %> 02: <%@ Import Namespace="System.Data.SqlClient" %> 03: <%@ Import Namespace="System.Text" %> 04: <%@ Import Namespace="System.Security.Principal" %> 05: <script runat="server" language="C#"> 06:  protected void Login_OnClick(Object sender, EventArgs e){ 07:   HttpContext context = HttpContext.Current; 08: 09:   string SqlStmt = "SELECT [Roles].[RoleName] FROM [Users_to_Roles] " + 10:    "INNER JOIN [Users] ON [Users].[UserID] = [Users_to_Roles].[UserID] " + 11:    "INNER JOIN [Roles] ON [Users_to_Roles].[RoleID] = [Roles].[RoleID] " + 12:    "WHERE [Users].[Email] = '" + UserName.Text + "' AND " + 13:    "[Users].[Password] = '" + Password.Text + "'"; 14: 15:   SqlDataReader reader = null;  16:   SqlConnection con = new graphics/ccc.gif SqlConnection("server=localhost;database=samples;uid=sa;pwd=;"); 17:   SqlCommand cmd = new SqlCommand(SqlStmt, con); 18: 19:   try{ 20:    con.Open(); 21:    reader = cmd.ExecuteReader(); 22: 23:    if(reader.FieldCount > 0){ 24:     StringBuilder cookieRoles = new StringBuilder(); 25:     while(reader.Read()){ 26:      cookieRoles.Append(reader["RoleName"].ToString()); 27:      cookieRoles.Append("."); 28:     } 29: 30:      //Save the Roles in a client Cookie for future requests 31:      HttpCookie RoleCookie = new HttpCookie("Roles"); 32:      RoleCookie.Value = cookieRoles.ToString(); 33:      Response.Cookies.Add(RoleCookie); 34: 35: FormsAuthentication.RedirectFromLoginPage(UserName.Text, PersistCookie.Checked);  36:  37:    } else{ 38:     ErrorMsg.Text = "User not found, or bad credentials - try again!<br>"; 39:    } 40:   } 41: 42:   catch(Exception ex){ 43:    ErrorMsg.Text = ex.ToString() + "<br>"; 44:   } 45:  } 46: 47:  protected void Signout_OnClick(Object sender, EventArgs e){ 48:   HttpContext context = HttpContext.Current; 49:   FormsAuthentication.SignOut(); 50:   context.Response.Cookies.Remove("Roles"); 51:  } 52: </script> [VB & C#] 53: <html> 54: <head> 55:  <title>Programming Datadriven Web Apps - Chapter 15</title> 56: </head> 57: <body> 58: <form runat="server"> 59: <H4>Account Logon</H4> 60:  <asp:Label runat="server" id="ErrorMsg" /> 61:  <b>E-Mail:</b><br/> 62:  <asp:TextBox runat="server" ID="UserName" /><br/> 63:  <b>Password:</b><br/> 64:  <asp:TextBox runat="server" ID="Password" TextMode="Password" /><br/> 65:  <b>Remember Me</b><br/> 66:  <asp:CheckBox runat="server" ID="PersistCookie" /><br/> 67:  <asp:Button runat="server" ID="Login" 68:   Text="Login" 69:   OnClick="Login_OnClick" /> 70: 71:  <asp:Button runat="server" ID="Signout" 72:   Text="Sign-Out" 73:   OnClick="Signout_OnClick" /> 74: </form> 75: </body> 76: </html> 

In Listing 15.11 you create a login Web Form that authenticates a user and assigns roles to the user based on information in the Samples database.

On lines 9 “13 you create a SQL statement to return all the RoleNames associated with a specific user, based on his username and password. The results are returned in a SqlDataReader . If any records are returned, the reader.FieldCount will be greater than 0, as evaluated on line 23. If records are returned, you build a string of RoleNames delimited by a period. This string ( cookieRoles ) will be saved to a non-persistent cookie on the client machine.

Rather than having to retrieve the RoleNames on each request, you should persist them somehow. This can be done by placing the cookieRoles object in a Session variable or a client-side cookie. Since it is likely that you have thousands of users accessing your Web site at any time, using a Session variable could be very resource intensive . By using a client-side cookie you move the resources required to the client, keeping your server resources free.

On line 31 you create a new cookie named "Roles". You assign the string representation of the cookieRoles object to the cookie, and add it to the client-side Cookies collection, on line 33. Once the cookie has been set you call FormsAuthentication.RedirectFromLoginPage() to set the authentication ticket and redirect the user to the originally requested resource.

At this point the user is authenticated, but the roles are not set to the current principal ( HttpContext.User ). You need to create a GenericPrincipal object with the user roles as an argument. The principal object is not persisted across requests, and must be recreated on each new request. This is why you stored the roles in a client-cookie.

To create a new GenericPrincipal object on each request, you use the global.asax Application_AuthenticateRequest() event handler. This handler is called each time a user request is authenticated. In this event handler you create a new GenericPrincipal object and assign it to the current principal instance. The roles in the current principal instance are compared against any <allow roles> or <deny roles> elements in the configuration file. You are not required to write any code to compare the roles; you only need to set the current principal instance.

In Listing 15.12 you create the Application_AuthenticateRequest() event handler in the global.asax file (in the root of your application).

Listing 15.12 Creating a New Principal in the Global.asax
 [VB] 01: <%@ Import Namespace="System.Security.Principal" %> 02: <script language="VB" runat="server"> 03:  Public Sub Application_AuthenticateRequest() 04:   Dim context As HttpContext = HttpContext.Current 05:   If Not context.User Is Nothing AndAlso context.User.Identity.IsAuthenticated Then 06:    ' Create a generic identity. 07:    Dim userIdentity As GenericIdentity = New graphics/ccc.gif GenericIdentity(context.User.Identity.Name, "Forms") 08:    ' Create a generic principal. 09:    Dim userPrincipal As GenericPrincipal = New GenericPrincipal(userIdentity, graphics/ccc.gif context.Request.Cookies("Roles").Value.Split(".")) 10:    ' Set the new Principal to the Current User 11:    context.User = userPrincipal 12:   End If 13:  End Sub 14: </script> [C#] 01: <%@ Import Namespace="System.Security.Principal" %> 02: <script language="C#" runat="server"> 03:  public void Application_AuthenticateRequest(){ 04:   HttpContext context = HttpContext.Current; 05:   if(context.User != null && context.User.Identity.IsAuthenticated){  06:    //Create a generic identity. 07:    GenericIdentity userIdentity = new GenericIdentity(context.User.Identity.Name, graphics/ccc.gif "Forms"); 08:    //Create a generic principal. 09:    GenericPrincipal userPrincipal = new GenericPrincipal(userIdentity, graphics/ccc.gif context.Request.Cookies["Roles"].Value.Split(new Char[] { '.'} )); 10:    //Set the new Principal to the Current User 11:    context.User = userPrincipal; 12:   } 13:  } 14: </script> 

On line 5 of Listing 15.12 you evaluate the current principal ( HttpContext.Current.User ) to see if it is not null. If it is null, then no principal exists, and you do not need to proceed setting up the roles. You also use the bitwise operator AndAlso ( && in C#) to evaluate the IsAuthenticated property of the principal only when the first evaluation returns True .

If the principal exists, and the user has been authenticated (when the authentication ticket was issued in the login Web Form), you need to create new GenericIdentity and GenericPrincipal objects.

On line 7 you create a new GenericIdentity object, specifying the value from the HttpContext.Current.User.Identity.Name property as the name argument, and the word "Forms" as the type argument. The GenericIdentity represents the user and authentication type being used. The GenericIdentity object is used as the identity argument when creating the new GenericPrincipal object, on line 9. The second argument in the GenericPrincipal constructor is a string array of roles. This string array is created by using the Split() method of the String class on the Roles cookie issued in the login Web Form.

On line 39 you assign the GenericPricipal object, which contains the user roles in an array, to the current instance of the HttpContext.Current.User class, which makes the GenericPrincipal the current principal object.

At this point, the current principal object contains the username (supplied by the user when he logged in), and an array of roles that will be compared against the configuration file.

only for RuBoard


Programming Data-Driven Web Applications with ASP. NET
Programming Data-Driven Web Applications with ASP.NET
ISBN: 0672321068
EAN: 2147483647
Year: 2000
Pages: 170

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net