我有一个 ASP.NET MVC 混合应用程序,除了 MVC Controller 之外,它还有一个 ApiController
。我在 MVC Controller 和 ApiController 中在 Controller 级别(有时在方法级别)使用基于角色的授权属性。我使用 Entity Framework 6 进行基于模型的设计。
Controller级别授权:
[Authorize(Roles = "Administrator,RegularUser")]
public class EngineController : ApiController
{
或
[System.Web.Mvc.Authorize(Roles = "Administrator,RegularUser")]
public class ProjectsController : Controller
{
当我从 Controller 级别授权转移时,因为它可供非登录用户访问:
[AllowAnonymous]
[HttpPost]
public async Task<CheckCouponReturnValueModel> CheckCoupon([FromBody] CouponCodeRequestModel requestModel)
或者因为我软化了授权(“User”的权限低于“RegularUser”):
[OverrideAuthorization()]
[Authorize(Roles = "User")]
[HttpPost]
public TopicReturnValueModel GetTopic([FromBody]TopicReferenceModel requestModel)
用户注册后,通常会获得“User”和“RegularUser”角色。我可以通过查询数据库的 AspNetUserRoles 表来确认这一点,或者我什至有一个管理 View 供管理员控制,它甚至可以通过相同的 ASP.NET MVC 应用程序显示角色。然而,当新创建的用户尝试访问 MVC Controller 上的端点或 View 时,它会受到框架授权规则的拒绝,并收到 401 Unauthorized
。就像一些内部部分(我不知道它是否使用 RoleManager 或者幕后的内容)“没有收到用户已经在角色中的消息”。
奇怪的是,ApiController 端点可以工作并识别用户的角色。 MVC Controller 抛出 401
后,用户将被重定向到登录页面(带有重定向提示)。在用户登录的同时,菜单栏会反射(reflect)这一点(即使重定向到登录页面 - 这很令人困惑)。一旦用户服从并重新登录,精神 split 的行为就会突然消失,MVC Controller 端点也开始识别用户的角色。不用说,这是 Not Acceptable 。
我的包裹:
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="animate.css" version="3.3.0.0" targetFramework="net461" />
<package id="Antlr" version="3.5.0.2" targetFramework="net45" />
<package id="bootstrap" version="3.3.7" targetFramework="net461" />
<package id="Bootstrap.Datepicker" version="1.6.4" targetFramework="net461" />
<package id="EntityFramework" version="6.1.3" targetFramework="net461" />
<package id="FontAwesome" version="4.7.0" targetFramework="net461" />
<package id="free-jqGrid" version="4.14.0" targetFramework="net461" />
<package id="jQuery" version="2.2.4" allowedVersions="[2,3)" targetFramework="net461" />
<package id="jquery.datatables" version="1.10.12" targetFramework="net461" />
<package id="jQuery.InputMask" version="3.3.4" targetFramework="net461" />
<package id="jquery.noty" version="2.3.5" targetFramework="net461" />
<package id="jQuery.UI.Combined" version="1.12.1" targetFramework="net461" />
<package id="jQuery.Validation" version="1.16.0" targetFramework="net461" />
<package id="JSZip" version="3.1.3" targetFramework="net461" />
<package id="knockoutjs" version="3.4.2" targetFramework="net461" />
<package id="KnockoutJS.Validation" version="3.0.0" targetFramework="net45" />
<package id="Microsoft.AspNet.Cors" version="5.2.3" targetFramework="net461" />
<package id="Microsoft.AspNet.Identity.Core" version="2.2.1" targetFramework="net461" />
<package id="Microsoft.AspNet.Identity.EntityFramework" version="2.2.1" targetFramework="net461" />
<package id="Microsoft.AspNet.Identity.Owin" version="2.2.1" targetFramework="net461" />
<package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.Razor" version="3.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.Web.Optimization" version="1.1.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Cors" version="5.2.3" targetFramework="net461" />
<package id="Microsoft.AspNet.WebApi.WebHost" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebPages" version="3.2.3" targetFramework="net45" />
<package id="Microsoft.jQuery.Unobtrusive.Validation" version="3.2.3" targetFramework="net45" />
<package id="Microsoft.Owin" version="3.1.0" targetFramework="net461" />
<package id="Microsoft.Owin.Host.SystemWeb" version="3.1.0" targetFramework="net461" />
<package id="Microsoft.Owin.Security" version="3.1.0" targetFramework="net461" />
<package id="Microsoft.Owin.Security.Cookies" version="3.1.0" targetFramework="net461" />
<package id="Microsoft.Owin.Security.Facebook" version="3.1.0" targetFramework="net461" />
<package id="Microsoft.Owin.Security.Google" version="3.1.0" targetFramework="net461" />
<package id="Microsoft.Owin.Security.MicrosoftAccount" version="3.1.0" targetFramework="net461" />
<package id="Microsoft.Owin.Security.OAuth" version="3.1.0" targetFramework="net461" />
<package id="Microsoft.Owin.Security.Twitter" version="3.1.0" targetFramework="net461" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
<package id="MimeTypeMap.List" version="1.1.0" targetFramework="net461" />
<package id="Modernizr" version="2.8.3" targetFramework="net45" />
<package id="Moment.js" version="2.18.1" targetFramework="net461" />
<package id="morelinq" version="2.3.0" targetFramework="net461" />
<package id="mousetrap" version="1.3" targetFramework="net461" />
<package id="Mvc.JQuery.DataTables" version="1.5.31" targetFramework="net461" />
<package id="Mvc.JQuery.DataTables.Common" version="1.5.31" targetFramework="net461" />
<package id="Mvc.JQuery.Datatables.Templates" version="1.5.31" targetFramework="net461" />
<package id="MvcSiteMapProvider.MVC5" version="4.6.22" targetFramework="net45" />
<package id="MvcSiteMapProvider.MVC5.Core" version="4.6.22" targetFramework="net45" />
<package id="MvcSiteMapProvider.Web" version="4.6.22" targetFramework="net45" />
<package id="Nager.Date" version="1.6.0" targetFramework="net461" />
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net461" />
<package id="Owin" version="1.0" targetFramework="net45" />
<package id="pdfmake" version="0.1.18" targetFramework="net461" />
<package id="PDFsharp" version="1.32.3057.0" targetFramework="net461" />
<package id="QueryInterceptor" version="0.2" targetFramework="net45" />
<package id="ReCaptcha-AspNet" version="1.4.0" targetFramework="net461" />
<package id="Respond" version="1.4.2" targetFramework="net461" />
<package id="Sendgrid" version="9.1.1" targetFramework="net461" />
<package id="SendGrid.CSharp.HTTP.Client" version="3.3.0" targetFramework="net461" />
<package id="Spin.js" version="2.3.2.1" targetFramework="net461" />
<package id="Stripe.net" version="8.2.0" targetFramework="net461" />
<package id="System.Linq.Dynamic.Core" version="1.0.6.13" targetFramework="net461" />
<package id="System.Net.Http" version="4.0.0" targetFramework="net461" allowedVersions="[4,4.0.0]" />
<package id="WebActivatorEx" version="2.2.0" targetFramework="net461" />
<package id="WebGrease" version="1.6.0" targetFramework="net45" />
</packages>
ApiController
的 [Authorize(Roles="...")]
属性正在使用 System.Web.Http.AuthorizeAttribute
当我的 MVC Controller 使用 System.Web.Mvc.AuthorizeAttribute 时。我认为 ApiController 的角色是正确的,但显然我将 MVC Controller 中的所有授权声明替换为 System.Web.Http.AuthorizeAttribute ,但这也没有解决问题。 p>
@solidau 询问的 Startup.Auth:
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and role manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
ExpireTimeSpan = new System.TimeSpan(8, 0, 0), // Uncomment this to enable 8 hour inactivity/idle expiration
SlidingExpiration = true,
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)),
// https://stackoverflow.com/questions/20149750/unauthorised-webapi-call-returning-login-page-rather-than-401
// http://brockallen.com/2013/10/27/using-cookie-authentication-middleware-with-web-api-and-401-response-codes/
// http://brockallen.com/2013/10/27/host-authentication-and-web-api-with-owin-and-active-vs-passive-authentication-middleware/
OnApplyRedirect = ctx =>
{
if (!IsAjaxRequest(ctx.Request))
{
ctx.Response.Redirect(ctx.RedirectUri);
}
}
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
// Enables the application to remember the second login verification factor such as phone or email.
// Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
// This is similar to the RememberMe option when you log in.
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
}
private static bool IsAjaxRequest(IOwinRequest request)
{
IReadableStringCollection queryXML = request.Query;
if ((queryXML != null) && (queryXML["X-Requested-With"] == "XMLHttpRequest"))
{
return true;
}
IReadableStringCollection queryJSON = request.Query;
if ((queryJSON != null) && (queryJSON["Content-Type"] == "application/json"))
{
return true;
}
IHeaderDictionary headersXML = request.Headers;
var isAjax = ((headersXML != null) && (headersXML["X-Requested-With"] == "XMLHttpRequest"));
IHeaderDictionary headers = request.Headers;
var isJson = ((headers != null) && (headers["Content-Type"] == "application/json"));
return isAjax || isJson;
}
是的,有一个技巧可以使 session 不仅可用于 MVC Controller ,而且还可用于 ApiController
,因为我确实需要它。我猜想 auth 子系统具有与 MVC Controller 使用的常规实体上下文(在基类中实例化)不同的数据库上下文。
public abstract class WorkflowControllersBase : Controller
{
protected Entities _context = new Entities();
并且每个 MVC Controller 都是该基类的后代。尽管我可能有不同的上下文,但我绝对确认我在数据库中添加了正确的角色,它们被持久化了。身份验证子系统上下文是否会与数据库状态不同步?如何同步?
@Ali,当前代码:
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await UserManager.AddToRoleAsync(user.Id, "User");
await UserManager.AddToRoleAsync(user.Id, model.AccountType);
await SignInAsync(user, isPersistent: true);
if (model.AccountType != "QuickDeal")
{
if (User.IsInRole("QuickDeal")) // Remove from QuickDeal if the user upgraded
await UserManager.RemoveFromRoleAsync(user.Id, "QuickDeal");
await UserManager.AddToRoleAsync(user.Id, "RegularUser");
}
我尝试在 SignInAsync
之后执行角色添加/删除,但到目前为止没有帮助。实际的 SignInAsync
是 AccountController
的一个方法,由 ASP.NET MVC 模板提供:
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, await user.GenerateUserIdentityAsync(UserManager));
}
<configSections>
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
</configSections>
<connectionStrings>
<add name="DefaultConnection" connectionString="Server=tcp:xyx.database.windows.net,1433;Initial Catalog=XYZ;Persist Security Info=False;User ID=csaba;Password=*************;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;" providerName="System.Data.SqlClient" />
<add name="Entities" connectionString="metadata=res://*/Models.EntityModel.csdl|res://*/Models.EntityModel.ssdl|res://*/Models.EntityModel.msl;provider=System.Data.SqlClient;provider connection string='Server=tcp:zyx.database.windows.net,1433;Initial Catalog=XYZ;Persist Security Info=False;User ID=csaba;Password=***************;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;'" providerName="System.Data.EntityClient" />
</connectionStrings>
请注意,Azure 提供的默认字符串未启用 MARS。但这样我就得到了一个错误,所以我设置了MultipleActiveResultSets=True
。也许这就是解决方案的一个线索。
最佳答案
由于您正在使用 cookie,因此您需要确保在分配新角色后使用新角色重新创建 cookie(否则,过时的 cookie 会一直存在,直到过期)。授予新角色后,您可以使用身份验证管理器注销用户,然后再次登录,从而使用新添加的角色重新创建他们的 cookie。我已经包含了一个片段,但您必须根据您的代码进行自定义:
IAuthenticationManager authenticationManager = HttpContext.GetOwinContext().Authentication;
authenticationManager.SignOut("ApplicationCookie");
authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = false }, identity);
关于asp.net-mvc-5 - 为什么 ASP.NET MVC 应用程序在修改后无法立即识别用户角色更改?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43701607/