How not cache an ASP.NET user control? - asp.net

I'm using OutputCache in my page that has a user control, but I don't want to cache this specific user control because it's related to a user login (if I access the page, I see the page as if I were authenticated with another user).
How can I do that?

Personally I use the VaryByCustom attribute to give logged in and logged out users different cached page views:
<%# OutputCache VaryByCustom="IsLoggedIn" Duration="30" VaryByParam="*" %>
then in global.asax you put
public override string GetVaryByCustomString(HttpContext context,
string arg)
{
if (arg == "IsLoggedIn")
{
if (context.Request.IsAuthenticated)
{
return "Logged in: " + context.User.Identity.Name;
}
else
{
return "Not Logged In";
}
}
else
{
return base.GetVaryByCustomString(context, arg);
}
}
I am just going to throw this out there. How about the substitution control?
http://msdn.microsoft.com/en-us/library/ms228212.aspx
According to msdn website:
The Substitution control lets you
create areas on the page that can be
updated dynamically and then
integrated into a cached page. ...
The Substitution control offers a
simplified solution to partial page
caching for pages where the majority
of the content is cached. You can
output-cache the entire page, and then
use Substitution controls to specify
the parts of the page that are exempt
from caching.
I have never used the substituion control personally, but I just happened to look it up the other day, and it sounded like it can somehow inject updated content into an otherwise cached page output.

You can cache a page and you can cache a user control, but you can't cache a page except for a user control. When the user control runs the entire page has to run. You have to make the output cache for the page recognise the different users.
You can use VaryByHeader="Cookie" to cache the page for each set of cookies if the user identity is stored in a cookie. You can use VaryByCustom="SomeString" and implement a check for SomeString to do your own check for user identity in the GetVaryByCustomString method in Global.asax.

You can create a cache filter : http://weblogs.asp.net/rashid/archive/2008/03/28/asp-net-mvc-action-filter-caching-and-compression.aspx
Check inside this filter if the user is logged or not.

Related

Prevent viewing a restricted page page on pressing back/forward button

I am trying to implement Login/Logout functionality in my website without using inbuilt functionality of Login controls in ASP.NET. In some pages, which require the user to be logged in, I have written this in Page_Load
if (Session["cod"] == null && Session["admin"] == null)
{
Response.Redirect("You need to Login.aspx");
}
if (Session["cod"] != null || Session["admin"] != null)
{
LinkButton1.Text = "Logout";
}
if (Page.IsPostBack == false)
{
log_bind();
grid1_bind();
grid2_bind();
}
But while I was testing this, I noticed that when I press the Back/Forward button on the browser, these pages are viewable without being logged in. How do I prevent this?
This has nothing to do with the login controls, but as others state, the caching of the page.
The trick is to tell the browser that it can't cache the page.
Look at this post, and its solution:
Disable browser cache for entire ASP.NET website
I think that even if you do not use ASP.NET login controls you should still use the Principal/Identity classes and verify if a user is Authenticated or not. That is surely the safest way.
I don't know of any reliable way to do this. Once a page has been viewed, it's on the user's computer. If they hit the back button, they are looking at a cached version anyway so I can't imagine why this would be an issue.
As long as they can't refresh the page to get the latest content, what does it matter if they're able to look at a page they already accessed?
Have you tried wrapping the whole function in
if (!IsPostBack)
{
}
The browser may simply be showing you a cached version of the page, try to attach a debugger to the page load event and check to see if:
It is actually hitting the server when you hit back and forward
The values in the session state, whether they are consistent with a logged out user.
If the values in the session are consistent with a logged in user then you have to check your session clearing logic.
It is however best to use the asp.net controls or the system.web.security.FormsAuthentication class to perform functions like logging in and logging out based on custom logic.

Display message to user when forms authentication session expires

This seems simple and I remember doing it a couple of years ago.
I simply want to display a message on the login page when the user is automatically redirected there after requesting a page that they were logged in for but their session has now expired. So essentially if the user was working but stepped away for a lunch break without logging out I want the system to tell them why they were sent back to the login page.
Something like "You have been idle for too long so you must log back in".
This has to be easy I am just running into a wall here. I thought about getting the original ticket and reading the expiration date but I'm a little lost.
Any ideas?
Brent
Try this JavaScript on for size:
Make sure that a property called LoginPageUrl exists on the code behind. Great for Master pages.
If you want to register the script from code-behind, you could even pull the session timeout from the application and inject it so that you still only have one place (web.config) to update it.
To display a message to the user after redirecting them to the login page (.NET will take care of expiring the cookie), send a query string parameter that the login page looks for and shows a message indicating that the user was logged out due to inactivity.
<head>
...
</head>
<body onload="logoutOnExpire();" >
<script type="text/javascript">
// ACTIVITIES TO RUN FOR THE PAGE
function logoutOnExpire() {
clearTimeout(logoutTimeout);
logoutTimeout =
setTimeout('location.href = '<%= LoginPageUrl %>';', 1200000);
// 20 minutes
}
</script>
<form id="form" runat="server">
...
</form>
</body>
</html>
You can check the session in the inner page and if session does not exist,Redirect to the login page with some value in querystring to understand from which page this call came.When user logged in back,You can use the querystring value to determine which page to be displayed back.
MyPage.aspx.cs,In Page load you can check,
if(Session["user"]==null)
{
Response.Redirect("Login.aspx?from=mypage");
}
else
{
// Do the other stuff for the loged in user
}
And In Login.aspx.cs,In the code where you check your login details from the form
string userName=txtUserName.Text;
string password=txtPass.Text;
if(IsValidLogin(userName,password)
{
string toUrl="defaul.aspx";
if(Request.QueryString["from"]!=null)
{
string fromPage=Request.QueryString["from"];
if(fromPage=="mypage")
{
toUrl="mypage.aspx";
}
else if(fromPage=="review")
{
toUrl="review.aspx";
}
}
Response.Redirect(toUrl);
}
If what you want is to send the user to a page other than the login page when they cause a server postback after their session expires, use the following code at the top of the Page_Load event (this may not work if .NET executes it's redirect first).
if(!Context.User.Identity.IsAuthenticated)
{
Response.Redirect("~/OtherPage.aspx", false);
}
If you create a base page in your website that all pages inherit from, add it to that page's Page_Load.
If you are redirected to the default login page, after an attempt to use a page after your session has been timed out, is not the redirecturl param set to the page you were trying to access.
So you could infer that if that is set they were previously on a page and then present your message about being logged out due to going for lunch., etc.

Turn off page-level caching in a user control

I have a page with the following caching defined:
<%# OutputCache Duration="60" VaryByParam="None" %>
I have a user control inside that page that i don't want cached. How can I turn it off just for that control?
Option One
Use the Substitution control or API on your page. this enables you to cache everything on your page except the part contained within the substitution control.
http://msdn.microsoft.com/en-us/library/ms227429.aspx
One nice way to use this is to implement your control as a simple server control which renders the html as a string, but does so in the context of the page (that is with the correct Client IDs). Scott Guthrie has a really nice example of how this works. Works nicely with AJAX calls too by the way...
http://weblogs.asp.net/scottgu/archive/2006/10/22/Tip_2F00_Trick_3A00_-Cool-UI-Templating-Technique-to-use-with-ASP.NET-AJAX-for-non_2D00_UpdatePanel-scenarios.aspx
Excerpt from Scott Gu's article...
[WebMethod]
public string GetCustomersByCountry(string country)
{
CustomerCollection customers = DataContext.GetCustomersByCountry(country);
if (customers.Count > 0)
//RenderView returns the rendered HTML in the context of the callback
return ViewManager.RenderView("customers.ascx", customers);
else
return ViewManager.RenderView("nocustomersfound.ascx");
}
Option Two
Render the dynamic control via an AJAX call on the page load. This way, you can safely cache the entire page (including the AJAX call) and it is only the rendered result of the call that changes between pages.

ASP.NET MVC output cache with dynamic fragment

How could I cache an entire page except a bit at the top which says something along the lines of "Welcome back, Matt! | Log Out" if the user is logged in and so-on?
I'm using ASP.NET MVC 2.
What you are trying to achieve is called donut-caching or cache substitution. As of ASP.NET MVC 2 there is no built in helper to support this scenario. As much as I know it was a planned feature in MVC v.1 but it was dropped somewhere in the way to the release. For more info check this links http://haacked.com/archive/2008/11/05/donut-caching-in-asp.net-mvc.aspx, Is Donut Caching working properly with ASP.NET MVC?.
VaryByParam option that is mentioned by Oleg here is not a good idea in your case. If you have VaryByParam a different version of the page will be put in the cache for every different value of the parameter (in your case for every user-name).
Personally I would think of caching the data, not the whole output of the page.
Probably helps
<%# OutputCache Duration="15" VaryByParam="*" %>
or with some other value for the VaryByParam. See http://msdn.microsoft.com/en-us/library/hdxfb6cy.aspx, http://blog.maartenballiauw.be/post/2008/06/Creating-an-ASPNET-MVC-OutputCache-ActionFilterAttribute.aspx and http://blogs.microsoft.co.il/blogs/gilf/archive/2010/07/18/asp-net-output-cache-provider.aspx.
Moreover, if you have the start page which is not user depended, it is possible to replace the start page with a very static welcome page with the empty field (hidden div) instead of "Welcome back, Matt! | Log Out". After that an ajax request for filling of the user name can be started at the client side. This kind of the welcome page page can be very good cached.
Not Supported != Not Possible
http://blog.maartenballiauw.be/post/2008/07/01/Extending-ASPNET-MVC-OutputCache-ActionFilterAttribute-Adding-substitution.aspx
http://www.klopfenstein.net/lorenz.aspx/output-donut-caching-attribute-asp-net-mvc-partial-requests
http://haacked.com/archive/2009/05/12/donut-hole-caching.aspx
Here you have a workaround solution:
*Add the OuptutCache attribute to the Controller that manages the whole view as usually:
[OutputCache(Duration = 3600, VaryByParam = "*")]
public ActionResult Index(FormCollection formCollection)
{
//Controller code
}
*For the part that you don't want to do caching, load it using jquery + an ajax request (with its own controller and without the OutputCache attribute):
<div id="loginContainer"></div>
$(document).ready(function() {
$.post('controller/action', postdata, function(data) {
if (data.success == true) {
//Populate the container with the obtained data
}
});
});
The view will be retrieved from the Output Cache and, once it is loaded, a request to obtain the login info will be performed. Hopefully, it will be a very quick request and the user will not notice the delay.
Get this via nuget:
http://mvcdonutcaching.codeplex.com/
Add an action for LogOnPArtial, so you can change it from Html.Partial to Html.Action in the _Layout.cshtml
#Html.Action("_LogOnPartial","Account",true)
The true is a exclude parameter that says, exclude this from caching. The action will be called even if the page it is in is cached. This is the "hole" in the donut that is not cached.
On your page, such as About.cshtml that you want cached, apply DonutOutputCache attribute. This allows the new framework to inspect the page as it's caching it, and add flags where you've excluded actions.
The nice thing is the _LogOnPartial is not cached and will refresh for different users while the rest of the page is cached and the About() action will not be run. You could even configure caching on the _LogOnPartial action you created using the DonutOutputCache attribute, but a more frequent or less frequent interval, or vary by some other param. This allows you to compose pages of partials, and the cache refreshing logic is independently configured for each partial.
IMO this tool is exactly how I imagined caching in MVC should have been implemented.

How do I best handle role based permissions using Forms Authentication on my ASP.NET web application?

I'm using the ASP.NET Login Controls and Forms Authentication for membership/credentials for an ASP.NET web application.
I've got two roles:
Users
Administrators
I want pages to be viewable by four different groups:
Everyone (Default, Help)
Anonymous (CreateUser, Login, PasswordRecovery)
Users (ChangePassword, DataEntry)
Administrators (Report)
Expanding on the example in the ASP.NET HOW DO I Video Series: Membership and Roles, I've put those page files into such folders:
And I used the ASP.NET Web Site Administration Tool to set up access rules for each folder.
It works but seems kludgy to me and it creates issues when Login.aspx is not at the root and with the ReturnUrl parameter of Login.aspx.
Is there a better way to do this? Is there perhaps a simple way I can set permissions at the page level rather than at the folder level?
A couple solutions off the top of my head.
You could set up restrictions for each page in your web.config file. This would allow you to have whatever folder hierarchy you wish to use. However, it will require that you keep the web.config file up to date whenever you add additional pages. The nice part of having the folder structure determine accessibility is that you don't have to think about it when you add in new pages.
Have your pages inherit from custom classes (i.e. EveryonePage, UserPage, AdminPage, etc.) and put a role check in the Page_Load routine.
One solution I've used in the past is this:
Create a base page called 'SecurePage' or something to that effect.
Add a property 'AllowedUserRoles' to the base page that is a generic list of user roles List or List where int is the role id.
In the Page_Load event of any page extending SecurePage you add each allowed user role to the AllowedUserroles property.
In the base page override OnLoad() and check if the current user has one of the roles listed in AllowedUserRoles.
This allows each page to be customized without you having to put tons of stuff in your web.config to control each page.
In the master page I define a public property that toggles security checking, defaulted to true. I also declare a string that is a ; delimited list of roles needed for that page.
in the page load of my master page I do the following
if (_secure)
{
if (Request.IsAuthenticated)
{
if (_role.Length > 0)
{
if (PortalSecurity.IsInRoles(_role))
{
return;
}
else
{
accessDenied = true;
}
}
else
{
return;
}
}
}
//do whatever you wanna do to people who dont have access.. bump to a login page or whatever
also you'll have to put
at the top of your pages so you can access the extended properties of your master page

Resources