I'm working with ASP.NET MVC 4. When we build a project, they add an AccountController by default. You can create an acoount and log in right away... but after they've logged in, try to type http://localhost:port/Account/LogOn. You still stay at the Login page with the login form.
I have tried to redirect the user by using this code:
//
// GET: /Account/LogOn
[AllowAnonymous]
public ActionResult LogOn()
{
if(Membership.GetUser().UserName != null)
RedirectToAction("Index", "Home");
return ContextDependentView();
}
The code works fine until I log out, and then this code no longer works. Please show me the right way to do this.
Fixed:
//
// GET: /Account/LogOn
[AllowAnonymous]
public ActionResult LogOn()
{
if(User.Identity.IsAuthenticated)
{
return RedirectToAction("Index", "Home");
}
else
{
return ContextDependentView();
}
}
You don't have to copy over the rights-chechking code - do write a filter instead and use it multiple times with no code duplication.
public class GuestsOnlyAttribute : ActionFilterAttribute
{
public override void OnActionExecuting( ActionExecutingContext filterContext )
{
if( User.Identity.IsAuthenticated )
{
filterContext.Result = new ViewResult { ViewName = "RestrictedArea" };
}
}
}
In your controller
// GET: /Account/LogOn
[GuestsOnly]
public ActionResult LogOn() { ... }
Related
I'm developing a simple Custom Role-based Web Application using ASP.Net MVC, In my login Action, I'm creating a Profile session as below:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
using (HostingEnvironment.Impersonate())
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
var employeeProfile = AccountBal.Instance.GetEmployee(loginId);
Session["Profile"] = employeeProfile;
FormsAuthentication.SetAuthCookie(model.UserName, true);
}
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", #"The user name or password provided is incorrect.");
return View(model);
}
}
And I'm checking this or using this session in all Controller Actions as below:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateOrEdit(MyModel model)
{
var employee = (Employee) Session["Profile"];
if (employee == null)
return RedirectToAction("Login", "Account");
if (ModelState.IsValid)
{
// Functionality goes here....
}
}
Is there any way I can move this piece of session checking code in a base class or centralized class? so that, I do not need to check it every time in a Controller Actions instead I will access the properties directly
say,
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateOrEdit(MyModel model)
{
var employee = _profileBase.GetCurrentProfile();
if (employee == null)
return RedirectToAction("Login", "Account");
if (ModelState.IsValid)
{
// Functionality goes here....
}
}
Create a base controller that contains your GetCurrentProfile method to retrieve current user profile like
public class BaseController : Controller
{
public Employee GetCurrentProfile()
{
return (Employee)Session["Profile"];
}
public bool SetCurrentProfile(Employee emp)
{
Session["Profile"] = emp;
return true;
}
}
And inherit your desired controller with above BaseController and access your GetCurrentProfile method like below
public class HomeController : BaseController
{
public ActionResult SetProfile()
{
var emp = new Employee { ID = 1, Name = "Abc", Mobile = "123" };
//Set your profile here
if (SetCurrentProfile(emp))
{
//Do code after set employee profile
}
return View();
}
public ActionResult GetProfile()
{
//Get your profile here
var employee = GetCurrentProfile();
return View();
}
}
GetCurrentProfile and SetCurrentProfile directly available to your desired controller because we directly inherit it from BaseController.
You may usetry/catch in above code snippet.
Try once may it help you
On a MVC controller I have the following:
public virtual ActionResult Test() {
ActionResult result = MVC.Home.Index();
return RedirectToAction(result);
return View();
}
This redirects to result. View is never returned.
Now I need to do something as follows:
public virtual ActionResult Test() {
ActionResult result = MVC.Home.Index();
MyClass.RedirectTo(result, this);
return View();
}
Where MyClass.RedirectTo method is the following:
public static void RedirectTo(ActionResult result, Controller controller) {
controller.Response.Redirect(result.ToString());
}
But in this case the redirection does not happen.
The View is returned. Does anyone knows how to do this?
Hi you have to redirect to a specific action on a specific controller
e.g
return RedirectToAction("Index", "ControllerName");
So basically in my UserController.cs class I have an Index method that returns the ActionResult to display the dashboard for the user. On this page is a html button with a type of submit. When I hit this button I want to capture it on the server side log the user out.
How can I do this since I'm not passing information back and the method signature ends up being the same.
Thanks,
Mike
[Authorize]
public ActionResult Index()
{
return View();
}
[Authorize]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index()
{
FormsAuthentication.SignOut();
return RedirectToAction("Index", "Home");
}
Use ActionNameAttribute:
[AcceptVerbs(HttpVerbs.Get), ActionName("Index"), Authorize]
public ActionResult IndexGet()
{
return View();
}
[AcceptVerbs(HttpVerbs.Post), ActionName("Index"), Authorize]
public ActionResult IndexPost()
{
FormsAuthentication.SignOut();
return RedirectToAction("Index", "Home");
}
Use:
[Authorize]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(FormCollection values)
{
FormsAuthentication.SignOut();
return RedirectToAction("Index", "Home");
}
Or
[Authorize]
[AcceptVerbs(HttpVerbs.Post), ActionName("Post")]
public ActionResult IndexPost()
{
FormsAuthentication.SignOut();
return RedirectToAction("Index", "Home");
}
As you know in C# you cannot have two methods with the same name and same arguments within the same class. You could either add some dummy parameter or I would advice you to rename the second action to SignOut which seems more semantically correct and better reflecting what this action actually does.
i have created the controller :
[Authorize]
[AcceptVerbs(HttpVerbs.Delete)]
public ActionResult Delete(int id)
{
try
{
db.DeleteObject(db.AEROLINEA.FirstOrDefault(x => x.AEROLINEAID == id));
db.SaveChanges();
}
catch { /* TODO:Display message*/ }
return View();
}
if i execute in firebug the next javascript anyone logged could delete an airline even if he doesnt have permissions to delete
var action = "/Airline/Delete/" + recordId;
var request = new Sys.Net.WebRequest();
request.set_httpVerb("DELETE");
request.set_url(action);
request.add_completed(deleteCompleted);
request.invoke();
HOw can avoid this issue???
You can filter the the roles:
Example:
[Authorize(Roles="Admin")]
[AcceptVerbs(HttpVerbs.Delete)]
public ActionResult Delete(int id)
{
try
{
db.DeleteObject(db.AEROLINEA.FirstOrDefault(x => x.AEROLINEAID == id));
db.SaveChanges();
}
catch { /* TODO:Display message*/ }
return View();
}
Or use the AntiforgeryToken with a juicy salt at the View..
[Authorize] without parameters allows you to indicate that a user must be logged in. You also can specify users/roles, authorized to access your action
I have a the following methods in an MVC Controller which redirect to the login page when a user is not logged in.
[Authorize]
public ActionResult Search() {
return View();
}
[Authorize]
public ActionResult Edit() {
return View();
}
Is there a quick/easy/standard way to redirect the second action to a different login page other than the page defined in the web.config file?
Or do I have to do something like
public ActionResult Edit() {
if (IsUserLoggedIn)
return View();
else
return ReturnRedirect("/Login2");
}
I think it is possible by creating a custom authorization filter:
public class CustomAuthorization : AuthorizeAttribute
{
public string LoginPage { get; set; }
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.HttpContext.Response.Redirect(LoginPage);
}
base.OnAuthorization(filterContext);
}
}
In your action:
[CustomAuthorization(LoginPage="~/Home/Login1")]
public ActionResult Search()
{
return View();
}
[CustomAuthorization(LoginPage="~/Home/Login2")]
public ActionResult Edit()
{
return View();
}
Web.config based forms authentication does not have such a functionality built-in (this applies to both WinForms and MVC). You have to handle it yourself (either through an HttpModule or ActionFilter, the method you mentioned or any other method)
I implemented the accepted answer by user434917 and even though I was being redirected correctly, I was also receiving the error "Server cannot set status after HTTP headers have been sent." in the server log. After searching, I found this post (answer by Mattias Jakobsson) that solved the problem. I combined the answers to get this solution.
Create a custom authorization filter:
using System.Web.Mvc;
using System.Web.Routing;
namespace SomeNamespace.CustomFilters
{
public class CustomAuthorization : AuthorizeAttribute
{
public string ActionValue { get; set; }
public string AreaValue { get; set; }
public string ControllerValue { get; set; }
public override void OnAuthorization(AuthorizationContext context)
{
base.OnAuthorization(context);
if (context.HttpContext.User.Identity.IsAuthenticated == false)
{
var routeValues = new RouteValueDictionary();
routeValues["area"] = AreaValue;
routeValues["controller"] = ControllerValue;
routeValues["action"] = ActionValue;
context.Result = new System.Web.Mvc.RedirectToRouteResult(routeValues);
}
}
}
}
Then on your controller, use the customer attribute.
[CustomAuthorization(ActionValue = "actionName", AreaValue = "areaName", ControllerValue = "controllerName")]
public class SomeControllerController : Controller
{
//DO WHATEVER
}
Yeah pretty easy! Lets say you have 2 different type of users. First typenormal users, the other one is administrators. You would like to make them login from different pages. You also want them to be able to access different ActionResults.
First you have add two different schemes. In these schemes you will define your different login pages and other options you want.
in startup.cs
services.AddAuthentication("UserSceheme").AddCookie("UserScheme", config =>
{
config.LoginPath = "/UsersLogin/Login/";
config.Cookie.Name = "UsersCookie";
});
services.AddAuthentication("AdminScheme").AddCookie("AdminScheme", config =>
{
config.LoginPath = "/AdminLogin/Login/";
config.Cookie.Name = "AdminsCookie";
});
Then you will define two policies. Here I called them UserAccess and AdminAccess Each policy will use the sceheme that I point.
In startup.cs just after schemes add those below.
services.AddAuthorization(options =>
{
options.AddPolicy("UserAccess", policy =>
{
policy.AuthenticationSchemes.Add("UserScheme");
policy.RequireAuthenticatedUser();
});
options.AddPolicy("AdminAccess", policy =>
{
policy.AuthenticationSchemes.Add("AdminScheme");
policy.RequireAuthenticatedUser();
});
});
The last thing we have to do is again telling the scheme we want to use when login.
var userPrincipal = new ClaimsPrincipal(new[] {
new ClaimsIdentity(loginClaims, "ServiceCenter")
});
HttpContext.SignInAsync("AdminScheme",userPrincipal);
Thats it! Now we can use these just like this; This will redirect you to the users login page.
[Authorize(Policy = "UserAccess")]
public IActionResult Index()
{
return View();
}
If you have some places that you want both user types to be able to access all you have to do;
[Authorize(Policy = "AdminAccess")]
[Authorize(Policy = "UserAccess")]
public IActionResult Index()
{
return View();
}
And finally for log-out you also have to point the scheme
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync("AdminScheme");
return View();
}
Thats it!