我有一个 Asp.net 4.5 MVC5 应用程序,它使用 SimpleMembership 在应用程序中进行身份验证和授权。现在我需要集成以使用外部 IdentityServer4 进行身份验证。我尝试将 OWIN 启动文件添加到项目中,并在 web.config 中关闭标准 IIS 身份验证。现在我可以被重定向到 IDServer 以进行登录和同意。
进入视图时出现问题:
错误信息:
未找到名为“”的用户。说明:执行当前 Web 请求期间发生未处理的异常。请查看堆栈跟踪以获取有关错误及其源自代码的位置的更多信息。
异常详细信息:System.InvalidOperationException:未找到名为“”的用户。
源错误:
第 15 行:@if (Model != null) 第 16 行:{ 第 17 行:
foreach(模型中的 var 声明)第 18 行:{ 第 19 行:
@claim.Type 源文件:e:\GitHub\CRURepoPostBR20190607_IS4\CRU_Build\Suite\Views\Home\IS4Auth.cshtml 行:17
堆栈跟踪:[InvalidOperationException:未找到名为“”的用户。]
WebMatrix.WebData.SimpleRoleProvider.GetRolesForUser(String username) +498 System.Web.Security.RolePrincipal.GetRoles() +215 System.Web.Security .d__4.MoveNext() +58
System.Security.Claims.d__51.MoveNext() +253
System.Security.Claims.d__37.MoveNext() +209
我的 IdentityServer 是 IdentityServer4 快速入门示例的克隆。我的 MVC5 应用程序很旧,使用 SimpleMembership 进行身份验证和角色授权。我手动添加了 OWIN Startup.cs,请参见下面的代码。
我尝试使用 IdentityServer https://demo.identityserver.io作为 IDServer 并遇到了同样的问题。这证明我的 IDServer 没有问题。
我尝试在没有 SimpleMembership 的情况下创建一个简单的单独的 Asp.net 4.5 MVC5 项目,但没有出现错误。
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "oidc",
SignInAsAuthenticationType = "Cookies",
ClientSecret = "secret",
//Authority = "http://localhost:5000", //ID Server
Authority = "https://demo.identityserver.io", //ID Server
RequireHttpsMetadata = false,
RedirectUri = "http://localhost:58018/signin-oidc",
//RedirectUri = "http://localhost:58018/",
//ClientId = "myOldMvc",
ClientId = "server.hybrid",
ResponseType = "id_token code",
Scope = "openid profile",
});
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
}
}
这是显示声明的视图:
@model IEnumerable<System.Security.Claims.Claim>
@{
ViewBag.Title = "IS4Auth";
}
<h2>IS4Auth</h2>
<dl>
@if (Model != null)
{
foreach (var claim in Model)
{
<dt>@claim.Type</dt>
<dd>@claim.Value</dd>
}
}
</dl>
这是我与 system.web 和 system.webServer 的配置相关的部分
<system.web>
<httpModules>
<add name="ar.sessionscope" type="Castle.ActiveRecord.Framework.SessionScopeWebModule, Castle.ActiveRecord.web" />
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" />
<add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" />
<add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" />
</httpModules>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" maxRequestLength="1048576" />
<!--Change for IS4Authentication-->
<authentication mode="None" />
<!--<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>-->
<!--<roleManager enabled="true" defaultProvider="simple">
<providers>
<clear />
<add name="simple" type="WebMatrix.WebData.SimpleRoleProvider,WebMatrix.WebData" />
</providers>
</roleManager>
<membership defaultProvider="simple">
<providers>
<clear />
<add name="simple" type="WebMatrix.WebData.SimpleMembershipProvider,WebMatrix.WebData" enablePasswordReset="true" requiresQuestionAndAnswer="false" />
</providers>
</membership>-->
<pages>
<namespaces>
<add namespace="System.Web.Helpers" />
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Optimization" />
<add namespace="System.Web.Routing" />
<add namespace="System.Web.WebPages" />
</namespaces>
</pages>
<httpHandlers>
<add verb="*" path="routes.axd" type="AttributeRouting.Web.Logging.LogRoutesHandler, AttributeRouting.Web" />
<add verb="POST,GET,HEAD" path="elmah" type="Elmah.ErrorLogPageFactory, Elmah" />
</httpHandlers>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="Elmah" verb="POST,GET,HEAD" path="elmah" type="Elmah.ErrorLogPageFactory, Elmah" />
</handlers>
<modules>
<!--Change for IS4Authentication-->
<remove name="FormsAuthentication" />
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" preCondition="managedHandler" />
<add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" preCondition="managedHandler" />
<add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" preCondition="managedHandler" />
</modules>
<httpProtocol>
<customHeaders>
<clear />
<add name="X-UA-Compatible" value="IE=Edge" />
</customHeaders>
</httpProtocol>
</system.webServer>
这是添加到 OpenIdConnectAuthenticationOptions 的通知代码
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
// use the code to get the access and refresh token
var tokenClient = new TokenClient(
"http://localhost:5000/connect/token",
//"server.hybrid",
"myApp",
"secret");
var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(n.Code, n.RedirectUri);
if (tokenResponse.IsError)
{
//throw new Exception(tokenResponse.Error);
throw new AuthenticationException(tokenResponse.Error);
}
// use the access token to retrieve claims from userinfo
var userInfoClient = new UserInfoClient("http://localhost:5000/connect/userinfo");
var userInfoResponse = await userInfoClient.GetAsync(tokenResponse.AccessToken);
// create new identity
var id = new ClaimsIdentity(n.AuthenticationTicket.Identity.AuthenticationType);
id.AddClaims(userInfoResponse.Claims);
var Name = userInfoResponse.Claims.FirstOrDefault(c => c.Type.Equals("Name", StringComparison.CurrentCultureIgnoreCase)).Value;
id.AddClaim(new Claim("access_token", tokenResponse.AccessToken));
id.AddClaim(new Claim("expires_at", DateTime.Now.AddSeconds(tokenResponse.ExpiresIn).ToLocalTime().ToString()));
id.AddClaim(new Claim("refresh_token", tokenResponse.RefreshToken));
id.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
id.AddClaim(new Claim("sid", n.AuthenticationTicket.Identity.FindFirst("sid").Value));
n.AuthenticationTicket = new AuthenticationTicket(
new ClaimsIdentity(id.Claims, n.AuthenticationTicket.Identity.AuthenticationType, JwtClaimTypes.Name, JwtClaimTypes.Role),
n.AuthenticationTicket.Properties
);
List<Claim> _claims = new List<Claim>();
_claims.AddRange(new List<Claim>
{
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", Name),
new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider",Name)
});
},
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType != OpenIdConnectRequestType.Logout)
{
return Task.FromResult(0);
}
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");
if (idTokenHint != null)
{
n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
}
return Task.FromResult(0);
}
}