2008年7月25日

[IIS][ASP.Net][Web Service][WSE] Web service and Forms authentication

We have a ASP.Net 2.0 web site secured by Forms authentication with custom membership provider & role manager, within this web site, we have a web service exposed as an API. We want this web service also be secured with same mechanism.

I thought of adding Credentials to web service proxy class, but this fails since Credentials should only work with Windows authentication.

Then I thought of maybe <location/> element helps since this element enables you have different configuration settings with different urls. and this also failed, because you can't write a web.config like this:

  <location path="." allowOverride="true" inheritInChildApplications="true">
<system.web>
<authentication mode="Forms">
<forms loginUrl="login.aspx" name=".ASPXFORMSAUTH">
</forms>
<authorization>
<deny users="?" />
</authorization>
</system.web>
</location>
<location path="svc.asmx">
<system.web>
<forms loginUrl="svc.asmx?op=Logon" name=".ASPXFORMSAUTH">
</forms>
</system.web>
</location>


And this fails still because <authentication/> is not a overridable element in web.config, so that you can not specify 2 different login pages in same web site.



Finally, Andrew Arnott's Blog saves me, he has the same issue I have, follow his instruction, we are now able to use same Membership provider/Role manager to authenticate both web site & web service.



Bellow is a summary:




  1. Install WSE 3.0


  2. On your web project, open up WSE 3.0 property page

    1. Check both [Enable this project for web services Enhancements] and [Enable Microsoft web services Enhacement Soap protocol Factory]


    2. Under [Security] tab, add a custom token manager


    3. Enable policy, name your new policy, this policy name will be used both server side and client side, so don't miss it.




  3. Click OK to complete server setting.

    1. a wse3policyCache.config file will be generated




  4. Enable your web site's Membership/RoleManager if they are not enabled yet.


  5. Modify wse3policyCache.config, allow specificed roles to access this web service(for me,"User" role are allowed), your wse3policyCache.config should look like this
    <policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy">
    <extensions>
    <extension name="usernameOverTransportSecurity" type="Microsoft.Web.Services3.Design.UsernameOverTransportAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    <extension name="requireActionHeader" type="Microsoft.Web.Services3.Design.RequireActionHeaderAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    </extensions>
    <policy name="usernameTokenSecurity">
    <authorization>
    <allow role="User"/>
    <deny role="*"/>
    </authorization>
    <usernameOverTransportSecurity />
    <requireActionHeader />
    </policy>
    </policies>



  6. Add a custom token manager in APP_CODE folder, code listed bellow:
    using System.Xml;
    using System.Web.Security;
    using System.Security.Permissions;
    using System.Security.Principal;
    using Microsoft.Web.Services3.Security;
    using Microsoft.Web.Services3.Security.Tokens;

    [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
    public class CustomUsernameTokenManager : UsernameTokenManager
    {
    //codes omitted.
    protected override string AuthenticateToken(UsernameToken token)
    {
    bool validCredentials = Membership.ValidateUser(token.Username, token.Password);
    if (!validCredentials) throw new UnauthorizedAccessException();

    GenericIdentity identity = new GenericIdentity(token.Username);
    GenericPrincipal principal = new GenericPrincipal(identity, Roles.GetRolesForUser(token.Username));
    token.Principal = principal;

    return token.Password;
    }
    }



  7. Modify <microsoft.web.services3> section in your web site's web.config, should look like this:
      <microsoft.web.services3>
    <security>
    <securityTokenManager>
    <add
    type="CustomUsernameTokenManager, __code"
    namespace="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" localName="UsernameToken" />
    </securityTokenManager>
    </security>
    <policy fileName="wse3policyCache.config" />
    </microsoft.web.services3>




  8. Modify web.config to allow client to call your web service


      <location path="svc.asmx">
    <system.web>
    <authorization>
    <allow users="*"></allow>
    </authorization>
    </system.web>
    </location>




Now it's time to configure your client application.





    1. open WSE property page on client project


      1. Check [Enable this project for Web Service Enhancements]


      2. Enable Policy, add a new policy with exactly same name you named your server policy, in our case, should be "usernameTokenSecurity"

        1. A wse3policyCache.config file will be generated




      3. Update your web reference


      4. Modify your client code as follow:
              private GetService CreateWebService()
        {
        if (ws == null)
        ws = new svcWse();
        ws = new svcWse();

        UsernameToken userName = new UsernameToken(user, password,PasswordOption.SendPlainText);
        ws.SetClientCredential(userName);
        ws.SetPolicy("usernameTokenSecurity");
        return ws;
        }
        public String[] GetItemList()
        {
        return CreateWebService().GetItemList();
        }







N

沒有留言:

Blog Archive

About Me