Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/261.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/asp.net/33.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 具有Asp.Net成员资格的ADFS单点登录_C#_Asp.net_Asp.net Mvc_Asp.net Membership_Adfs - Fatal编程技术网

C# 具有Asp.Net成员资格的ADFS单点登录

C# 具有Asp.Net成员资格的ADFS单点登录,c#,asp.net,asp.net-mvc,asp.net-membership,adfs,C#,Asp.net,Asp.net Mvc,Asp.net Membership,Adfs,这是一个挑战——我维护一个混合的asp.net mvc/web表单应用程序,它使用表单身份验证和旧的asp.net成员资格提供程序(aspnet_用户、aspnet_成员资格等)。我们公司正在使用ADFS进行单点登录。我们必须修改混合asp.net应用程序以使用ADFS进行身份验证 我的问题是,我是否可以将混合asp.net应用程序更改为使用ADFS进行身份验证,但继续使用现有的成员资格提供程序来处理授权 这个计划行得通吗?我的假设正确吗 >P>使用Windows身份基础4.5被动重定向,如该链

这是一个挑战——我维护一个混合的asp.net mvc/web表单应用程序,它使用表单身份验证和旧的asp.net成员资格提供程序(aspnet_用户、aspnet_成员资格等)。我们公司正在使用ADFS进行单点登录。我们必须修改混合asp.net应用程序以使用ADFS进行身份验证

我的问题是,我是否可以将混合asp.net应用程序更改为使用ADFS进行身份验证,但继续使用现有的成员资格提供程序来处理授权

这个计划行得通吗?我的假设正确吗

>P>使用Windows身份基础4.5被动重定向,如该链接中所描述的:未经验证的用户将自动重定向到我们的ADFS安全令牌服务器

  • 在asp.net网站中,从ADFS令牌读取经过身份验证的用户的用户名,并调用FormsAuthentication.SetAuthCookie以使成员资格提供程序可用。这将在基本页面类(对于web表单)或自定义授权属性(对于mvc控制器,覆盖AuthorizeCore)中完成。对于一个特定的用户,调用只进行一次,我将使用会话变量来跟踪是否进行了调用

  • 在某种程度上,它归结为这个问题:由于我们将使用ADFS进行身份验证,asp.net网站的web.config将具有“无”身份验证模式,并拒绝所有匿名用户。使用此web.config设置,对FormsAuthentication.SetAuthCookie的调用是否会单独启用成员资格提供程序?或者成员资格提供商是否要求将身份验证模式设置为“表单”


    如果您想知道“为什么不试试呢?”,这是因为ADFS服务器将在几个月内不可用,但我现在负责提出一个开发计划。我知道,如果我只使用常规的asp.net mvc应用程序,将身份验证模式设置为“无”,然后使用正确的用户名和密码调用Membership.ValidateUser,然后调用FormsAuthentication.SetAuthCookie,则成员资格提供方似乎工作正常,尽管Request.IsAuthenticated当然是false,因此,我没有方便的方法对此进行全面测试,因为每次授权检查都首先查看用户是否经过身份验证,然后再查看角色

    这种方法适用于WIF,但如果您使用的是ADFS 4.0,也可以使用OWIN Katana和OpenID Connect

    OWIN管道允许多个连接,例如


    或者您可以使用像identityserver这样的支持ASP.NET成员身份的工具,并且您可以将其与ADF联合。identityserver将有两个按钮,用户可以选择其中一个进行身份验证。

    结果证明这非常简单。我正在使用Framework 4.7和Windows Server 2016。注意--框架的其他版本和Windows Server的说明完全不同

    遵循以下步骤后,我成功地将ADFS集成到使用成员资格的asp.net web应用程序中。对成员数据库的所有现有调用都有效(例如,Membership.Getuser()、Roles.GetRolesForUser())。此外,System.Threading.Thread.CurrentPrincipal.Identity功能齐全。诸如IsInRole()和[Authorize]属性之类的调用可以正常工作,无需进行任何更改

    这不是一个详细的演练,只是对我在web应用程序中必须更改的内容的粗略描述(设置ADF是一个完全独立的问题)

    设置ADF后,在web应用程序中创建指向ADF的FederationMetadata.xml文件。Google获取有关创建FederationMetadata.xml文件的说明。警告:不要使用Framework 3.5 Windows Identity Federation实用程序创建FederationData.xml;该实用工具将更改web.config以使用不推荐使用的Micorsoft.Identity库。您将希望使用System.Identity库。我的FederationMetadata.xml文件如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <EntityDescriptor ID="_ff25f54f-e839-4005-9dc5-bb598b34a50d" entityID="https://MyServer.MyCompany.com/ADFSAuthentication/" xmlns="urn:oasis:names:tc:SAML:2.0:metadata">
      <RoleDescriptor xsi:type="fed:ApplicationServiceType" xmlns:fed="http://docs.oasis-open.org/wsfed/federation/200706" protocolSupportEnumeration="http://docs.oasis-open.org/wsfed/federation/200706" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <fed:TargetScopes>
          <wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
            <wsa:Address>https://MyServer.MyCompany.com/ADFSAuthentication/</wsa:Address>
          </wsa:EndpointReference>
          <wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
            <wsa:Address>https://MyServer.MyCompany.com/ADFSAuthentication/</wsa:Address>
          </wsa:EndpointReference>
        </fed:TargetScopes>
        <fed:PassiveRequestorEndpoint>
          <wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
            <wsa:Address>https://MyServer.MyCompany.com/ADFSAuthentication/</wsa:Address>
          </wsa:EndpointReference>
        </fed:PassiveRequestorEndpoint>
      </RoleDescriptor>
    </EntityDescriptor>
    
  • 在我们的应用程序中,单个Active Directory用户可以具有多个成员身份。有时用户会想要更改成员身份。这很容易做到:

    private void ExampleOfHowToChangeIdentity(Guid newIdentity)
    {
    //assume that a user has multiple identies and is logged in as the default.
    //the user now selects a new identity
    var currentPrincipalIdentity = (System.Security.Claims.ClaimsIdentity)System.Threading.Thread.CurrentPrincipal.Identity;
    var allClaims = currentPrincipalIdentity.Claims.ToList();
    
    //first remove all of the roles from old identity
    var allRoles = allClaims.Where(o => o.Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/role").ToList();
    foreach (var role in allRoles)
    {
        currentPrincipalIdentity.RemoveClaim(role);
    }
    
    //second, fetch the new claims from the database using the newIdentity
    //we will have a column or table in the Membership database that matches this guid to the UserId
    //below I am hard-coding some new claims, but in fact they will be added from a database call.
    currentPrincipalIdentity.AddClaim(new System.Security.Claims.Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", "Nonesuch"));
    currentPrincipalIdentity.AddClaim(new System.Security.Claims.Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", "Nonesuch2"));
    currentPrincipalIdentity.AddClaim(new System.Security.Claims.Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", "Nonesuch3"));
    
    //third, replace the MyCompany/userID claim with that of the new identity
    //this will always be hard-coded.  this is read by Application_AuthenticateRequest each time the user visits the site
    currentPrincipalIdentity.RemoveClaim(allClaims.Single(o => o.Type == "MyCompany/userID"));
    currentPrincipalIdentity.AddClaim(new System.Security.Claims.Claim("MyCompany/userID", newIdentity.ToString()));
    
    //four, create a new session security token 
    //cast to pass into session security token constructor
    var claimsPrincipal = new System.Security.Claims.ClaimsPrincipal(currentPrincipalIdentity);
    var token = new System.IdentityModel.Tokens.SessionSecurityToken(claimsPrincipal);
    System.IdentityModel.Services.FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(token);
    }
    

  • 事后看来,我关于调用“FormsAuthentication.SetAuthCookie”的问题非常愚蠢。此呼叫绝对不需要,也与成员资格提供商无关。通过在web.config中添加标记来启用成员资格提供程序;表单身份验证是一个完全不同的领域。
    <system.web>
    <authorization>
      <deny users="?" />
    </authorization>
    <authentication mode="None" />
    
      <appSettings>
    <add key="ida:FederationMetadataLocation" value="https://adfs.MyCompany.com/federationmetadata/2007-06/FederationMetadata.xml" />
    <add key="ida:Issuer" value="http://adfs.MyCompany.com/adfs/ls/" />
    <add key="ida:ProviderSelection" value="productionSTS" />
    <add key="ida:EnforceIssuerValidation" value="false" />
    
    <system.identityModel>
    <identityConfiguration>
      <audienceUris>
        <add value="https://MyServer.MyCompany.com/ADFSAuthentication/" />
      </audienceUris>
      <!--certificationValidationMode set to "None" by the the Identity and Access Tool for Visual Studio. For development purposes.-->
      <certificateValidation certificateValidationMode="None" />
      <issuerNameRegistry type="System.IdentityModel.Tokens.ValidatingIssuerNameRegistry, System.IdentityModel.Tokens.ValidatingIssuerNameRegistry">
        <authority name="http://adfs.MyCompany.com/adfs/services/trust">
          <keys>
            <add thumbprint="MyGuid" />
          </keys>
          <validIssuers>
            <add name="http://adfs.MyCompany.com/adfs/services/trust" />
          </validIssuers>
        </authority>
      </issuerNameRegistry>
      <securityTokenHandlers>
        <add type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
        <remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
      </securityTokenHandlers>
    </identityConfiguration>
    
      <connectionStrings>
    <clear />
    <add name="LocalSqlServer" connectionString="Server=MyServer; Database=MyMembershipDatabase; Integrated Security=SSPI;" providerName="System.Data.SqlClient" />
    
    protected void Application_AuthenticateRequest(Object sender, EventArgs e)
        {
            var currentPrincipalIdentity = (System.Security.Claims.ClaimsIdentity)System.Threading.Thread.CurrentPrincipal.Identity;
            var claims = currentPrincipalIdentity.Claims.ToList();
    
            //if WSFederationAuthenticationModule just fired (aka user's first visit) the claims have not been loaded yet.
            //if SessionAuthenticationModule just fired (aka the user has a valid security token cookie) then no need to reload the claims, they are a part of Thread.CurrentPrincipal
            if (!claims.Exists(o => o.Type == "MyCompany/objectGUID_decoded"))
            {
                //get the encoded guid.  if this does not exist exit immediately, the user has no business in our web site
                var encodedGuidClaim = claims.FirstOrDefault(o => o.Type == "MyCompany/objectGUID");
                if (encodedGuidClaim == null)
                    return;
    
                currentPrincipalIdentity.AddClaim(new System.Security.Claims.Claim("MyCompany/objectGUID_decoded", new Guid(Convert.FromBase64String(encodedGuidClaim.Value)).ToString()));
    
                //we will need a new column or table in membership database to link users to the ActiveDirectory objectGUID.
                //if the user has multiple identities we will load the default (the default must exist)
                //for this example I am hard-coding the MyCompany/userID guid, but in fact it will be the single or default userID guid for the user
                currentPrincipalIdentity.AddClaim(new System.Security.Claims.Claim("MyCompany/userID", "310860D2-6329-41B7-AF44-E8DC2113B4C7"));
    
                //for this example I am hard-coding the roles, but in face we will load the user's roles from database using the userId retrieved in the line above.
                //when user changes identity then we need to write a new cookie with the new roles collection, see ExampleOfHowToChangeIdentity() above.
                currentPrincipalIdentity.AddClaim(new System.Security.Claims.Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", "MyRole1"));
                currentPrincipalIdentity.AddClaim(new System.Security.Claims.Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", "MyRole2"));
                currentPrincipalIdentity.AddClaim(new System.Security.Claims.Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", "MyRole3"));
            }
        }
    
    private void ExampleOfHowToChangeIdentity(Guid newIdentity)
    {
    //assume that a user has multiple identies and is logged in as the default.
    //the user now selects a new identity
    var currentPrincipalIdentity = (System.Security.Claims.ClaimsIdentity)System.Threading.Thread.CurrentPrincipal.Identity;
    var allClaims = currentPrincipalIdentity.Claims.ToList();
    
    //first remove all of the roles from old identity
    var allRoles = allClaims.Where(o => o.Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/role").ToList();
    foreach (var role in allRoles)
    {
        currentPrincipalIdentity.RemoveClaim(role);
    }
    
    //second, fetch the new claims from the database using the newIdentity
    //we will have a column or table in the Membership database that matches this guid to the UserId
    //below I am hard-coding some new claims, but in fact they will be added from a database call.
    currentPrincipalIdentity.AddClaim(new System.Security.Claims.Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", "Nonesuch"));
    currentPrincipalIdentity.AddClaim(new System.Security.Claims.Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", "Nonesuch2"));
    currentPrincipalIdentity.AddClaim(new System.Security.Claims.Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", "Nonesuch3"));
    
    //third, replace the MyCompany/userID claim with that of the new identity
    //this will always be hard-coded.  this is read by Application_AuthenticateRequest each time the user visits the site
    currentPrincipalIdentity.RemoveClaim(allClaims.Single(o => o.Type == "MyCompany/userID"));
    currentPrincipalIdentity.AddClaim(new System.Security.Claims.Claim("MyCompany/userID", newIdentity.ToString()));
    
    //four, create a new session security token 
    //cast to pass into session security token constructor
    var claimsPrincipal = new System.Security.Claims.ClaimsPrincipal(currentPrincipalIdentity);
    var token = new System.IdentityModel.Tokens.SessionSecurityToken(claimsPrincipal);
    System.IdentityModel.Services.FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(token);
    }