65.9K
CodeProject 正在变化。 阅读更多。
Home

WCF REST 4.0 授权与基于表单的身份验证 (SetAuthCookie)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (22投票s)

2011 年 12 月 25 日

CPOL

2分钟阅读

viewsIcon

93998

downloadIcon

1771

如何创建自定义授权策略并返回 HTTPContext Identity 用于授权。

引言

Windows Communication Foundation 提供了大量方法来验证用户、检查授权,基于服务类型,这使得为 WCF REST 4.0 实现简单的基于表单的身份验证和基于角色的授权变得相当令人困惑。

注意:本文假定 WCF REST 服务与 ASP.NET 应用程序一起托管,并共享相同的 web.config。 确保在 web.config 文件中启用了表单身份验证。

背景

在 WCF 服务中验证和授权用户有很多种方法,但在本示例中,身份验证 Cookie 已经由登录页面创建,后续对 REST 服务的请求将使用该 Cookie 进行授权。

Using the Code

授权用户特定角色的典型方法是使用 PrincipalPermission 属性。 类似于这样

WebGet(UriTemplate = "")]
[PrincipalPermission(SecurityAction.Demand, Role="Admin")]
public List<SampleItem> GetCollection(){} 

但是,即使在用户使用 Membership provider 验证并通过 HTTPContext.Current.User.Identity 获得上下文,并且该上下文在服务级别可用,PrincipalPermission 属性仍然会抛出安全异常。

原因是 PrincipalPermission 属性检查的是 System.Threading.Thread.CurrentPrincipal.Identity,而不是 HTTPContext Identity。

为了解决这个问题,我们必须为 WCF 服务创建自定义 Principal 和授权策略。 然后,该策略将使用 ServiceBehaviour 挂接到 WCF REST 服务。

自定义 Principal

以下是自定义 Principal 的代码

public class CustomPrincipal: IPrincipal
{
    private IIdentity _identity;
    public IIdentity Identity
    {
        get
        {
            return _identity;
        }
    }

    public CustomPrincipal(IIdentity identity)
    {
        _identity = identity;
    }

    public bool IsInRole(string role)
    {
        return Roles.IsUserInRole(role);
    } 
} 

这里使用了 ASP.NET Membership Role provider 来验证用户是否属于特定角色。 我们可以拥有自己的自定义实现,不使用 Membership provider。

授权策略

现在创建一个授权策略,将自定义 Principal 设置为评估上下文

public class AuthorizationPolicy : IAuthorizationPolicy
{
    string id = Guid.NewGuid().ToString();

    public string Id
    {
        get { return this.id; }
    }

    public System.IdentityModel.Claims.ClaimSet Issuer
    {
        get { return System.IdentityModel.Claims.ClaimSet.System; }
    }

    // this method gets called after the authentication stage
    public bool Evaluate(EvaluationContext evaluationContext, ref object state)
    {
        // get the authenticated client identity
        IIdentity client = HttpContext.Current.User.Identity; 
        
        // set the custom principal
        evaluationContext.Properties["Principal"] = new CustomPrincipal(client);

        return true;
    }              
}

仔细观察,自定义 Principal 是使用用户使用 Membership provider 验证身份后创建的 HTTPContext Identity 创建的,并且在验证用户后设置了身份验证 Cookie。 类似于这样

FormsAuthentication.SetAuthCookie(username, false);

将授权策略附加到 WCF

这可以通过在 web.config 文件中创建一个服务行为来完成。 但是,这里我通过实现 IServiceBehavior 并将授权策略附加到它来创建自定义服务行为。

[AttributeUsage(AttributeTargets.Class)]
public class SecurityBehaviorAttribute : Attribute, IServiceBehavior
{
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, 
    	System.ServiceModel.ServiceHostBase serviceHostBase)
    {
        List<IAuthorizationPolicy> policies = new List<IAuthorizationPolicy>();
        policies.Add(new AuthorizationPolicy());
        serviceHostBase.Authorization.ExternalAuthorizationPolicies = 
						policies.AsReadOnly();

        ServiceAuthorizationBehavior bh =
            serviceDescription.Behaviors.Find<ServiceAuthorizationBehavior>();
        if (bh != null)
        {
            bh.PrincipalPermissionMode = PrincipalPermissionMode.Custom;
        }
        else
            throw new NotSupportedException();
    }

    public void AddBindingParameters(ServiceDescription serviceDescription, 
    	System.ServiceModel.ServiceHostBase serviceHostBase, 
    	System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, 
    	System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { }

    public void Validate(ServiceDescription serviceDescription, 
    	System.ServiceModel.ServiceHostBase serviceHostBase) { }
}

这里,ServiceAuthorizationBehavior PrincipalPermissionMode 设置为 Custom,并且 Authorization 策略被添加到 servicehost

服务代码

确保将服务行为作为属性添加到 service 类。

[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = 
	AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    [SecurityBehavior]    
public class Service1  {
    [WebGet(UriTemplate = "")]
    [PrincipalPermission(SecurityAction.Demand, Role="Admin")]
    public List<SampleItem> GetCollection()
    {
        var value =  System.Web.HttpContext.Current.User.Identity.IsAuthenticated;
        return new List<SampleItem>() { new SampleItem() 
			{ Id = 1, StringValue = "Hello" } };
    }
} 

就这样。 现在我们可以将 PrincipalPermission 属性添加到任何 Web 方法,并授权用户访问特定角色。 我们可以还实现自定义 PrincipalPermission 属性来控制授权的粒度。

注意:我们还可以创建一个 Authentication 服务来验证用户名密码,并在验证后创建身份验证 Cookie。 这里假设 WCF REST 与 Web 应用程序一起托管,因此共享上下文。

请告诉我是否有更好的方法来实现相同的功能,而无需在每次请求中提供用户名和密码。

© . All rights reserved.