Author: Willmove && Heath Stewart
Home page: http://www.amuhouse.com
E-mail: [email protected]
Note: I just learned ASP.NET two months ago. I saw an article titled Role-based Security with Forms Authentication on codeproject.com and found it very helpful. At that time I wanted to translate it into Chinese. However, direct translation is really boring. In the past two days, I referred to this article by Heath Stewart, and based on my own understanding, I wrote it into Chinese according to my own ideas and expressions. Attached is a demo web application I made for this article.
If there is any misunderstanding, please write in to point it out or leave a comment.
P.S. Spam is really annoying, please show your respect.
The original article is at http://www.codeproject.com/aspnet/formsroleauth.asp
Original authorHeath Stewart
summary:
ASP.NET provides a role-based authentication mechanism, but its support for roles is incomplete. This article attempts to illustrate how to implement and use this role-based authentication mechanism through some examples.
Introduction:
Form authentication in ASP.NET is a very powerful feature that requires only a small amount of code to implement a simple platform-independent security authentication system.
However, if you need a more complex and efficient authentication mechanism, then you will need to take advantage of its flexibility by dividing many users into user groups. Windows Integrated Authentication provides this authentication mechanism, but it uses NTLM, the Windows NT LAN Manager, so it is not cross-platform. Now more and more people are using Linux systems, and there are more and more users of Mozilla Forefox browser. We certainly cannot keep these people out, so we are looking for another authentication mechanism. There are two options: one is to divide the website into multiple areas and provide multiple login pages, forcing users to register and log in one by one; the other is to group users and restrict the access rights of specific user groups to a certain page or area. . The latter is certainly the better choice. We can achieve this functionality by assigning roles to individual users.
Microsoft left the role-based authentication mechanism in forms authentication for the .NET platform, but we have to implement it ourselves. This article strives to cover some basic things about the role-based authentication mechanism in forms authentication, such as its concept, its implementation, how to apply it in web applications, etc.
Necessary preparation:
We first need to create a database, a Web application project, several confidential directories with different security levels, and several ASP.NET pages. Of course you can also add these to your existing web application project.
1. To create a database,
you must first choose the database management system DBMS you want to use. This article uses SQL Server 2000.
In the database of actual application projects, there is usually a user data table Users, which may include the user's unique tag: UserID, username: UserName, password: Password, user's email address: Email, user's city: City, and the number of user logins LoginCount etc. You can assign roles to users by creating a UserInRoles data table (generally including two fields, user name: UserName, user role: UserRoles).
For the sake of simplicity, I only create a Users data table, which has 3 fields, username UserName, password Password, and user roles UserRoles. Before creating a table, you need to select a database or create a new database. To create a new database named WebSolution, only a simple SQL statement is required:
program code
Create DATABASE WebSolution
To select a database called msdb
in GO
, you can use the SQL statement:
program code
USE msdb
GO
Next, we create the Users data table just mentioned. The SQL script is as follows:
Program code
Create TABLE Users
(
UserName nvarchar(100) CONSTRAINT PK_UserName PRIMARY KEY,
Password nvarchar(150),
UserRoles nvarchar(100)
)
can create index Credentials for this table. The SQL statement is as follows:
Program code
Create INDEX Credentials ON Users
(
UserName,
Password
)
Creating an index is optional and is up to you. Please refer to relevant information for the benefits and disadvantages of indexing.
Then we add data to this Users database. The character name is of your own choice, but it is best to use a meaningful name, such as
"Administrator" (top-level administrator), "Manager" (administrator), "Member" (joined member), "User" (ordinary user), etc. For example:
UserName|Password|Roles
"willmove"|"pwd123"|"Administrator,User"
"amuhouse"|"pwd123"|"User"
is:
program code
--note that '45CB41B32DCFB917CCD8614F1536D6DA' is a string encrypted by md5 using 'pwd123'
Insert INTO Users(UserName,Password,UserRoles) VALUES ('willmove','45CB41B32DCFB917CCD8614F1536D6DA','Administrator,User')
GO
Insert INTO Users(UserName,Password,UserRoles) VALUES ('amuhouse','45CB41B32DCFB917CCD8614F1536D6DA','User')
GO
Note that Roles are case-sensitive because they are case-sensitive in the Web.config file. Now we create several necessary pages to implement this security authentication mechanism.
The first is the user login page Login.aspx
If you haven't created a web application yet, create one now. Of course you can also create this page in an existing web application. Here I assume that a Web application named RolebasedAuth has been created (ie Project in Visual Studio .Net). I put this Login.aspx in its root directory, which is accessible through http://localhost/RolebasedAuth/Login.aspx .
It doesn't matter where this Login.aspx is placed, but it must be accessible to the public.
Under the application root path, we create two secret subdirectories, namely Admin and User.
Next, we create a forms authentication login system that supports role authentication. Because Microsoft does not provide a simple implementation mechanism, we have to spend some time creating the authentication ticket ourselves. It needs to store a small amount of information. Of course, some names must be the same as those configured in Web.config, otherwise ASP.NET will think that your authentication ticket is invalid and force you to redirect to the login page. We add two TextBox controls to Login.aspx in VS.NET and name them UserNameTextBox and PasswordTextBox. We also add a Button and name it LoginButton. Click it to enter the background code. Add the required code in the LoginButton_Click method. as follows:
program code
private void LoginButton_Click(object sender, System.EventArgs e)
{
//Initialize FormsAuthentication
// Note that it is in the System.Web.Security namespace
// So add using System.Web.Security; at the beginning of the code
FormsAuthentication.Initialize ();
// Create database connection and database operation command objects
// Note that it is in the System.Data.SqlClient namespace
// So add using System.Data.SqlClient; at the beginning of the code
SqlConnection conn =
new SqlConnection("Data Source=sun-willmove;integrated security=SSPI;Initial Catalog=WebSolution;");
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "Select UserRoles FROM Users Where UserName=@username " +
"AND Password=@password ";
// Fill in each parameter
cmd.Parameters.Add("@username", SqlDbType.NVarChar, 100).Value =
UserNameTextBox.Text;
cmd.Parameters.Add("@password", SqlDbType.NVarChar, 150).Value =
FormsAuthentication.HashPasswordForStoringInConfigFile(
PasswordTextBox.Text, "md5"); // or "sha1"
// Execute database operation command
conn.Open();
SqlDataReader reader = cmd.ExecuteReader();
if (reader.Read())
{
// To implement authentication, create a new ticket
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1, // Ticket version number
UserNameTextBox.Text, // Ticket holder
DateTime.Now, //Time to allocate tickets
DateTime.Now.AddMinutes(30), //Expiration time
true, //requires user's cookie
reader.GetString(0), // User data, here is actually the user's role
FormsAuthentication.FormsCookiePath);//Valid cookie path
//Use machine code machine key to encrypt cookies for safe transmission
string hash = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(
FormsAuthentication.FormsCookieName, // The name of the authentication cookie
hash); //Encrypted cookie
//Set the cookie expiration time to be consistent with the expiration time of tickets
if (ticket.IsPersistent) cookie.Expires = ticket.Expiration;
//Add cookies to the page request response
Response.Cookies.Add(cookie);
//Redirect the user to the previously requested page,
// If no page has been requested before, redirect to the homepage
string returnUrl = Request.QueryString["ReturnUrl"];
if (returnUrl == null) returnUrl = "./";
// Do not call the FormsAuthentication.RedirectFromLoginPage method.
// Because it will replace the ticket (cookie) just added
Response.Redirect(returnUrl);
}
else
{
// Don't tell the user "Password is wrong", this is equivalent to giving the intruder a chance.
// Because they know the username they entered exists
//
ErrorLabel.Text = "Username or password is wrong, please try again!";
ErrorLabel.Visible = true;
}
reader.Close();
conn.Close();
}
The front-end aspx page code is as follows:
program code
<%@ Page language="c#" Codebehind="Login.aspx.cs" AutoEventWireup="false" Inherits="RolebasedAuth.Login" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>Login</title>
<meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1">
<meta name="CODE_LANGUAGE" Content="C#">
<meta name="vs_defaultClientScript" content="JavaScript">
<meta name="vs_targetSchema" content=" http://schemas.microsoft.com/intellisense/ie5 ">
</HEAD>
<body>
<form id="Form1" method="post" runat="server">
<P>
<asp:Label id="Label1" runat="server">Username:</asp:Label>
<asp:TextBox id="UserNameTextBox" runat="server"></asp:TextBox></P>
<P><FONT face="宋体"> </FONT>
<asp:Label id="Label2" runat="server">Password:</asp:Label>
<asp:TextBox id="PasswordTextBox" runat="server" TextMode="Password"></asp:TextBox></P>
<P>
<asp:Label id="ErrorLabel" runat="server" Visible="False"></asp:Label></P>
<P>
<asp:Button id="LoginButton" runat="server" Text="Login"></asp:Button></P>
</form>
</body>
</HTML>
You'll notice what we did with the password above: hashing it. Hash encryption is a one-way algorithm (irreversible) that generates a unique array of characters. So changing the case of even one letter in the password will produce a completely different hash column. We store these encrypted passwords in the database, which is more secure. In a practical application, you may want to retrieve a forgotten password for a user. But hashing is irreversible, so you won't be able to recover the original password. But you can change the user's password and tell him the changed password. If a website can give you old passwords, then you need to think clearly, your user data is not safe! In fact, most domestic websites directly store user passwords in the database without encryption. If a hacker succeeds, these user accounts are in danger!
Without SSL, your password is transmitted in clear text across the network. It may be stolen during transmission. Encrypting passwords on the server side only ensures the security of password storage. SSL-related information can be found at http://www.versign.com or http://www.thewte.com .
If you don't want to store the password in the database encrypted, you can change the above code to
FormsAuthentication.HashPasswordForStoringInConfigFile(PasswordTextBox.Text, "md5") can be changed to PasswordTextBox.Text.
Next, we need to modify the Global.asax file. If your web application does not have this file, please right-click the web application project and select "Add->Add New Item...->Global Application Class". In Global.asax or Global.asax.cs, find the method (function) called Application_AuthenticationRequest. First confirm that the System.Security.Principal and System.Web.Security namespaces have been included or used, and then modify it. The modified code:
program code
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
if (HttpContext.Current.User != null)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
if (HttpContext.Current.User.Identity is FormsIdentity)
{
FormsIdentity id =
(FormsIdentity)HttpContext.Current.User.Identity;
FormsAuthenticationTicket ticket = id.Ticket;
// Get the user data stored in the ticket, which is actually the user's role here
string userData = ticket.UserData;
string[] roles = userData.Split(',');
HttpContext.Current.User = new GenericPrincipal(id, roles);
}
}
}
}
The authentication ticket (username and password) is not stored as part of the cookie, and it cannot be since users can modify their cookies.
In fact, FormsAuthentication uses your machine key (usually in machine.config) to encrypt the ticket (FormsAuthenticationTicket). We use UserData to store user roles and generate new credentials. Once the credential has been created, it is added to the current context (i.e. HttpContext) so that it can be used to retrieve the user's role.
Next, we set up the secret directory (that is, the "security directory", a directory that only specific users such as administrators have permission to access). First check if there is a Web.config file in the root directory of your web application. If not, create one. You can also create a Web.config file in your subdirectory. Of course, this Web.config file is restricted (some parameters cannot be set).
To implement security authentication, findthe program code
under the <system.web> node in the Web.config file in the root directory of the Web application.
<authentication mode="Windows" />, change it to
<authentication mode="Forms">
<forms name="AMUHOUSE.ASPXAUTH"
loginUrl="Login.aspx"
protection="All"
path="./" />
</authentication>
<authorization>
<allow users="*"/>
</authorization>
In the name="AMUHOUSE.ASPXAUTH" above, the name AMUHOUSE.ASPXAUTH is arbitrary. To control the permissions of users or user groups, we can have two methods. One is to configure the Web.config file in the application root directory, and the other is to create an independent Web.config file in the secret directory. (The latter may be better.) If it is the former, the Web.config should contain the following content (or similar content):
program code
<configuration>
<system.web>
<authentication mode="Forms">
<forms name="AMUHOUSE.ASPXAUTH"
loginUrl="login.aspx"
protection="All"
path="/"/>
</authentication>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
<location path="./Admin">
<system.web>
<authorization>
<!-- Attention! The order and case of the following lines are very important! -->
<allow roles="Administrator"/>
<deny users="*"/>
</authorization>
</system.web>
</location>
<location path="./User">
<system.web>
<authorization>
<!-- Attention! The order and case of the following lines are very important! -->
<allow roles="User"/>
<deny users="*"/>
</authorization>
</system.web>
</location>
</configuration>
In order to make the directories of web applications not dependent on each other before and make it easier to rename or move them, you can choose to configure a separate Web.config file in each security subdirectory. It only needs to configure the <authorization/> node, as follows:
program code
<configuration>
<system.web>
<authorization>
<!-- Attention! The order and case of the following lines are very important! -->
<allow roles="Administrator"/>
<deny users="*"/>
</authorization>
</system.web>
</configuration>
It should be reminded again that the above roles are case-sensitive. For convenience, you can also modify the above to:
<allow roles="Administrator,administrator" />
If you want to allow or deny multiple roles access to this directory, you can separate them with commas, such as:
<allow roles="Administrator,Member,User" />
<deny users="*" />
At this point, we have configured a role-based security authentication mechanism for the website. You can compile your program first, and then try to access a secret directory, such as http://localhost/RolebasedAuth/Admin , at which time you will be redirected to the user login page. If you log in successfully and your role has access rights to this directory, you will return to this directory. There may be users (or intruders) trying to enter the confidential directory. We can use a Session to store the number of times the user has logged in. If the number exceeds a certain number, the user will not be allowed to log in, and "The system has rejected your login request!" will be displayed.
Below, we discuss how to make web controls display different content based on user roles.
Sometimes it's better to display content based on the user's role, because you probably don't want to make a bunch of pages with a lot of duplicate content for so many different roles (user groups). On such a site, various user accounts can coexist, and paid user accounts can access additional paid content. Another example is a page that will display an "Enter Admin" button linking to the Admin page if the current user is in the "Administrator" role. We will implement this page now.
The GenericPrincipal class we used above implements the IPincipal interface. This interface has a method called IsInRole(), and its parameter is a string. This string is the user role to be verified. If we want to display content to logged in users whose role is "Administrator", we can add the following code in Page_Load:
Program code
if (User.IsInRole("Administrator"))
AdminLink.Visible = true;
The entire page code is as follows (for simplicity, the background code is also written in the aspx page):
program code
<html>
<head>
<title>Welcome! </title>
<script runat="server">
protected void Page_Load(Object sender, EventArgs e)
{
if (User.IsInRole("Administrator"))
AdminLink.Visible = true;
else
AdminLink.Visible = false;
}
</script>
</head>
<body>
<h2>Welcome! </h2>
<p>Welcome to Amu House http://amuhouse.com/ ^_^</p>
<asp:HyperLink id="AdminLink" runat="server"
Text="Admin Home Page" NavigateUrl="./Admin"/>
</body>
</html>
In this way, the HyperLink control linked to the Admin directory will only be displayed to users whose role is Administrator. You can also provide non-logged-in users with a link to the login page, such as:
program code
protected void Page_Load(object sender, System.EventArgs e)
{
if (User.IsInRole("Administrator"))
{
AdminLink.Text = "Administrator please come in";
AdminLink.NavigateUrl="./Admin";
}
else if(User.IsInRole("User"))
{
AdminLink.Text = "Registered users please enter";
AdminLink.NavigateUrl="./User";
}
else
{
AdminLink.Text = "Please log in";
AdminLink.NavigateUrl="Login.aspx?ReturnUrl=" + Request.Path;
}
}
Here, by setting the QueryString variable called ReturnUrl, we can return the user to the current page after successful login.
summary:
This article is used to help you understand the importance and practicality of role-based security mechanisms, and also uses ASP.NET to implement role-based security mechanisms. It is not a difficult mechanism to implement, but it may require some knowledge of what user credentials are, how to authenticate users, and how to authenticate authorized users. I'd be very happy if you found it helpful. I hope it can guide you in implementing role-based forms security authentication in your website.
Attached:
Sample project source code for this article: