run under a different account, or change the process account within the section of config.web, the "Process Model" account you use must have READ access to these folders.. run under a d
Trang 1
End Try
We can now save the personalization value(s) in the user's Session object so we can access them within the application pages without having to keep going back to the database We also tell ASP.NET to create the authentication cookie and redirect the user back to the page they originally requested:
If blnIsAuthenticated Then
'save background color in Session object
Session("BGColor") = strBGColor
' save other personalization settings here
'redirect user to original page
The Personalized default.aspx Page
Following a successful login, the user is redirected to the page default.aspx In it, we display their user name We also include the same Log Off button - we won't be describing those features again here
What is different is the way that we set the background color of the page, and display an appropriate set of hyperlinks depending on the current username The opening <body> tag in the page includes a bgcolor attribute, with the value set to a variable named strColor:
Trang 2<body bgcolor="<% = strColor %>">
This variable is declared in the <script> section of the page, and so is globally available throughout the page:
<script language="VB" runat="server">
Dim strColor As String
<ASP:Hyperlink id="lnkAdmin1" Text="Manage All Users"
Visible="False" NavigateUrl="http://dummy" runat="server" /><br />
<ASP:Hyperlink id="lnkAdmin2" Text="Change Application Settings"
Visible="False" NavigateUrl="http://dummy" runat="server" /><br />
<ASP:Hyperlink id="lnkAdmin3" Text="Display User Session Details"
Trang 3Visible="False" NavigateUrl="http://dummy" runat="server" /><br />
The <script> section of the page also contains an event handler that executes as the page is being created in response
to the Page-Load event This first checks that the user has been authenticated, and if so displays the authenticated user name:
Sub Page_Load()
If User.Identity.IsAuthenticated Then
'display welcome message
msgHello.InnerHtml = "Hello <b>" & User.Identity.Name & "</b>"
Next, it extracts the BGColor value from the user's Session (stored there when they submitted their login details) and sets the strColor variable so that the background color of the page is set to the appropriate color for this user:
'set preferred background color to the value that was
'saved in this user's Session object by the "login" page
strColor = Session("BGColor")
Now we can use the current login username to see if we should display the "administration" hyperlinks In our example,
we only display them if the user is sarahware:
Trang 4
'if user is "sarahware" display admin hyperlinks in page
If User.Identity.Name = "sarahware" Then
Other Personalization and Programmatic Security Options
This is a very simple example of personalization and programmatic security, but you can see how it can easily be extended For example we could:
Include more personalization options, such as the text color, font size and style, page layout, page content, etc
Include a page containing form controls where the user can select the personalization options and values, and then use a SQL statement or stored procedure to update these in the database
Store a custom "role" name for each user in the database table, giving us the ability to allocate users to separate
"roles" (rather like Windows account groups) We could then extract the role name for each user as they log in and perform programmatic security checks using this role name rather than the username
ASP.NET Process Model and Trust Levels
We've mentioned several times in this chapter how ASP.NET pages and associated resources such as Web Services, User Controls, Components, etc., run under the special ASP.NET process account named ASPNET by default (if impersonation
is not enabled) This account has broadly the same privileges as the IUSR_machinename account that is created when IIS
is installed (as used with ASP 3.0) However it has extra privileges granted beyond that of the IUSR account, due to the fact that ASP.NET takes advantage of dynamic compilation and disk caching features
Also, by default ("out of the box"), ASP.NET runs in a mode called Full Trust level, which applies few security limits on the code that can be executed This is fine for an Intranet scenario or a development machine, but once we place the server
Trang 5on the Internet we should consider reducing the permissions available to the process that the ASP.NET pages are running under
There are several different ways that we can configure reduced permissions in ASP.NET They all revolve around the ultimate decision that we have to make - which account should we run the pages under? Once we know which account is being used, or specify the one that we want to use, we can use the Windows ACLs on all the resources on our machine to limit and control access
We can also control access permissions to access other applications and services by changing the trust level We'll come back to this shortly
The ASPNET Process Account
To be able to generate Intermediate Language (IL) code and binary executable files using the compilers included with the NET Framework, the account under which the page executes requires more permissions than the equivalent in ASP 3.0 To accomplish this, an account named ASPNET is created by the ASP.NET setup routine This account is
automatically configured with the following access privileges:
The ASP.NET installation folder hierarchy (%installroot%) This is where the NET Framework assemblies and machine configuration files reside By default the ASPNET account has READ access to these folders If you configure an application to use impersonation (i.e run under a different account), or change the process account within the <processModel> section of config.web, the "Process Model" account you use must have READ
access to these folders
The folder used for dynamic compilation of ASP.NET pages and resources The root folder for this is
%installroot%\ASP.NET Temporary Files Application code generation occurs in a discrete directory beneath this folder for each application (the location of this root folder can be configured using the tempDir attribute of the <compilation> section of config.web) By default the ASPNET account has READ/WRITE
access to these folders If you configure an application to use impersonation (i.e run under a different account),
or change the process account within the <processModel> section of config.web, the account you use must have READ/WRITE access to these folders
The Global Assembly Cache (GAC) folder, where shared assemblies are located (usually %windir%\assembly)
By default the ASPNET account has READ access to this folder If you configure an application to use impersonation (i.e run under a different account), or change the process account within the <processModel> section of config.web, the account you use must have READ access to this folder
The folder used by Web Services to generate serialization proxies By default this is %windir%\temp, and the
ASPNET account has READ/WRITE access to this folder If you change the process account within the
<processModel> section of config.web, the account you use must have READ/WRITE access to this folder
The Default Web Site root folder (usually %systemdrive%\inetpub\wwwroot) and its subfolders By default the ASPNET account has READ access to these folders If you configure an application to use impersonation (i.e run under a different account), or change the process account within the <processModel> section of
Trang 6config.web, the account you use must have READ access to these folders If you want to write to the disk from
an ASP.NET page or other resource, you must enable WRITE permission for the target folder as well (in the same way as was required under ASP 3.0) ASP.NET will try to read a configuration file located at
\inetpub\wwwroot\web.config, and perform change monitoring on that directory
Your own application directories, where the application content resides By default the ASPNET account has
READ access to these folders If you configure an application to use impersonation (i.e run under a different account), or change the process account within the <processModel> section of config.web, the account you use must have READ access to these folders
By default ("out of the box"), ASP.NET runs in a mode called Full Trust level, which applies few security limits on the code that can be executed This is fine for an Intranet scenario or a development machine, but once we place the server on the Internet we should consider reducing the permissions that are available to the process running the ASP.NET pages
There are several different ways that we can configure reduced permissions in ASP.NET They all revolve around the ultimate decision that we have to make - which account should we run the pages under? Once we know which account is being used, or specify the one that we want to use, we can use the Windows ACLs on all the resources on our machine to limit and control access
We can also control access with the <processModel> element in machine.config (or in a web.config file placed in the application directory) This specifies which account is used when impersonation is not enabled:
Trang 7password="AutoGenerate"
/>
So, by default, all our ASP.NET pages and resources will be executed under the special process account - the account with the "moniker" of ASPNET This account generally has appropriate permissions set by default that allow ASP.NET pages and resources to execute And by modifying the permissions that this account has we can thereby control how ASP.NET will be able to access specified resources For example, to be able to write to the server's disk, the ASPNET account must have WRITE access permission for the target folder
However, there is another "moniker" value you can use here instead, namely "system", and with the password also set to
"AutoGenerate" This causes ASP.NET to run under a local SYSTEM account (as in Beta 2) To run ASP.NET under the
SYSTEM account, change the <processModel> element attributes as follows:
<processModel
enable="true"
userName="MyProcess"
Trang 8password="secret"
/>
Note that the settings specified in the <processModel> element are only applicable to ASP.NET, and do not affect other types of application or service running under the NET Framework
The Identity Element and Impersonation
The <processModel> element provides account details that are used only when impersonation is not enabled Recall from our discussions near the start of this chapter that turning on impersonation means that ASP.NET will run under the context of the account that is authenticated by IIS when a request is received If IIS is configured to allow anonymous access (the default for a Web site), then the context is that of the IUSR account (or the account you specified that IIS use for anonymous access if you changed this)
Simply adding the <identity impersonate="true"> element within the <system.web> section of the
machine.config or web.config files means that anonymous access will take place under the IIS anonymous account (IUSR_machinename)
Trang 9In fact there is a little more to it than this The <processModel> element specifies the account under which the worker process is run when it's enabled (it's not in IIS6 in native mode, for example) All threads start as the specified account When impersonation is enabled, they temporarily take on the impersonated context Calling the RevertToSelf method will always get back to the process account There are a couple of events that fire without a Request context being available, such as Application_OnEnd, and these always run with the ASPNET process account identity regardless of impersonation
Specifying the Trust Level
There is another option for controlling the permissions for ASP.NET to process resources This takes advantage of the
<trust> element in machine.config, and it can be used to set a more stringent "trust level" The default setting is
"Full", specified by this line from the default machine.config file:
<trust level="Full" originUrl="" />
The other options are "High", "Low", and "None", and these apply progressively more stringent limitations on the permissions that code running under the NET Framework will have The <securityPolicy> element specifies which security configuration file applies to each of the trust levels:
<securityPolicy>
<trustLevel name="Full" policyFile="internal" />
Trang 10<trustLevel name="High" policyFile="web_hightrust.config" />
<trustLevel name="Low" policyFile="web_lowtrust.config" />
<trustLevel name="None" policyFile="web_notrust.config" />
</securityPolicy>
Each of these files has sections that describe the permissions that are available to NET framework applications and code Some examples of the permissions that can be set are:
Which environment variables the code can query
Which directories the code can write to through the file I/O classes
Whether DNS enquiries are allowed
Whether blank passwords can be used with the ADO.NET data providers
Whether messages can be sent to, and received from, the Message Queue Service
Whether access to a printer is permitted
Which performance counters can be accessed
You can change the trust level and edit the "trust" configuration files to finely control the permissions available to your code and resources Remember that the settings specified in the <trust> element are applicable to the whole of the NET framework, not just ASP.NET
A simple configuration Wizard is provided with the Frameworks that can be used to change the trust level Select Programs | Administrative Tools | Microsoft NET Framework Wizards, and select the "Adjust NET Security" icon:
*** Insert picture: 4885v2-14-90.bmp
Trang 11*** to right of above paragraph
This Wizard allows you to specify which trust level each Internet Zone should run under The defaults are shown in the next screenshot:
The topic list for this chapter was:
Trang 12 An overview of the security model in Windows 2000 and IIS.
An overview of the new security features in ASP.NET
The different types of access control that we can implement with ASP.NET
A detailed look at how we apply the ASP.NET security and access control features
A brief overview of the "trust" model
Web application security is based around the three fundamental concepts of authentication (forcing a user to prove that they are who they say they are), authorization (checking if the user has permission to access the resource they requested), and impersonation (allowing applications to be executed under the context of a different user)
We looked at each topic in turn, and saw how they are implemented and configured in IIS, in Windows 2000, and in ASP.NET We also saw the whole chain of events that occur as part of the overall process, and the various access control options that they provide
We then concentrated on ASP.NET security configuration, and saw how our three fundamental concepts are implemented through the web.config files we place in our application folders We completed this chapter with some examples of creating and configuring secured applications using the various techniques:
Configuring a Web application using Windows authentication
Accessing the user's identity within this application
Accessing the user's role within this application
Configuring a Web application using Forms-based authentication
Using different types of user credentials lists
Accessing the user's identity within this application
A simple personalization example
In the next chapter, we change topics to start an in-depth look at some of the base classes that are provided by the Framework In particular, we'll investigate data structures such as Collections and Lists
Trang 13Working with Collections and Lists
Over the years Microsoft Windows has grown into an enterprise-caliber operating system that millions of companies and users worldwide depend on daily Windows NT and Windows 2000 have proved themselves as solid and stable operating systems, providing scalability, reliability, and return on investment (ROI)
The one area where Windows suffers badly today compared with its main rivals, is the complexity of its developer platform Yes, Windows has fantastic tools like Visual Studio, but Windows supports ever-growing numbers of APIs and object models that are increasingly inconsistent, fragmented, and difficult to learn Also, developers are penalized depending on their choice of programming language or technology (such as ASP)
The complexity of developing on the Windows platform is a problem that Microsoft has understood for some time A major objective for NET was to bring simplicity and consistency to the Windows development platform, making it more competitive with Java in that respect, without sand-boxing developers or sacrificing performance From a consistency and accessibility viewpoint, the Common Language Runtime (CLR) provides the foundations that enable this The NET Framework uses the CLR to provide a clean object-oriented approach to development by grouping classes within hierarchical namespaces, as well as using making the functionality of the platform simple and consistent
A key part of an object-oriented development platform like NET is its Base Class Library The classes in this library provide core functionality, with which developers can build their own application and class libraries If you have ever programmed with the C/C++ Standard Template Library (STL), used the VB.NET scripting runtime objects, or used the Java SDK, you'll have a good idea of the type of functionality that the NET Framework base classes provide
In this chapter and the next one, we're going to examine some of the commonly-used classes in the NET Framework Base Class Library that have been designed to allow applications to be built quickly and elegantly There are far too many classes in the NET Framework to cover them all in a single book, so we're going to focus on collections in this chapter, and then cover files and regular expressions in the next chapter
Computers are essentially designed to store and manipulate data and ever since I started programming (which feels like
a very long time ago now) I've spent a sizable chunk of my time writing code to manage sets of data held in different types
of data structures, such as queues, dictionaries, and stacks I doubt that I'm the only developer who's spent time doing this and because working with different data structures is such a universal and common requirement, the NET Framework provides an impressive class library for dealing with common data structures
Trang 14By the end of this chapter you will:
Understand the support provided by the NET Framework for working with common data structures such as lists, queues, stacks, and dictionaries
Have a working knowledge of the most important collection interfaces and classes in the System.Collections and System.Collection.Specialized namespaces
Know how to build your own strongly-typed collection classes
For the examples in this chapter, I used a virtual directory in IIS called Collections to map to the Collections
directory located in the downloadable source code for this book This is reflected in all screenshots
Working with Collections and Lists
The NET Framework contains thousands of types, a large proportion of which are data structures that are enumerable- that is, they support the ability for a contained or associated collection of items to be accessed in a sequential or key-based (random access) way
To assist in working with enumerable types, the System.Collection namespace provides:
Collection interfaces that define standard methods and properties implemented by different types of data structures These interfaces allow enumerable types to provide consistent functionality, and aid interoperability
Functionality-rich implementations of many common collection classes such as lists and dictionaries These all implement one or more of the common collection interfaces
Collection Interfaces
Dealing with enumerable classes is a common task for developers, so the NET Framework class library includes a set of interfaces in the System.Collections namespace that define contracts (of functionality) that enumerable classes implement These interfaces provide consistency throughout the framework classes, making the life of a developer an easier one Once we know how to work with one enumerable class that supports one or more common interfaces, we should, in theory, be able to work with any other enumerable class that supports the same interface in a uniform way, including the custom types that other developers create
As developers, it's in our best interests to understand the collection interfaces in the System.Collections namespace There aren't too many, and once we understand how they are organized we can examine the interfaces that any enumerable type implements or returns from properties or methods, and determine what enumerable support a given type has For example, any type that implements the IEnumerable interface supports forward-only iteration through its contained item If a collection implements this interface it also means that we can use the Visual Basic.NET and C#
Trang 15for each declaration with it
During compilation, compilers convert for each declarations into calls to IEnumerable and its associated interface
IEnumerator
It's worth mentioning early on that most of the collection classes have many members (methods, properties, and so on) that are not defined in standard collection interfaces The collection interfaces exist to define a common usage pattern across many different collection classes The implementation of a given data structure, such as a queue, has many unique characteristics that are not defined in a standard interface- they are just members of a particular type
We'll focus mainly on the members defined by common interfaces in this chapter, but there are many more methods available on most of the types we cover, all of which are documented in the NET SDK Let's start by examining the core collection interfaces and some of the classes that implement them
The System.Collections Core Interfaces
The core collection interfaces defined in the System.Collections are:
The IEnumerable and IEnumerator Interfaces
A type that implements the IEnumerable interface indicates to consumers that it supports the notion of forward-only access to its items, using an enumerator object An enumerator object provides a forward-only read-only cursor for a set
of items
The IEnumerable interface has one method, GetEnumerator:
public interface IEnumerable
Trang 16{
IEnumerator GetEnumerator();}
This method returns a new instance of an enumerator object each time it is called The returned object implements the IEnumerator interface, which has methods that can be used to sequentially access the set of System.Object types exposed by an enumerable type The enumerator object supports retrieving the item at the current cursor position, or resetting the cursor back to the beginning of the item set
The built-in Array type supports the IEnumerable interface The following code shows how to declare a simple string array (although any type of array could be used), call the GetEnumerator method to create an enumerator object, and use the methods of returned IEnumerator interface to sequentially access each item in the array Using Visual Basic.NET
we would write:
Dim authors As string()
authors = New string() {"Richard","Alex","Dave","Rob","Brian","Karli"}
Dim e As IEnumerator
e = authors.GetEnumerator()
Do While e.MoveNext() = True
Response.Write("<p>" & e.Current)
Loop
Using C# we would write:
string[] authors = new string[6] {"Richard","Alex","Dave","Rob","Brian","Karli"};
IEnumerator e;
e = authors.GetEnumerator();
Trang 17while( e.MoveNext() == true )
in a collection, it returns false We can therefore safely loop through all items in the collection by calling MoveNext while
it returns true, exiting when it returns false
The NET Framework guidelines state that once an enumerator object is created, it takes a snapshot of the items contained within an enumerable object at that point in time If the original object is changed, the enumerator becomes invalid, and the enumerator object should throw an InvalidOperationException the next time one of its methods is called All of the NET Framework classes follow these guidelines, as should the enumerable types that we write For reasons of performance, the enumerators implemented in the NET Framework class library don't actually copy all the items when an enumerable object is created Instead, they just maintain a reference to the enumerable object, and provide a logical snapshot It's much cheaper to maintain a reference and an index to the original enumerable object- copying each and every item would be an expensive process for a large collection
A simple versioning scheme is used to implement the actual semantics of a logical snapshot Each time an enumerable object is changed (for example, an author is added or removed from our array), it increments a version number (think of this as a change counter) When an enumerator object is created it copies the current version number of the enumerable object Then, each time an enumerator object method is called the enumerator compares its stored version number to the enumerable object's current version number If these version numbers are different, the enumerator throws an InvalidOperationException
The Current property of the IEnumerator interface is defined as the System.Object type In our earlier code, we didn't have to cast the returned object from Current before using it, since Response.Write will call the ToString method for us However, if we wanted to store the underlying string type in our example, we would typically cast it For example, using Visual Basic.NET we would write:
Dim author As String
author = CType(e.Current, string)
Trang 18Using C# we would write:
string author
author = (string) e.Current;
All of the collection interfaces in the System.Collections namespace use the System.Object type, which gives them great flexibility because they can be used with any type However, this generic approach does mean that the CLR must perform type-conversion checking for most calls, which imposes a small performance overhead
The for each Statement
Visual Basic.NET and C# both have a statement that calls the enumerator directly C# has the foreach statement and Visual Basic.NET has the For Each Next statement (we'll refer to these as for each statements) Both these languages implement their for each functionality using the IEnumerable and IEnumerator interfaces This means that we could change the author example from earlier to use a for each rather than a while statement Using Visual Basic.NET we would write:
Dim author As string
For Each author In authors
Response.Write("<p>" & author)
Next
Using C# we would write:
foreach( string author in authors )
{
Response.Write("<p>" + author );
}
Trang 19Using the for each statement requires less code than using the IEnumerable and IEnumerator interfaces directly, but there will be times when it is not advisable (or even possible) to use the for each statement For example, if we need to create some generic functionality that doesn't deal with concrete types if would be better not to use the for each statement and if we had a loop that must be performed across several method invocations it would not even
be possible to use the for each statement
Provided that a type has a GetEnumerator method that returns a type derived from IEnumerable, it does not have to implement the IEnumerable interface for the for each statement to work with it in C# or VB.NET However, unless you have a very good reason for not doing so, your enumerable types should implement IEnumerable- that way they will be in accordance with the guidelines that the rest of the framework follows
All other enumerable types in the NET Framework class library derive from the IEnumerable interface This means that, although other enumerable types provide additional members for accessing the items they contain, all of them also support the forward-only cursor approach using enumerator objects The same is true for the IEnumerator interface For these reasons, all other enumerator interfaces derive from IEnumerator
The following diagram shows the interface inheritance for the core interfaces:
If you've not seen a UML class diagram before, don't worry, you can read this one as follows:
The IList and IDictionary interfaces derive from ICollection, which in turn derives from IEnumerable
The interface IEnumerable is associated with IEnumerator
The interface IDictionaryEnumerator derives from IEnumerator
Types that implement the IEnumerable interface can be enumerated in VB 6 and other COM-aware languages using COM interop, since the GetEnumerator method will be exposed with a DISPID of -4 You should refer to MSDN for more information
Trang 20The ICollection and IList Interfaces
Enumerating through a collection sequentially is a common task, but it's also useful to be able to directly access items using a key or an index For example, to check if a specific author exists in our array of authors from earlier, we could use the static Array.IndexOf method Using Visual Basic.NET we would write:
Dim index As Integer
index = Array.IndexOf(authors, "Richard")
Here we're using the Array.IndexOf method to retrieve and store the index of a specific author in index If the value
of index is not -1 (which would mean that the author was not found), we use the index to display the value held at that offset within the array Under the hood, this method searches the array item by item, performing a comparison against each one When a match is found, the index is returned
The IList interface defines methods and properties that allow us to work with arrays of System.Object items, such as our string array of authors The IList interface defines methods and properties that allow us to: