I have a custom HttpModule, which handles if a user need to pay a invoice. If the user has made a postback, but is "caught" in the invoice section of my HttpModule, I would like to repost the original postback, that the user made, after the invoice has been paid, so the user does not have to start over.
Example:
The user fill out a form and submit it to the server
The HttpModule identifies that the user has an unpaid invoice, and redirects the user to the payment page
The user pays the bill
The original post from point 1 is reposted and the user can continue
I've tried saving the HttpContext (from HttpContext.Current) in the session state and setting HttpContext.Current to the value in the session, when the bill has been paid, but it does not work.
Is it possible to reuse a postback after the HttpModule has interrupted the normal flow?
My HttpModule looks like this:
class UnpaidInvoiceHttpModule : IHttpModule
{
private HttpApplication cHttpApp;
public void Dispose(){}
public void Init(HttpApplication context)
{
cHttpApp = context;
context.PreRequestHandlerExecute += new EventHandler(CheckForUnpaidInvoices);
}
private void CheckForUnpaidInvoices(Object s, EventArgs e)
{
if (HttpContext.Current.Request.Path.EndsWith(".aspx") || HttpContext.Current.Request.Path.EndsWith(".asp") || HttpContext.Current.Request.Path == "/")
{
if (HttpContext.Current.Request.Path != "/login.aspx"
&& HttpContext.Current.Request.Path != "/Payment/Default.aspx"
&& HttpContext.Current.Request.Path != "/Payment/Default_notice.aspx"
&& HttpContext.Current.User != null)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
CustomUser mUser = ManagerSecurity.SecurityAPI.GetUser();
if (mUser.HasUnpaidInvoices)
{
HttpContext.Current.Session["prepaymentHttpContext"] = HttpContext.Current;
HttpContext.Current.Response.Redirect("/Payment/Default.aspx");
}
else
{
if (HttpContext.Current.Session["prepaymentHttpContext"] != null)
{
HttpContext.Current = (HttpContext)HttpContext.Current.Session["prepaymentHttpContext"];
}
}
}
}
}
}
}
This link should provide you with everything you need to do what you describe. Note that this solution doesn't delay the post. It immediately reposts the data to a different page. You will have to modify it to store the name/value collection somewhere (perhaps in ViewState on the invoice page, or in a database) so it can be pulled up again after the invoice is paid. When the invoice is paid, you can pull up the name-value collection and pass it to the "redirect and post" method to put the user back on track to their original destination.
Related
I'm working on a custom implementation of ASP.NET membership, which uses my own database tables. Everything works as it should, but I need to redirect customers, which have not paid their invoice, to a payment page. This should not only happen on login, but also for users which already are logged in, so if an invoice is registered as "not paid" while the user is logged in, then the user must be redirected to the payment page, the next time they load a page.
Can this be done?
I did something similar to this using a HttpModule. What you want to do is handle the PreRequest event and if they are authenticated and if so make your unpaid invoice check and redirect as necessary.
e.g.
protected void OnPreRequestHandlerExecute(object sender, EventArgs e)
{
if (HttpContext.Current.Request.Path != "/UnPaid.aspx" && HttpContext.Current.User != null)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
// You don't want to make this check for every resource type
bool isPage = HttpContext.Current.Request.Path.EndsWith(".aspx") || HttpContext.Current.Request.Path == "/";
if (isPage)
{
bool isPaid = false; // Make isPaid check here
if (!isPaid)
{
// Optional pass ina return url for after the invoice is paid
string returnUrl = HttpUtility.UrlEncode(HttpContext.Current.Request.RawUrl, HttpContext.Current.Request.ContentEncoding);
HttpContext.Current.Response.Redirect(string.Concat("/UnPaid.aspx?ReturnUrl=", returnUrl), true);
}
}
}
}
}
I wouldn't let the membership provider know this information. It is job of your application to know this, not your security model. It may be as simple as adding/removing a role, but that's not ideal either.
You can do that on global.asax using the Application_AuthenticateRequest
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
string cTheFile = HttpContext.Current.Request.Path;
if(!cTheFile.EndsWith("ThePaymentPage.aspx"))
{
if(HttpContext.Current.User != null
&& HttpContext.Current.User.Identity != null
&& HttpContext.Current.User.Identity.IsAuthenticated)
{
// check here if need to redirect or not.
if(NeedPayment())
{
HttpContext.Current.Responce.Redirect("ThePaymentPage.aspx");
}
}
}
}
This is called on every page, so maybe you can add some more checks and make it real fast. Other checks can be if the page ends on .aspx
How about having inheritance.
You can "inject" a BasePage between Page class and your ASPX code behind class.
This way, you will have access to your business logic classes and then you can decide, on each request, where the user should be re-directed.
Me too agree on the point that this logic should be handled by your applications business logic instead of the security model.
Hope this helps.
In my application,I do not want two user login with the same login name.
For example, user1 login with name "test1",then user2 try to login with "test1" too,but at this moment the user1's formauthentication does not expire,so the login of user2 should be denied.
I created a cache class to record all the active session:
public class SessionDb {
private static Dictionary<string, HttpSessionState> sessionDB=new Dictionary<string, HttpSessionState>();
public SessionDb() {
}
public static void addUserAndSession(string name, HttpSessionState session) {
sessionDB.Add(name, session);
}
public static bool containUser(string name) {
//in fact,here I also want to check if this session is active or not ,but I do not find the method like session.isActive() or session.HasExpire() or something else.
return sessionDB.ContainsKey(name);
}
public static void removeUser(string name) {
if(sessionDB.ContainsKey(name)) {
sessionDB.Remove(name);
}
}
}
In the login.aspx.cs:
//check the name and password
if(checkNameAndPass(sUserName, sUserPwd)) {
if(!SessionDb.containUser(sUserName)) {
//let the user login
Session["current_user_name"] = sUserName;
SessionDb.addUserAndSession(sUserName, Session);
FormsAuthentication.RedirectFromLoginPage(UserName.Text, false);
}
else {
//
this.error.Text=string.Format("user {0} have logined!", sUserName);
}
}
Global.asax:
void Session_End(object sender, EventArgs e)
{
SessionDb.removeUser(Session["current_user_name"].ToString());
}
But it seems that it the Session_End() method is called at some time according the timeout setting in the sessionState.
Obviously I need the the SessionDb remove the related session when the authentication timeout.
Any idea to improve my code? or any other idea to implement my requirement?
I just do not want the user repeat-login(with the same name).
UPDATE:
BTW,I think my code also have some problems: I store the log in token using the Session,but how about if the formauthentication have timeout but the session does not?
If you are using a Membership provider:
A user is considered online if the current date and time minus the UserIsOnlineTimeWindow property value is earlier than the LastActivityDate for the user.
from MSDN Article
so you can simple use the check
Membership.IsOnline();
before you login the user.
In asp.net site how to prevent multiple logins of same user id?
Another approach (Disclaimer: I have not tested this.):
In the web.config add userIsOnlineTimeWindow to 1 and in the loggingIn event handler:
protected void Login1_LoggingIn(object sender, LoginCancelEventArgs e)
{
MembershipUser u = Membership.GetUser(Login1.UserName);
Response.Write(u.IsOnline);
if (u.IsOnline)
{
Login1.FailureText = "A user with this username is already logged in.";
e.Cancel = true;
}
Session in Application_AuthenticateRequest method in Global.asax is always null.Ive already try:
this.Session,HttpContext.Current.Session
always null.
protected void Application_AuthenticateRequest()
{
string userRole = string.Empty;
if (Request.IsAuthenticated)
{
if (this.Session["UserRole"] == null)
{
InsertSessionValue();
}
userRole =Session["UserRole"].ToString();
HttpContext.Current.User = new GenericPrincipal(User.Identity, new string[] {userRole});
}
}
Ive also try to use Cache,but it doesnt work because i need unique information for each user.
How to use Session in Global.asax?Is HttpApplication Application property unique for each user?
You just can't use Session at this point in the request lifecycle, it isn't available/populated yet, if you want to use it you'll need to move to an event later in the lifecycle, for example PostAcquireRequestState.
I'm recording the session start times from when people log into my .NET 2.0 web application, but I'd also like to record the Session ID. Can someone give me some example code on how to accomplish this (how to access the Session ID from within the Global.ASAX).
If you need any additional info just let me know.
HttpContext.Current.Session.SessionID
Edit to show null test:
if ((HttpContext.Current != null) && (HttpContext.Current.Session != null) {
id = HttpContext.Current.Session.SessionID
}
You can get at it quite simply with HttpContext.Current.Session.SessionId as you probably already know. You need to be on or after Application_AcquireRequestState before the session state has been loaded, and session state is also only loaded when the requested resource implements IRequiresSessionState. You can see a list of all the events in global.asax here: https://web.archive.org/web/1/http://articles.techrepublic%2ecom%2ecom/5100-10878_11-5771721.html and read more about IRequiresSessionState here: http://msdn.microsoft.com/en-us/library/system.web.sessionstate.irequiressessionstate.aspx
Write to the session the datetime and sessionid at the moment of the first request following ASP.NET's identifying the user's session.
protected void Application_PreRequestHandlerExecute(object sender, EventArgs eventArgs) {
var session = HttpContext.Current.Session;
if (session != null) {
if (session["foo"] == null) {
session["foo"] = DateTime.Now.Ticks + "|" + session.SessionID;
}
}
}
Consider the following code:
public partial class TeacherControlPanel : System.Web.UI.Page
{
protected string username = string.Empty;
protected void Page_Load(object sender, EventArgs e)
{
username = (string)Request.QueryString["username"];
Ice_Web_Portal.BO.Teacher teacher = Ice_Web_Portal.BO.Teacher.GetTeacherByUsername(username);
if (teacher != null)
{
labUsername.Text = username;
labName.Text = teacher.TeacherName;
labTeacherCode.Text = teacher.TeacherCode;
Dept dept = teacher.Department;
if (dept != null)
{
labDepartment.Text = dept.DeptName;
}
}
else
{
//labErrorMessage.Text = "No teacher found";
}
}
protected void btnSendMail_Click(object sender, EventArgs e)
{
Response.Redirect(#"~/Teacher/TeacherComposeMail.aspx?username=mahabub" + username);
}
}
In this code, when I am declaring 'username' as private, it is initialized to null after subsequent post backs.
Why?
What is the secret?
Because ASP.NET is stateless meaning it does not keep it state from post back to postback. Save the user to the viewstate, session, or application to see it on postback to postback.
#region UserName
public string UserName
{
get
{
if (this.ViewState["UserName"] == null)
return string.Empty;
return (string)this.ViewState["UserName"];
}
set { this.ViewState["UserName"] = value; }
}
#endregion
Every time you do any postback, even for "simple" things like button click events, you're working with a new instance of the page class. That's ASP.Net 101.
Declaring the username field as private or protected has no bearing on this situation. The only bearing protected/private would have is the accessibility of the variable outside the class or in inherited members.
I believe this is likely a lifecycle problem.
When you navigate to this page for the first time, user name will only have a value if the query string was set for the request. So, "/TeacherControlPanel.aspx" will have a user name with no value, but "/TeacherControlPanel.aspx?username=SomeUserName". In these cases, the field username is only going to have a value if one is set. And if no querystring is set, then when the page processes the button click event, the load will fire, no query string set means that username will be null, which means that the click event will have nothing to append to the redirect string.
So the question is, in your application, what navigation path are you using to get to TeacherControlPanel.aspx?