AngularJS客户端路由和令牌认证与web api

Posted

技术标签:

【中文标题】AngularJS客户端路由和令牌认证与web api【英文标题】:AngularJS clientside routing and token authentication with webapi 【发布时间】:2014-02-10 20:59:03 【问题描述】:

我想在 SPA angularjs 应用程序中创建一个身份验证和授权示例,使用 asp.net mvc webapi 作为后端和客户端路由(无 cshtml)。以下只是可用于设置完整示例的函数示例。但我就是不能把它们放在一起。任何帮助表示赞赏。

问题:

    什么是最佳实践:基于 Cookie 还是基于令牌? 如何以角度创建不记名令牌以对每个请求进行授权? 验证 API 函数? 如何在客户端保留登录用户的身份验证?

示例代码:

    登录表格

    <form name="form" novalidate>
     <input type="text" ng-model="user.userName" />
     <input type="password" ng-model="user.password" />
     <input type="submit" value="Sign In" data-ng-click="signin(user)">
    </form>
    

    身份验证角度控制器

    $scope.signin = function (user) 
    $http.post(uri + 'account/signin', user)
        .success(function (data, status, headers, config) 
            user.authenticated = true;
            $rootScope.user = user;
            $location.path('/');
        )
        .error(function (data, status, headers, config) 
    
            alert(JSON.stringify(data));
            user.authenticated = false;
            $rootScope.user = ;
        );
    ;
    

    我的 API 后端 API 代码。

    [HttpPost]
    public HttpResponseMessage SignIn(UserDataModel user)
    
        //FormsAuthetication is just an example. Can I use OWIN Context to create a session and cookies or should I just use tokens for authentication on each request? How do I preserve the autentication signed in user on the client?
        if (this.ModelState.IsValid)
        
            if (true) //perform authentication against db etc.
            
                var response = this.Request.CreateResponse(HttpStatusCode.Created, true);
                FormsAuthentication.SetAuthCookie(user.UserName, false);
    
                return response;
            
    
            return this.Request.CreateErrorResponse(HttpStatusCode.Forbidden, "Invalid username or password");
        
        return this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, this.ModelState);
    
    

    授权 使用 JWT 库来限制内容。

    config.MessageHandlers.Add(new JsonWebTokenValidationHandler
    
      Audience = "123",
      SymmetricKey = "456"
    );
    

    我的 API 方法

    [Authorize]
    public IEnumerable<string> Get()
    
     return new string[]  "value1", "value2" ;
    
    

【问题讨论】:

【参考方案1】:

是否使用 cookie 身份验证或(不记名)令牌仍取决于您拥有的应用类型。据我所知,还没有任何最佳实践。但由于您正在开发 SPA,并且已经在使用 JWT 库,因此我更倾向于基于令牌的方法。

很遗憾,我无法在 ASP.NET 方面为您提供帮助,但 JWT 库通常会为您生成并验证令牌。您所要做的就是在凭证(和秘密)上调用generateencode,在随每个请求发送的令牌上调用verifydecode。而且你不需要在服务器上存储任何状态,也不需要发送 cookie,你可能对FormsAuthentication.SetAuthCookie(user.UserName, false) 做了什么。

我确信您的库提供了有关如何使用生成/编码和验证/解码令牌的示例。

所以生成和验证不是你在客户端做的事情。

流程是这样的:

    客户端将用户提供的登录凭据发送到服务器。 服务器验证凭据并使用生成的令牌进行响应。 客户端将令牌存储在某处(本地存储、cookie 或仅在内存中)。 客户端在每次向服务器请求时将令牌作为授权标头发送。 服务器验证令牌并相应地发送请求的资源或 401(或类似的东西)。

第 1 步和第 3 步:

app.controller('UserController', function ($http, $window, $location) 
    $scope.signin = function(user) 
    $http.post(uri + 'account/signin', user)
        .success(function (data) 
            // Stores the token until the user closes the browser window.
            $window.sessionStorage.setItem('token', data.token);
            $location.path('/');
        )
        .error(function () 
            $window.sessionStorage.removeItem('token');
            // TODO: Show something like "Username or password invalid."
        );
    ;
);

sessionStorage 只要用户打开页面,就会保留数据。如果您想自己处理过期时间,可以改用localStorage。界面是一样的。

第 4 步:

要在每个请求中向服务器发送令牌,您可以使用 Angular 所称的 Interceptor。您所要做的就是获取先前存储的令牌(如果有)并将其作为标头附加到所有传出请求:

app.factory('AuthInterceptor', function ($window, $q) 
    return 
        request: function(config) 
            config.headers = config.headers || ;
            if ($window.sessionStorage.getItem('token')) 
                config.headers.Authorization = 'Bearer ' + $window.sessionStorage.getItem('token');
            
            return config || $q.when(config);
        ,
        response: function(response) 
            if (response.status === 401) 
                // TODO: Redirect user to login page.
            
            return response || $q.when(response);
        
    ;
);

// Register the previously created AuthInterceptor.
app.config(function ($httpProvider) 
    $httpProvider.interceptors.push('AuthInterceptor');
);

并确保始终使用 SSL!

【讨论】:

这真是一个很好的解释,正是我想要的。非常感谢您的回答和详细的示例。 当然,很高兴我能帮上忙 :) 是否有通过隐藏的 i-frame 刷新访问令牌(假设身份验证服务器记住他们之前的权限)? 更具体地说,您为什么使用return config || $q.when(config); 而不仅仅是return config 使用会话存储/本地存储来存储令牌是否安全?另一个页面中的脚本可以访问令牌吗?

以上是关于AngularJS客户端路由和令牌认证与web api的主要内容,如果未能解决你的问题,请参考以下文章

使用 angularjs ui 时节点/Angularjs 应用程序未接收 JSON Web 令牌

Django REST框架令牌认证AngularJS

从 Web Api 向 AngularJs 客户端返回权限

如何在路由前检查令牌的有效性?

Laravel 通过 Passport 实现 API 请求认证:开放平台篇(客户端凭证令牌)

Laravel 通过 Passport 实现 API 请求认证:隐式授权令牌