You want to restrict access to many, but not all, of the pages in your application (i.e., you want to make some pages accessible to the public).
Implement the solution described in Recipe 8.1 and then modify the contents of the
web.config
file to list the pages that allow
public access and those that require authentication.
Modify web.config
as follows:
Change the <deny>
child element of the
<authorization>
element to
<deny
users="*"/>
and
delete the <allow>
child element to deny
access to all users.
Add a <location>
element to the
configuration level for each application page to specify whether it
is available to the public or only to authenticated users.
Example 8-5 shows how we have implemented this
solution with some sample web.config
entries. We
begin by adding settings that deny access to all users. We then add
settings that allow public access to PublicPage.aspx but restrict access to
Home.aspx only to authenticated
users.
The approach we advocate for this recipe is the same as for
Recipe 8.1, except for certain aspects of
the web.config
file configuration.
The <authentication>
element and its
<forms>
child are the same as in
Recipe 8.1.
We have modified the <authorization>
element
that we used in Recipe 8.1 to deny
access to all users. By denying authorization to all users at the
application level, elements can be added to authorize access to
particular pages.
Access to the individual pages in the application is controlled by
providing a <location>
element for each
page. For pages with public access, the
<location
> element should be set up as
follows:
<locationpath="PublicPage.aspx">
<system.web> <authorization><allow users="*"/>
</authorization> </system.web> </location>
The path
attribute of the
<location>
element specifies the page that
this <location>
element applies to. The
<authorization>
element defines the access
to the page defined in the path
attribute. For
public access, the <authorization>
should be
set to allow all users (users="*
“).
To limit access to individual pages, a
<location>
element is provided for each of
the restricted pages, as follows:
<locationpath="Home.aspx"
> <system.web> <authorization><deny users="?" /> <!— Deny anonymous (unauthenticated) users —>
<allow users="*"/> <!— Allow all authenticated users —>
</authorization> </system.web> </location>
The primary difference is the
inclusion of two <authorization>
child
elements. The <deny>
element denies access
to anonymous (unauthenticated) users. The
<allow>
element allows access to all
authenticated users. Notice that we’ve used two
special identities in the code: ?
and
*
. Here ?
refers to the
anonymous identity, and *
refers to all
identities.
At first glance this code sequence would appear to allow access to
all users. ASP.NET processes authentication and authorization in a
hierarchical fashion, starting at
machine.config
, followed by the
web.config
for the application, and then any
web.config
files located in folders in the path
to the requested page. As soon as ASP.NET finds the first access rule
that applies to the current user, the rule is applied. The rule
evaluation continues, and if any other rules are found that reduce
the access, they are applied; otherwise, they are skipped. In this
case, the <deny users="?" />
rule is
processed first. The <allow users="*"/>
rule
is processed second, and because it does not reduce the access, it is
skipped; therefore, if the user is not authenticated, he is denied
access. As you can see by this example, the precise ordering of the
<allow>
and <deny>
elements is important. They must be in most restrictive order, from
top to bottom.
One of the disadvantages of using a
<location>
element for each page you wish to
make accessible to authenticated users is the maintenance of all the
page names in the <location>
elements. For
an application with 50 pages, it is not a big problem; however, for
an application with a large number of pages, it can be a big task.
ASP.NET provides another mechanism that is described later to make
the process a little easier.
Even though this approach may have some maintenance issues, it does
have the advantage of providing more control over the individual
pages served by your application. For instance, without modifying the
web.config
file, no one will be able to add a
page to your application that will be viewable through the web
server. If someone does succeed in adding a page, attempts to access
it will result in a redirection to the login page.
If the idea of maintaining a <location>
element for each page in your application is not appealing, you can
structure your application to place public pages and private pages in
separate folders, and then provide one
<location>
element for the public folder and
a second one for the private pages:
<!-- **************************************************************************** The following location element provides public access to all pages in the PublicPages folder. **************************************************************************** --><location path="PublicPages">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
<!-- **************************************************************************** The following location element restricts access to all pages in the PrivatePages folder. **************************************************************************** --><location path="PrivatePages">
<system.web>
<authorization>
<deny users="?" /> <!— Deny anonymous (unauthenticated) users —>
<allow users="*"/> <!— Allow all authenticated users —>
</authorization>
</system.web>
</location>
Using <location>
elements to define separate
folders for public and private pages is analogous to their use to set
authorization requirements for individual pages, except the
path
attribute is set to a relative path to an
applicable folder rather than to a specific
.aspx
page.
Nothing comes for free. The folder approach to controlling security
makes the maintenance of the web.config
file
simpler; however, it has two drawbacks. First, any page that is
placed in a designated folder becomes part of your application,
whether you want it to or not. Second, sharing images and user
controls is much more difficult. This is the result of having to
provide a relative path from the requested page to the image or user
control. It becomes even more difficult if a user control contains
images and is used in the root folder as well as a subfolder.
Recipe 8.1; MSDN documentation for
more information on web.config
format (search
for “Format of ASP.NET Configuration
Files”)
Example 8-5. web.config entries to restrict access to some pages
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.web> .. <authentication mode="Forms"> <forms name=".ASPNETCookbookVB2" loginUrl="login.aspx" protection="All" timeout="30" path="/"> </forms> </authentication> <authorization><deny users="*" /> <!-- Deny all users -->
</authorization> ..<!--
****************************************************************************
The following section provides public access to pages that do not
require authentication. An entry must be included for each page
that does not require authentication.
****************************************************************************
-->
<location path="PublicPage.aspx">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
<!--
****************************************************************************
The following section defines the pages that require authentication
for access. An entry must be included for each page that requires
authentication.
****************************************************************************
-->
<location path="Home.aspx">
<system.web>
<authorization>
<deny users="?" /> <!-- Deny anonymous (unauthenticated) users -->
<allow users="*"/> <!-- Allow all authenticated users -->
</authorization>
</system.web>
</location>
</configuration>