简体   繁体   中英

Cannot access WSDL on protected WCF service

I have set up a WCF service that runs in ASP.NET compatibility mode so that I can take advantage of the SSO authentication that the rest of my site uses. The SSO is based off of Jasig's CAS and I've modified the dotNetCasClient so that it will work with WCF services. The idea is that the dotNetCasClient http module intercepts any requests made to the service and sets up the principal according to the tickets that CAS provides.

To get this work I had to deny access to all anonymous users with:

<system.web>
...
<authorization>
<deny users="?" />
</authorization>
...
</system.web>

Everything up to this point works. Authenticated users can call my service while unauthenticated users are turned away. The problem is that I am unable to access my WSDL through .svc?wsdl. I simply get a 401.2.

Is there a way for me to allow anonymous traffic to my WSDL while still denying anonymous traffic to the rest of my service?

I've tried using variations of the following, but it doesn't like having the ?wsdl in the path.

<location path=path/to/svc?wsdl>
    <system.web>
        <authorization>
            <allow users="*" />
        </authorization>
    </system.web>
</location>

I've also played around with allowing anonymous traffic and using the PrincipalPermissionAttribute instead, but this isn't working likely due to the fact that I am using custom principals through the dotNetCasClient rather than windows principals.

Note that if I remove the authorization tags from my web.config I once again can access the WSDL, but that will prevent the SSO authentication from working properly.

Here is my complete web.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <configSections>
        <section name="casServiceClientConfig" type="DotNetCasClient.Configuration.CasClientConfiguration, DotNetServiceCasClient" />
    </configSections>
<system.web>
    <compilation debug="true" targetFramework="4.5.1" />
    <httpRuntime targetFramework="4.5.1" />
    <authentication mode="Forms">
        <forms loginUrl="https://XXXX/cas/login" cookieless="UseCookies" />
    </authentication>
    <authorization>
        <deny users="?" />
    </authorization>
</system.web>
<casServiceClientConfig casServerLoginUrl="https://XXXX/cas/login"
      casServerUrlPrefix="https://XXXX/cas/"
      serverName="https://XXXX"
      notAuthorizedUrl="~/NotAuthorized.aspx"
      cookiesRequiredUrl="~/CookiesRequired.aspx"
      redirectAfterValidation="true"
      renew="false"
      singleSignOut="true"
      ticketValidatorName="Cas20"
      serviceTicketManager="CacheServiceTicketManager"
      proxyTicketManager="CacheProxyTicketManager"
  />
<system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <modules runAllManagedModulesForAllRequests="true">
        <remove name="DotNetCasClient" />
        <remove name="PanelScheduler" />
        <remove name="ScriptModule" />
        <remove name="FormsAuthentication" />
        <add name="DotNetServiceCasClient" type="DotNetCasClient.CasAuthenticationModule,DotNetServiceCasClient" />
    </modules>
</system.webServer>
<system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
    <services>
        <service name="XXXX" >
            <endpoint contract="XXXX" address="" binding="customBinding" bindingConfiguration="nonSsLAuthBinding" />
            <endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
        </service>
    </services>
    <behaviors>
        <serviceBehaviors>
            <behavior>
                <serviceMetadata httpGetEnabled="true" />
                <serviceAuthorization serviceAuthorizationManagerType="XXXX, XXXX">
                    <authorizationPolicies>
                        <add policyType="XXXX, XXXX" />
                    </authorizationPolicies>
                </serviceAuthorization>
            </behavior>
        </serviceBehaviors>
    </behaviors>
    <extensions>
        <bindingElementExtensions>
            <add name="nonSslAuthTransport" type="XXXX, XXXX"/>
        </bindingElementExtensions>
    </extensions>
    <bindings>
        <customBinding>
            <binding name="nonSsLAuthBinding">
                <textMessageEncoding messageVersion="Soap11" />
                <nonSslAuthTransport authenticationScheme="None" allowCookies="true"  keepAliveEnabled="true" />
            </binding>
        </customBinding>
    </bindings>
</system.serviceModel>
</configuration>

I was on the right track earlier when I was looking at the PrincipalPermissionAttribute .

Due to a misunderstanding on my part, this article on MSDN led me to believe that this attribute only works with windows groups and windows principals.

However I discovered the principalPermissionMode attribute on the serviceAuthorization element . According to MSDN:

principalPermissionMode

Sets the principal used to carry out operations on the server. Values include the following:

  • None

  • UseWindowsGroups

  • UseAspNetRoles

  • Custom

The default value is UseWindowsGroups.

So in the end my solution was to update the web.config as follows:

Removed the authorization element from system.web. This chunk of code was denying access to any user that was not authenticated.

<system.web>
...
    <authorization>
        <deny users="?" />
    </authorization>
...
</system.web>

I also had to modify my serviceHostingEnvironment by adding principalPermissionMode="Custom". This will allow me to use my custom principals instead of the default of windows groups.

<system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
    <behaviors>
        <serviceBehaviors>
            <behavior>
                <serviceMetadata httpGetEnabled="true" httpGetUrl="WSDL" />
                <serviceAuthorization serviceAuthorizationManagerType="XXXX, XXXX" principalPermissionMode="Custom">
                    <authorizationPolicies>
                        <add policyType="XXXX, XXXX" />
                    </authorizationPolicies>
                </serviceAuthorization>
            </behavior>
        </serviceBehaviors>
    </behaviors>
    ...
<system.serviceModel>

After that I had to add the PrincipalPermissionAttribute onto my service methods as follows:

    [PrincipalPermission(SecurityAction.Demand, Authenticated=true, Unrestricted=true)]
    public string GetData(string data)
    {
        string primaryIdentity;
        if (ServiceSecurityContext.Current != null)
            primaryIdentity = ServiceSecurityContext.Current.PrimaryIdentity.Name;
        else
            primaryIdentity = "Not found";

        int cookies = 0;
        var request = HttpContext.Current.Request;
        if (request != null && request.Cookies.Count > 0)
            cookies = request.Cookies.Count;


        return string.Format("You passed in: {0} - Primary Identity of Authenticated User: {1} - Found {2} cookies", data, primaryIdentity, cookies);
    }

I can now access my WSDL without authentication, yet users who have an SSO ticket are still able to call into my web service, while those that don't are turned away. Works perfectly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM