【问题标题】:Angular.js SPA security with ASP.NET MVC and WebApi使用 ASP.NET MVC 和 WebApi 的 Angular.js SPA 安全性
【发布时间】:2016-07-01 16:29:01
【问题描述】:

我正在使用 Angular.js 和 ASP.NET 构建一个 SPA,我想知道保护它的最佳方法是什么。

这是我需要的:

我想使用 MVC 框架仅对登录用户隐藏我的应用程序。因此,用户在启动 SPA 之前要做的第一件事就是使用简单的登录表单登录网站。

当 Angular 应用启动时,它将使用 REST 请求与我的 ApiController 通信。

我还希望我的用户在 20 分钟不活动后自动注销。

我知道 REST 应该是无状态的……但我不知道如何在没有会话的情况下实现我所需要的一切……

但另一方面,我希望能够将我的 WebAPI 与未来的移动应用程序一起使用。我将不得不在此应用程序上使用令牌进行身份验证。

对我来说,实现这种身份验证的最佳方式是什么?

感谢您的宝贵时间!

【问题讨论】:

    标签: angularjs asp.net-mvc asp.net-web-api single-page-application


    【解决方案1】:

    我按照这篇文章here 中很好解释的内容开发了一个与您的条件相同的完整安全层。

    顺便说一句,令牌将在 20 分钟后自动过期,因为当您创建它时,您将立即设置它的过期日期;每次您要发出请求时,系统都会检查令牌到期日期和当前日期,如果时间过去则拒绝您的令牌。例如,这是一个带有令牌和刷新令牌设置的典型 oauth 服务器配置:

    internal static OAuthAuthorizationServerOptions GetAuthorizationServerOptions(IComponentContext scope)
        {
            OAuthAuthorizationServerOptions oAuthServerOptions = new OAuthAuthorizationServerOptions
            {                
                AllowInsecureHttp = true,
                ApplicationCanDisplayErrors = true,
                TokenEndpointPath = new PathString(Constants.PublicAuth.OAUTH_TOKEN_PATH),
                AuthorizeEndpointPath = new PathString(Constants.ExternalAuth.AUTH_ENDPOINT),
                AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(Constants.PublicAuth.TOKEN_EXPIRATION_IN_MINUTES),
                Provider = scope.Resolve<AuthorizationServerProvider>(),
                AccessTokenFormat = new CustomJwtFormat(),
                RefreshTokenProvider = scope.Resolve<SimpleRefreshTokenProvider>()
            };
            return oAuthServerOptions;
        }
    

    刷新令牌也很有用,但您必须自己管理令牌替换;例如,在我们的应用程序中,我们通过单个服务传递每个 API 调用,如果服务器响应 401 (unauthorized),它将尝试使用刷新令牌请求新令牌,然后再次尝试相同的调用。只有在第二次失败后,您才会被重定向到登录页面。

    例如:

    function executeCallWithAuth(method, url, payload, params) {
        var defer = $q.defer();
        debug.logf('{0}: {1}', method.toUpperCase(), url);
        $http({ method: method, url: url, data: payload, headers: createHeaders(), params: params }).then(
            function(results) { defer.resolve(results); }, 
            function(error) {
                if (error.status !== 401) defer.reject(error);
                else {
                    debug.warn(`Call to: ${method}:${url} result in 401, try token refresh...`);
                    auth.refreshToken().then(
                        function() {
                            debug.warn('Token refresh succesfully, retry api call...');
                            $http({ method: method, url: url, data: payload, headers: createHeaders() }).then(
                                function(results) { defer.resolve(results); },
                                function(errors) { defer.reject(errors); });
                        },
                        function(tokenError) {
                            debug.warn('Token refresh rejected, redirect to login.');
                            $state.go('login');
                            defer.reject(tokenError);
                        });
                }
            });
    
        return defer.promise;
    }
    

        function createHeaders() {
        var headers = {
        };
    
        var authData = storage.get('authorizationData');
        if (authData) {
            headers.Authorization = 'Bearer ' + authData.token;
        }
    
        return headers;
    }
    

    使用Angular 保护路由的最佳方式是“不创建路由”。基本上,您需要加载用户配置文件,然后才能创建仅到他可以导航到的页面的路由。如果您不为页面创建路由,则不需要保护该页面:Angular 会自动将用户发送到 404。

    【讨论】:

    • 感谢您抽空卢卡!请举这个例子:用户在 12:00 登录应用程序...令牌有效期为 20 分钟...所以它在 12:20 到期。用户十分钟不使用应用程序... 12:10,他使用它...令牌仍然只有十分钟...我如何将过期时间再延长20分钟?正如 ASP.NET 会话那样?我有一个小想法......每次我的一个 WebAPI 接到电话时,我是否必须将刷新令牌延长 20 分钟?还是有另一种更好的方法来实现这一目标?谢谢!
    • 这是一种“sliding-expiration”令牌,是的,如果你愿意,你可以这样做。但这种方法的问题在于,无论何时或多少次,您都必须为每次调用生成一个新令牌。这绝对是昂贵的。另一种选择是将令牌到期时间设置为例如 30 分钟,并在 JS 中设置超时函数以每 25 分钟刷新一次令牌,例如,使用说明的刷新令牌;您可以将 api 服务与 401 管理作为最后的手段。
    • 与其生成新令牌...我认为使用简单的 SQL 查询来更新我的 RefreshToken 到期日期会更好...不是吗?您的超时函数可以工作......但在我们这里的例子中,我们不希望延长非活动用户会话(令牌)......这是我们无法承受的安全风险。
    • 另一种方法可能是创建一个新的 AuthorizeAttribute 类,并覆盖 IsAuthorized 方法以进入数据库以查看上次调用 API 的时间......一种使用令牌的自定义会话管理。 ..但是在那种情况下...我在问自己...为什么不简单地使用会话身份验证而不是滑动令牌...
    • 我建议您阅读我在答案开头链接的文章。刷新令牌的有效期可能非常长,甚至几天。这里的重点是要有一个有效的不记名令牌。刷新令牌只是避免再次计算用户声称的“技巧”。
    【解决方案2】:

    我会使用 OAuth2 保护您的 WebAPI 调用(您甚至可以使用内置的 Identity 2.0 提供程序,它随附)。保持您的 WebAPI 无状态,使用 SSL(考虑使用过滤器强制它),并使用 [Authorize] 标签来保护您的服务。在 MVC 方面,这将必须维护状态,并且您将希望登录表单从您的 WebAPI 层获取 OAuth2 令牌并将其传递给 Angular。将此到期时间设置为 20 分钟。您还可以在此处使用 cookie 身份验证模型,因为它需要在 MVC 端是有状态的,但是 Angular 对 WebAPI 层进行的所有 ajax 调用都需要将 OAuth2 令牌作为授权请求标头中的不记名令牌传递。

    【讨论】:

    • 感谢 Daniel 的快速回答。如何让 Bearer 令牌在 20 分钟不活动后过期?在我阅读的每个文档中,延长 Bearer 令牌的到期时间是不正常的……也许是刷新令牌?但是,我如何处理在 20 分钟不活动后,我希望取消令牌这一事实?
    • 查看这篇文章:stackoverflow.com/questions/33701398/… 意识到由于不记名令牌将是无状态的,我不相信您会从它们中获得滑动到期。最好的办法是在 Angular 中进行设置,以捕获任何未经授权的 http 错误,并通过获取新的令牌来刷新其持有者令牌。
    猜你喜欢
    • 1970-01-01
    • 2012-06-20
    • 1970-01-01
    • 1970-01-01
    • 2016-11-12
    • 1970-01-01
    • 2010-10-02
    • 2014-12-14
    • 2014-05-03
    相关资源
    最近更新 更多