使用表单身份验证(授权)和基本身份验证(消息处理程序)的安全Web API

时间:2014-04-28 21:38:13

标签: c# authentication asp.net-web-api

我正在尝试一起使用表单身份验证(过滤器属性)和基本身份验证(消息处理程序)。我知道Web API的管道在消息处理程序之前执行而不是过滤器。

我已尝试过所有内容,但我尝试在google中搜索了很多但我找不到解决方案,我只能将身份验证和权限分开但不能一起工作。

代码:

TestController.cs

public class TestController : ApiController
{
    private readonly worldEntities  _db = new worldEntities();

    // GET api/Country
    [Authorize]
    public Capital Get()
    {

        var capital = new Capital
        {
            CapitalCountry = _db.cities.FirstOrDefault(),
            Country = _db.countries.FirstOrDefault()
        };
        capital.Cities = _db.cities.Where(s => s.CountryCode == capital.Country.Code);
        _db.SaveChanges();
        return capital;

    }


    // Post api/Country
    public Capital Post()
    {

        var capital = new Capital
        {
            CapitalCountry = _db.cities.FirstOrDefault(),
            Country = _db.countries.FirstOrDefault()
        };
        capital.Cities = _db.cities.Where(s => s.CountryCode == capital.Country.Code);
        _db.SaveChanges();
        return capital;

    }

}

WebApiConfig.cs

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
        config.MessageHandlers.Add(new BasicAuthMessageHandler());

        config.Filters.Add(new AuthorizeAttribute());

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

BasiAuthMessagHandle.cs

public class BasicAuthMessageHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        var headers = request.Headers;
        if (headers.Authorization != null && headers.Authorization.Scheme == "Basic")
        {
            var userPwd = Encoding.UTF8.GetString(Convert.FromBase64String(headers.Authorization.Parameter));
            var user = userPwd.Substring(0, userPwd.IndexOf(':'));
            var password = userPwd.Substring(userPwd.IndexOf(':') + 1);
            // we suppose that it's ok
            var principal = new GenericPrincipal(new GenericIdentity(user), null);
            PutPrincipal(principal);

        }

        return base.SendAsync(request, cancellationToken);
    }

    private void PutPrincipal(IPrincipal principal)
    {
        Thread.CurrentPrincipal = principal;
        if (HttpContext.Current != null)
        {
            HttpContext.Current.User = principal;
        }
    }


}

AuthController.cs

public class AuthController : ApiController
{

        public string Get(string id)
        {
            FormsAuthentication.SetAuthCookie(id ?? "FooUser", false);
            return "You are autenthicated now";
        }

}

的Web.Config

<authentication mode="Forms" />

非常感谢!!

2 个答案:

答案 0 :(得分:2)

在线有很多关于如何使用授权最好地包装Web API的示例(请参阅Toan的答案)。我知道可以使用Forms Authentication令牌保护web api并使用示例中的属性。我不相信你应该编写自己的Message Handler来做到这一点。

http://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api

首先查看此网页。此外,Toan的答案和链接也是很好的建议。你当然是在正确的轨道上。通过使用身份验证构建MVC模板,您还可以很好地了解ASP.NET Web API中的安全性,因为这些示例包括帐户管理控制器以及执行身份验证和授权的所有代码。

假设您已在网站中正确设置了表单身份验证。当调用Web API方法(Get,Post,Put,Delete,Options)时,Controller.User将是一个填充的IPrincipal对象,包含Name,IsAuthenticated bool和Roles列表。这些值由您的表单身份验证部分控制,并且是您在使用[AllowAnonymous]或[Authorize]属性时由框架查询的内容。

请注意:使用不使用SSL的表单身份验证是非常非常糟糕的事情,因为凭据是以明文形式共享的。 Forms Auth也容易受到跨站请求伪造

以下是我在MVC4中使用名为BaseApiController的超类在Web API上执行表单身份验证的示例

    public BaseApiController()
    {
        CurrentUser = new ScrubbedUser(User);
    }
    protected ScrubbedUser CurrentUser { get; set; }

然后在我的ScrubbedUser类中,我从数据库(或缓存/会话)中检索用户的信息,记住用户可能是匿名的

public class ScrubbedUser
    {
        private IPrincipal Principal { get; set; }
        public ScrubbedUser(string principal)
        {
            Principal = null;
            if (string.IsNullOrEmpty(principal))
            {
                Profile = GetDefaultProfile();
            }
            else
            {
                Profile = GetUserProfile(principal);
            }
            //Get User Memberships
            Memberships = GetUserMemberships();
            Settings = GetUserSettings();
        }
        public SurgeStreetUser(IPrincipal principal) 
        {
            Principal = principal;
            if (Principal == null
                || Principal.Identity == null
                || Principal.Identity.IsAuthenticated == false
                || string.IsNullOrEmpty(Principal.Identity.Name))
            {
                Profile = GetDefaultProfile();
            }
            else
            {
                Profile = GetUserProfile(Principal.Identity.Name);
            }
            //Get User Memberships
            Memberships = GetUserMemberships();
            Settings = GetUserSettings();
        }
        public UserProfile Profile { get; private set; }
        public List<V_UserMembership> Memberships { get; private set; }
        public List<Setting> Settings { get; private set; }

        private UserProfile GetDefaultProfile()
        {
            //Load an Anonymous Profile into the ScrubbedUser instance any way you like
        }
        private UserProfile GetUserProfile(string userName)
        {
            //Load the UserProfile based on userName variable (origin is Principle Identity Name
        }
        private List<V_UserMembership> GetUserMemberships()
        {
            //Load User's Memberships or Roles from Database, Cache or Session
        }
        private UserProfile PopulateCurrentUser(UserProfile userProfile)
        {
            var user = new UserProfile
            {
                //Convenience Method to clone and set a Profile Property
            };
            return user;
        }
        private List<Setting> GetUserSettings()
        {
            //Get the User's Settings or whatever
        }
        //Convenience to return a JSON string of the User (great to use with something like Backbone.js)
        private dynamic JSONRecord
        {
            get
            {
                return new
                {
                    CustId = Profile.CustId,
                    UserName = Profile.UserName,
                    UserId = Profile.UserId,
                    Email = Profile.Email,
                    FirstName = Profile.FirstName,
                    Language = Profile.Language,
                    LastActivityDate = Profile.LastActivityDate,
                    LastName = Profile.LastName,
                    DebugOption = Profile.DebugOption,
                    Device = Profile.Device,
                    Memberships = Memberships,
                    Settings = Settings
                };
            }
        }
    }

我使用Memberships而不是Roles,可以使用Super Class的CurrentUser属性来测试用户是否是某个成员的成员。我还可以在类级别或方法级别使用Web API控制器上的[Authorize]属性

public class ListController : BaseApiController
{
    //main is "global"
    public dynamic Get(string id)//JObject values)
    {
        //I can test here for anonymous as well, even if I allow anonymous

        //Example using my own convention on User Profile Class populated by ScrubbedUser constructor
        if (CurrentUser.Profile.CustId == "public")
        {
            return HttpStatusCode.Forbidden;
        }
        //Redundant Code
        if (!User.Identity.IsAuthenticated)
        {
            return HttpStatusCode.Forbidden;
        }
        string filterExt = string.IsNullOrEmpty(id) || id=="global"
            ? "*" : id;
        return ListRepository.GetList(filterExt, SSUser);
    }
    [Authorize]
    public dynamic Post(JObject values)
    {
        //Just a sample, this will not fire unless the user is authenticated
        return CurrentUser.JSONRecord;
    }
}

答案 1 :(得分:1)

我认为在如何保护Web API方面存在一些误解。在这里使用基于表单的身份验证的目的是什么?

您具有对用户进行身份验证的基本身份验证。为什么需要使用基于表单的身份验证来对用户进行身份验证?

如果您要验证用户的权限,请将用户凭据映射到一组声明并在API控制器上进行检查。

您可以查看以下链接http://channel9.msdn.com/Shows/Web+Camps+TV/Securing-ASPNET-Web-APIs