Implementation of ASP. NET Form Verification

Source: Internet
Author: User
Implementation of ASP. NET Form Verification

For form authentication of Web applications, because the company has a class library that adopts session implementation, it has never been carefully understood. In fact, I do not agree. net uses session for identity authentication. After all. NET provides a powerful authentication system, and the company's class library does not implement any special functions, just save a session variable to provide identity recognition, in terms of security and availability. compared with the implementation of net, I personally feel that there is a big gap.

I have seldom worked overtime recently, so I just took a look and thought out a rough idea.

First, configure web. config, Which is set under <system. Web>:

<Authentication mode = "forms">

<Forms name = ". sometsteauth"

Loginurl = "admin/login. aspx"

Defaulturl = "admin/index. aspx"

Path = "/"

Timeout = "10">

</Forms>

</Authentication>

<Authentication> mode = "forms" indicates that Web applications use form verification. In addition, "Windows", "Passport", and "NONE" are used. "Windows" is usually used in LAN, with ad for identity authentication, "Passport" seems to have to pay for Microsoft before it can be used. It is not clear. "None" indicates that verification is not performed.

<Forms> common attributes:

The name attribute specifies the name of the cookie required for verification. The default value is ". aspxauth". If multiple web applications are attached to or from a serverProgram, You must re-specify this name because each application requires a unique cookie.

The loginurl attribute specifies the logon page to provide the user name and password. The default value is "login. aspx ". This page can be stored in the same directory as the page that requires identity verification (Oh, I thought this page should be placed in a separate publicly accessible directory ).

The defaulturl attribute specifies the page to jump to after login. The default value is "default. aspx". Of course, you can also jump to the previous page before user login, and this is the default implementation of. net.

The path attribute specifies the cookie path. The default value is "/". For most browsers, the Cookie Path is case-sensitive. Therefore, if the path is case-insensitive, no cookie will be sent back. (Note: "/" refers to the root directory of the website. during development, Visual Studio usually creates a new directory under the root directory of the website as the root of the Web application, such as http: // localhost/mysite, to set the path value to "mysite/admin" instead of "admin" for the Web application to access the files in the Admin directory, otherwise the user will not be able to log on normally .)

The timeout attribute specifies how long the user does not perform operations. The identity creden。 expire in minutes. The default value is 30 minutes.

After <authentication> is set, you need to set <authorication>. The most common method is as follows:

<Authorization>

<Deny users = "? "/>

</Authorization>

"?" Number indicates anonymous users, and "*" indicates all users. See some documents on the Internet, in <deny users = "?" /> The following sentence <allow users = "*"/> does not need to be added unless the role is used to control the permissions of each sub-directory. This is a potential danger. If someone accidentally places <allow users = "*"/> in <deny user = "?" /> The system will not perform verification.

To perform separate permission control on sub-directories, you need to add a <location> segment to the root element of Web. config <configuration>:

<Location Path = "admin">

<System. Web>

<Authorization>

<Deny users = "? "/>

</Authorization>

</System. Web>

</Location>

If multiple sub-directories are involved and different permissions are assigned, you need to use the role. Replace the content under <authorization>

<Allow roles = "admin"/>

<Deny users = "*"/>

Web. in the configuration of config, note that only one web application can be configured in <authentication> section, that is, only one authentication method can be used in one web application, in the <location> section, you can configure multiple permissions for different directories. For directories without the <authorization> section configured, the access permission is the same as that of the Web application root. If the application root is not configured with <authorization>, it is accessible to anyone by default, even if the mode attribute of <authentication> is set to "forms ".

The second step is encoding. On the logon page, click "Log on" in the event:

If (username. Text. Trim () = "your username" & password. Text = "your password ")

Formsauthentication. redirectfromloginpage ("your username", false );

Else

// The user name and password are incorrect.

The first parameter of redirectfromloginpage () is the user name currently being verified. The second parameter indicates whether to save the login information to the cookie for a long time. NET 2.0 and. in. NET 1.1. in. NET 1.1, the login information is saved to the cookie and the expiration time is set to 50 years later, that is, you no longer need to enter the user name and password, unless the cookie is deleted or 50 years later (you add an antique computer, but the most beautiful one ). In. in. NET 2.0, this parameter only indicates whether the logon information still exists in the cookie after the browser is closed, and the expiration time is still valid, that is, after the cookie expires, whether you restart your browser or not, you still need to enter the logon credential.

CodeThis is simple and unexpected ,. net will automatically create a ticket token and redirect it to the page accessed by the user before logon that requires verification. If the user directly accesses the logon page, it will be redirected to the Web. the default page defined in config. If you need to control the redirection process by yourself, you can do this:

If (username. Text. Trim () = "your username" & password. Text = "your password ")

{

Formsauthentication. setauthcookie ("your username", false );

Response. Redirect ("index. aspx ");

}

Else

// The user name and password are incorrect.

In fact, the formsauthentication. redirectfromloginpage ("your username", false) Statement is equivalent

Formsauthentication. setauthcookie ("your username", false );

Response. Redirect (formsauthentication. getredirecturl ("your username", false ));

Of course, you can also manually create a ticket token and add it to the response Cookie set. The complete code is as follows:

If (username. Text. Trim () = "your username" & password. Text = "your password ")

{

// Create a new ticket for the current Login User

Formsauthenticationticket ticket = new formsauthenticationticket (

2, // version number

"Your username", // user name for Logon

Datetime. Now, // date of release of ticket

Datetime. Now. addminutes (15), // The expiration time of the ticket

False, // whether the logon information is retained after the browser is closed

"", // A small amount of user data can be added (Note: The value cannot be null)

Formsauthentication. formscookiepath // Cookie Path

);

// Encrypted ticket token to obtain the encrypted string

String encrypt = formsauthentication. Encrypt (ticket );

// Create a cookie using the encrypted string

Httpcookie cookie = new httpcookie (

Formsauthentication. formscookiename,

Encrypt

);

// Add the cookie to the client

Response. Cookies. Add (cookie );

Response. Redirect (formsauthentication. getredirecturl ("your username", false ));

}

Else

// The user name and password are incorrect.

 

It should be clear that form verification uses a specific cookie to verify whether the cookie exists every time you connect to the server, so as to determine whether the user has the relevant permissions. In the above code, you can also add the code for Cookie control. After using the encrypted ticket token to create a cookie, add the Code:

// The HTTPOnly attribute is true, indicating that the cookie cannot be accessed on the browser.

Cookie. HTTPOnly = true;

// Cookie path, which is the value of the <forms> attribute path in Web. config

Cookie. Path = formsauthentication. formscookiepath;

// Set the cookie expiration time, which is the same as the expiration time of the ticket renewal. If the two times are different, any expiration time is deemed as the expiration time.

Cookie. expires = ticket. expiration;

 

After clarifying the general mechanism of Form Verification, role-based form verification is easy to handle. The general process is described as follows:

1. In the code file on the logon page, add a common cookie to save the user's role to the cookie (a user can have multiple roles ).

2 In Global. in asax's authenticaterequest event, determine whether the user has passed the verification. If the user has passed the verification, remove the role string from the cookie added by 1 and construct a system. security. principal. genericprincipal object. The constructor of this object includes two parameters: User ID and role array. the user ID can be accessed through httpcontext. current. user. identity is obtained. The role array converts the role string to a string array and assigns it to it.

3. Adjust web. config to set the role permissions.

In the past two days, we have re-designed user verification in the ERC project. At the beginning, we have implemented simple use of ASP. net Form Verification monitors user login and security checks. However, a user permission assignment and UI display based on user permissions are involved, therefore, to improve the reading method of the original session, we mainly consider the session instability.
The first thought is to use the append to the page. the genericprincipal object of the user is displayed according to the help information of msdn, it can be seen that the genericprincipal object is a common user object designed by Microsoft to use form to verify the permission to read the active domain or IIS users, of course, we can manually attach our data to the Change object and bind the change object to the page. user object. After a long debugging, this idea was basically implemented yesterday, that is, Friday. The specific code is as follows:
// Construct the ticket string of the manual ID card
String m_userdata = username. tostring () + "," + strrole. tostring () + "," + lastloginip. tostring () + "," + lastlogintime. tostring ();
// Create an ID card ticket object named "ticket" with the built-in name "ercuser". The object expires for 20 minutes, including the user name, user permission, user's last logon IP address, and user's last logon time.
Formsauthenticationticket ticket = new formsauthenticationticket (
1, "ercuser", datetime. Now, datetime. Now. addminutes (20), false, m_userdata ,"/");
// The encrypted serialization verification ticket is a string
String encryptticket = formsauthentication. Encrypt (ticket );
// Generate
// Http erc = new HTTP (formsauthentication. formsname, encryptticket );
Http erc = new HTTP ("webbuser", encryptticket );
// Add to context Context
Httpcontext. Current. response. S. Add (ERC );
Response. s ["webbuser"]. expires = datetime. Now. addminutes (20 );
Because I implemented it in the callback mechanism, I did not call the page callback. Therefore, the following statement may need to be called if it is not a new mechanism of refreshing traffic.
// Context. response. Redirect (context. request ["returnurl"]);

The above Code builds. net formauthenticaion encryption object, during the debugging process, originally used formsauthentication. formsname is stored as this name (I do not know formsauthentication. what is formsname? Go to msdn, this is pp), but after N times of debugging, I found that I was in global. the reason why asax reads information is that it should not use the default name of the system. This lesson is very painful. After n times of debugging, I found the cause of the error here, later, when tracking and debugging, I found that as long as my web page is refreshed, the system's default name information will change. Why? I don't know yet... Later, I changed the webbuser name to read the encrypted information.
Then we can handle the application_authenticaterequest event in global. asax. The implementation code is as follows:

// Add the information to the genericprincipal object that indicates the user identity on the server in global. asax

Http erc = CTX. Request. s [formsauthentication. formsname];
Http erc = CTX. Request. s ["webbuser"];

Formsidentity id = (formsidentity) CTX. User. identity;
Formsauthenticationticket ticket = NULL; // get the authentication ticket
Try
{
Ticket = formsauthentication. decrypt (ERC. value );
}
Catch (exception ex)
{
// Log exception details (omitted for simplicity)
Return;
}

If (null = ticket)
{
// Failed to decrypt.
Return;
}

string [] roles = ticket. userdata. split (','); // convert the role data in the authentication ticket to a string array
CTX. user = new genericprincipal (ID, roles); // Add the original identity with the role information to create a genericprincipal to indicate the current user, so that the current user has the role information

in this way, the m_userdata string you constructed earlier will be added to the page. user's gennericprincial object. Although the string is successfully added, the user is found during tracking and debugging. identity's gennericprincial object does have a collecation object that contains m_userdata, but I cannot read them because in msdn's explanation of gennericprincial objects, there is only one inrole () method, which only provides detection for all user roles (this role represents an active domain or a Windows User role), and will be read later using the event reflection mechanism, but it eventually ended in failure, because I don't know which method should be used for reflection .... Really failed .. The Code is as follows:
iidentity = user. identity;
methodinfo method = identity. getType (). getmethod ("isinrole", bindingflags. instance | bindingflags. nonpublic);
string [] rolenames = (string []) method. invoke (identity, new object [] {});
bool dd = user. isinrole ("test");
system compilation error. The error message does not exist .... -_-
after one day and one night of tossing, the train of thought for improving Form Verification finally collapsed, crying ~~ However, through debugging, we further learned about the form verification mechanism and the order of page loading processing: (you cannot draw a picture here, and you have the opportunity to draw another picture.)

Since the attachment permission data is transferred to the user. if identity fails, you can only use another method. Of course, some people may suggest that you can write iprincial interface to overload the control over the user object, which makes it more complicated, the dual workload has not been mitigated. Since it is too lazy to write interfaces (it should not be written), you can only write the base page class to control the session and write a basepage. CS base class, from system. web. UI. page inheritance, reload the init () event, and check whether the session exists in the event. If the session does not exist, re-read and reset it from the database. Of course, this process must be controlled after the user passes the ticket verification, the following code is used;
# Region event processing
Protected override void oninit (eventargs E)
{
Base. oninit (E );
// Perform session detection after login to prevent application crash due to session Stability Failure
If (user. Identity. isauthenticated)
{
If (session ["role"] = NULL | session ["username"] = NULL)
{
Member member = new member ();
Sqldatareader sdr_member = member. getuserinfo (user. Identity. Name. tostring ());
While (sdr_member.read ())
{
Session ["role"] = sdr_member ["role"]. tostring ();
Session ["username"] = sdr_member ["username"]. tostring ();
}
}
}

This. Error + = new system. eventhandler (this. pagebase_error );

}

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.