找出用户是不是在 MVC WEB API 中登录的正确方法是啥?

Posted

技术标签:

【中文标题】找出用户是不是在 MVC WEB API 中登录的正确方法是啥?【英文标题】:What is the correct way of find out if user is logged in in MVC WEB API?找出用户是否在 MVC WEB API 中登录的正确方法是什么? 【发布时间】:2014-03-22 17:53:04 【问题描述】:

我对这个问题很困惑。 Restfull 服务由您决定以哪种方式实现此功能。

我已经阅读了多篇关于这个问题的文章,但每篇文章都有不同的说法。

例如,有些人提出会话,但如果你这样做,Web api 就会失去它的“休息充分性”。其他人建议使用 cockies。

我不知道我做的是否真的做对了:

在用户登录时,我创建了一个包含 UserID(Guid) 的 cockie,并且在每个需要用户登录的请求上,我检查这个 id 是否存在于数据库中。

它足够安全吗?或者我应该如何使它更安全?还是我必须选择完全不同的方式?

【问题讨论】:

尝试用谷歌搜索 OAuth,但这完全取决于您的 API 目标 【参考方案1】:

只需在服务器端创建身份验证令牌并将其存储在您的数据库甚至缓存中。然后将此令牌与来自您的客户端应用程序的请求一起发送。 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 并装饰它的方法和属性。如果缓存中没有任何内容,请检查数据库。

这是一个真实应用的例子,但我希望这个想法很清楚:)

【讨论】:

哦,这是一个非常优雅的解决方案。生病了,非常感谢,甚至没有想过使用缓存:D 如果您有 100 万用户,缓存是好的解决方案吗?有什么后果? @DmitryKvochkin 缓存是用户数量非常多时的最佳解决方案。您可以使用 NoSQL 数据库进行缓存。我强烈推荐 Redis 或 MongoDB。 Redis 更快,MongoDB 提供更多功能。 @Timsen 很高兴为您提供帮助!祝你好运! @AndreiMikhalevich 最后一个问题,如果我的缓存是空的,如果我不知道要问什么,我怎么能问数据库?还是我还是应该保留我的公鸡?

以上是关于找出用户是不是在 MVC WEB API 中登录的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 ASP.NET 5 MVC 6 保护 Web API

仍登录 MVC 站点,但无法调用 Web API

Web API 身份验证(无 asp.net 身份)

将身份验证同时用于 MVC 页面和 Web API 页面?

.NET Core Web API理解

一个 MVC Web 应用程序作为 API 和客户端