我有一个 ASP.NET MVC 混合应用程序,它ApiController
除了 MVC 控制器之外还有一个。我在 MVC 控制器和 ApiController 中都在控制器级别,有时在方法级别使用基于角色的授权属性。我正在使用 Entity Framework 6 和基于模型的设计。
控制器级别授权:
[Authorize(Roles = "Administrator,RegularUser")]
public class EngineController : ApiController
{
或者
[System.Web.Mvc.Authorize(Roles = "Administrator,RegularUser")]
public class ProjectsController : Controller
{
当我转移控制器级别授权时,因为非登录用户可以访问它:
[AllowAnonymous]
[HttpPost]
public async Task<CheckCouponReturnValueModel> CheckCoupon([FromBody] CouponCodeRequestModel requestModel)
或者因为我软化了授权(“用户”的特权低于“常规用户”):
[OverrideAuthorization()]
[Authorize(Roles = "User")]
[HttpPost]
public TopicReturnValueModel GetTopic([FromBody]TopicReferenceModel requestModel)
用户注册后,通常会获得“用户”和“常规用户”角色。我可以通过查询数据库的 AspNetUserRoles 表来确认这一点,或者我什至有一个管理视图供管理员控制,它甚至可以通过同一个 ASP.NET MVC 应用程序显示角色。但是,当新创建的用户尝试访问 MVC 控制器上的端点或视图时,它会受到框架的授权规则的破坏并获得401 Unauthorized
. 这就像一些内部部分(我不知道它是否使用 RoleManager 或引擎盖下的东西)“没有得到消息”用户已经在角色中。
奇怪的是,ApiController 端点可以工作并识别用户的角色。在 MVC 控制器抛出 a 之后401
,用户被重定向到登录页面(带有重定向提示)。在用户登录的同时,菜单栏反映了这一点(即使重定向到登录页面 - 这很混乱)。一旦用户服从并突然重新登录,精神分裂症的行为就会消失,并且 MVC 控制器端点也开始识别用户的角色。不用说,这种方式是不可接受的。
我的包裹:
<?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
的MVC 控制器使用时使用。我认为 ApiController 获得了正确的角色,但显然我将 MVC 控制器中的所有授权声明替换为,但这也没有解决问题。[Authorize(Roles="...")]
System.Web.Http.AuthorizeAttribute
System.Web.Mvc.AuthorizeAttribute
System.Web.Http.AuthorizeAttribute
@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;
}
是的,有一个技巧可以使会话ApiController
不仅可用于 MVC 控制器,因为我真的需要它。我猜 auth 子系统与 MVC 控制器使用的常规实体上下文(在基类中实例化)具有不同的 DB 上下文。
public abstract class WorkflowControllersBase : Controller
{
protected Entities _context = new Entities();
并且每个 MVC 控制器都是该基类的后裔。尽管我可能有不同的上下文,但我绝对确认我在数据库中添加了正确的角色,它们是持久的。auth 子系统上下文能否与 DB 的状态不同步?怎么同步呢?
@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
是 ASP.NET MVC 模板提供的一个方法AccountController
:
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
. 也许这是解决问题的线索。