7

我正在使用 MVC 4 Web Api,我希望在使用我的服务之前对用户进行身份验证。

我已经实现了一个授权消息处理程序,它工作得很好。

public class AuthorizationHandler : DelegatingHandler
{
    private readonly AuthenticationService _authenticationService = new AuthenticationService();

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        IEnumerable<string> apiKeyHeaderValues = null;
        if (request.Headers.TryGetValues("X-ApiKey", out apiKeyHeaderValues))
        {
            var apiKeyHeaderValue = apiKeyHeaderValues.First();

            // ... your authentication logic here ...
            var user = _authenticationService.GetUserByKey(new Guid(apiKeyHeaderValue));

            if (user != null)
            {

                var userId = user.Id;

                var userIdClaim = new Claim(ClaimTypes.SerialNumber, userId.ToString());
                var identity = new ClaimsIdentity(new[] { userIdClaim }, "ApiKey");
                var principal = new ClaimsPrincipal(identity);

                Thread.CurrentPrincipal = principal;
            }
        }

        return base.SendAsync(request, cancellationToken);
    }
}

问题是,我使用表单身份验证。

[HttpPost]
    public ActionResult Login(UserModel model)
    {
        if (ModelState.IsValid)
        {
            var user = _authenticationService.Login(model);
            if (user != null)
            {
                // Add the api key to the HttpResponse???
            }
            return View(model);
        }

        return View(model);
    }

当我调用我的 api 时:

 [Authorize]
public class TestController : ApiController
{
    public string GetLists()
    {
        return "Weee";
    }
}

处理程序找不到 X-ApiKey 标头。

只要用户登录,有没有办法将用户的 api 密钥添加到 http 响应标头并将密钥保留在那里?有没有另一种方法来实现这个功能?

4

2 回答 2

3

我找到了以下文章http://www.asp.net/web-api/overview/working-with-http/http-cookies 使用它我将我的 AuthorizationHandler 配置为使用 cookie:

public class AuthorizationHandler : DelegatingHandler
{
    private readonly IAuthenticationService _authenticationService = new AuthenticationService();

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var cookie = request.Headers.GetCookies(Constants.ApiKey).FirstOrDefault();
        if (cookie != null)
        {
            var apiKey = cookie[Constants.ApiKey].Value;
            try
            {
                var guidKey = Guid.Parse(apiKey);

                var user = _authenticationService.GetUserByKey(guidKey);
                if (user != null)
                {

                    var userIdClaim = new Claim(ClaimTypes.Name, apiKey);
                    var identity = new ClaimsIdentity(new[] { userIdClaim }, "ApiKey");
                    var principal = new ClaimsPrincipal(identity);

                    Thread.CurrentPrincipal = principal;

                }
            }
            catch (FormatException)
            {
            }
        }

        return base.SendAsync(request, cancellationToken);
    }
}

我配置了我的登录操作结果:

[HttpPost]
    public ActionResult Login(LoginModel model)
    {
        if (ModelState.IsValid)
        {
            var user = _authenticationService.Login(model);
            if (user != null)
            {
                _cookieHelper.SetCookie(user);

                return RedirectToAction("Index", "Home");
            }

            ModelState.AddModelError("", "Incorrect username or password");
            return View(model);
        }

        return View(model);
    }

在其中我使用的是我创建的 CookieHelper。它由一个接口组成:

public interface ICookieHelper
{
    void SetCookie(User user);

    void RemoveCookie();

    Guid GetUserId();
}

还有一个实现接口的类:

public class CookieHelper : ICookieHelper
{
    private readonly HttpContextBase _httpContext;

    public CookieHelper(HttpContextBase httpContext)
    {
        _httpContext = httpContext;
    }

    public void SetCookie(User user)
    {
        var cookie = new HttpCookie(Constants.ApiKey, user.UserId.ToString())
        {
            Expires = DateTime.UtcNow.AddDays(1)
        };

        _httpContext.Response.Cookies.Add(cookie);
    }

    public void RemoveCookie()
    {
        var cookie = _httpContext.Response.Cookies[Constants.ApiKey];
        if (cookie != null)
        {
            cookie.Expires = DateTime.UtcNow.AddDays(-1);
            _httpContext.Response.Cookies.Add(cookie);
        }
    }

    public Guid GetUserId()
    {
        var cookie = _httpContext.Request.Cookies[Constants.ApiKey];
        if (cookie != null && cookie.Value != null)
        {
            return Guid.Parse(cookie.Value);
        }

        return Guid.Empty;
    }
}

通过这个配置,现在我可以为我的 ApiControllers 使用 Authorize 属性:

[Authorize]
public class TestController : ApiController
{
    public string Get()
    {
        return String.Empty;
    }
}

这意味着,如果用户未登录。他无法访问我的 api 并收到 401 错误。此外,我还可以在代码中的任何位置检索用作用户 ID 的 api 密钥,这使得代码非常干净易读。

我不认为使用 cookie 是最好的解决方案,因为某些用户可能在他们的浏览器中禁用了它们,但目前我还没有找到更好的授权方式。

于 2013-04-26T18:12:32.920 回答
1

从您的代码示例来看,您似乎没有使用 Web 表单。您可能正在使用表单身份验证吗?您是否在服务中使用 Membership Provider 来验证用户凭据?

您可以使用 HttpClient 类,也可以使用它的属性 DefaultRequestHeaders 或来自将调用 API 以设置标头的代码中的 HttpRequestMessage。

这里有一些HttpClient的例子: http ://www.asp.net/web-api/overview/web-api-clients/calling-a-web-api-from-a-net-client

于 2013-04-26T03:12:53.000 回答