来自Windows Forms的MVC WebAPI身份验证

14

我正在尝试制作一个Windows Forms应用程序,该应用程序插入到ASP.NET MVC WebAPI暴露的一些服务中,但是在身份验证/登录部分遇到了很大的麻烦。

我无法找到一个简单演示如何从Windows Forms应用程序中执行此操作的示例,我找到的所有内容似乎都非常复杂并包括很多非常深入的管道,或者针对其他ASP.NET网站而不是Windows Forms。

我是否错过了什么?这是否不可能?还是只是不打算实现?我查看了像这样的东西.NET WebApi Authentication,它声称可以这样做,但我不知道如何从Windows Forms的角度使用cookies。我还阅读了http://blogs.msdn.com/b/webdev/archive/2012/08/26/asp-net-web-api-and-httpclient-samples.aspx,但仍然运气不佳。

2个回答

12

只需在服务器端创建身份验证令牌并将其存储在数据库中甚至是缓存中。然后,从您的Win Forms应用程序发送此令牌以进行请求。WebApi 应始终检查此令牌。这已足够好,并且您可以完全控制您的身份验证过程。

让我分享一下我是如何做到的:

带有身份验证详细信息的对象:

public class TokenIdentity
{
    public int UserID { get; set; }

    public string AuthToken { get; set; }

    public ISocialUser SocialUser { get; set; }
}

Web API身份验证控制器:

  public class AuthController : ApiController
    {
        public TokenIdentity Post(
            SocialNetwork socialNetwork,
            string socialUserID,
            [FromUri]string socialAuthToken,
            [FromUri]string deviceRegistrationID = null,
            [FromUri]DeviceType? deviceType = null)
        {
            var socialManager = new SocialManager();

            var user = socialManager.GetSocialUser(socialNetwork, socialUserID, socialAuthToken);

            var tokenIdentity = new AuthCacheManager()
                .Authenticate(
                    user,
                    deviceType,
                    deviceRegistrationID);

            return tokenIdentity;
        }
    }

认证缓存管理器:

public class AuthCacheManager : AuthManager
    {
        public override TokenIdentity CurrentUser
        {
            get
            {
                var authToken = HttpContext.Current.Request.Headers["AuthToken"];
                if (authToken == null) return null;

                if (HttpRuntime.Cache[authToken] != null)
                {
                    return (TokenIdentity) HttpRuntime.Cache.Get(authToken);
                }

                return base.CurrentUser;
            }
        }

        public int? CurrentUserID
        {
            get
            {
                if (CurrentUser != null)
                {
                    return CurrentUser.UserID;
                }
                return null;
            }
        }

        public override TokenIdentity Authenticate(
            ISocialUser socialUser, 
            DeviceType? deviceType = null, 
            string deviceRegistrationID = null)
        {
            if (socialUser == null) throw new ArgumentNullException("socialUser");
            var identity = base.Authenticate(socialUser, deviceType, deviceRegistrationID);

            HttpRuntime.Cache.Add(
                identity.AuthToken,
                identity,
                null,
                DateTime.Now.AddDays(7),
                Cache.NoSlidingExpiration,
                CacheItemPriority.Default,
                null);

            return identity;
        }
    }

认证管理器:

 public abstract class AuthManager
    {
        public virtual TokenIdentity CurrentUser
        {
            get
            {
                var authToken = HttpContext.Current.Request.Headers["AuthToken"];
                if (authToken == null) return null;

                using (var usersRepo = new UsersRepository())
                {
                    var user = usersRepo.GetUserByToken(authToken);

                    if (user == null) return null;

                    return new TokenIdentity
                    {
                        AuthToken = user.AuthToken,
                        SocialUser = user,
                        UserID = user.ID
                    };
                }
            }
        }

        public virtual TokenIdentity Authenticate(
            ISocialUser socialUser, 
            DeviceType? deviceType = null, 
            string deviceRegistrationID = null)
        {
            using (var usersRepo = new UsersRepository())
            {
                var user = usersRepo.GetUserBySocialID(socialUser.SocialUserID, socialUser.SocialNetwork);

                user = (user ?? new User()).CopyFrom(socialUser);

                user.AuthToken = System.Guid.NewGuid().ToString();

                if (user.ID == default(int))
                {
                    usersRepo.Add(user);
                }

                usersRepo.SaveChanges();

                return new TokenIdentity
                {
                    AuthToken = user.AuthToken,
                    SocialUser = user,
                    UserID = user.ID
                };
            }
        }
    }

全局操作筛选器:

public class TokenAuthenticationAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
    public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        if (actionContext.Request.RequestUri.AbsolutePath.Contains("api/auth"))
        {
            return;
        }

        var authManager = new AuthCacheManager();

        var user = authManager.CurrentUser;

        if (user == null)
        {
            throw new HttpResponseException(HttpStatusCode.Unauthorized);
        }

        //Updates the authentication
        authManager.Authenticate(user.SocialUser);
    }
}

Global.asax注册:

GlobalConfiguration.Configuration.Filters.Add(new AuthFilterAttribute());

这个想法是AuthCacheManager扩展了AuthManager并装饰了它的方法和属性。如果缓存里面没有任何内容,就去检查数据库。


2
你有代码示例吗?Darin的很有趣,但你可能有更好的。 - hawbsl
1
@hawbsl 我已更新了我的回答。希望你能在那里找到一些有趣的东西,并为你的应用程序得到一些想法。 - Andrei
1
+50 +10 我还没有尝试过,但它看起来是一个非常好的路线图可以跟随。 - hawbsl
感谢 @hawbsl,这是一个正在运行的应用程序的一部分(正如您所看到的)。很高兴能帮助您! - Andrei

4

您可以使用基于令牌的身份验证。这里有一篇优秀的文章, 介绍如何编写一个自定义动作过滤器,使用RSA公私密钥加密。


1
我有点困惑。在Windows Forms中我没有操作过滤器。 - Derek
1
我指的是登录身份验证,而不是加密。 - Derek
1
是的,那就是想法。请阅读我在回答中提供的链接文章,并尝试使用其中所示的代码进行操作。 - Darin Dimitrov
2
这仍然不太有意义。所有这些都假定您已经对用户进行了身份验证。这就是我遇到麻烦的部分。 - Derek
1
没错,但我仍然不明白的是,经过身份验证的用户传递了哪些数据。 - Derek
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接