让 ng-token-auth 与 devise_token_auth 一起工作
Getting ng-token-auth to work with devise_token_auth
我有一个 Rails 和 Ionic 项目。后端使用devise_token_authGem,前端使用ng-token-auth;这些应该可以工作 "seamlessly".
就注册和登录而言,我已经完成了所有工作,returns 这是一个有效的响应对象。但是,在我使用 $state.go('app.somepage') 之后的任何进一步请求都会导致 401 未经授权的响应。
我感觉我实际上并没有将令牌存储在任何地方。有人可以帮忙吗?
以下是一些片段:
.controller('LoginCtrl',['$scope', '$auth', '$state', function($scope, $auth, $state) {
$scope.loginForm = {}
$scope.handleLoginBtnClick = function() {
console.log($scope.loginForm);
$auth.submitLogin($scope.loginForm)
.then(function(resp) {
$state.go('app.feed');
})
.catch(function(resp) {
console.log(resp.errors);
});
};
状态定义:
.state('app', {
url: "/app",
abstract: true,
templateUrl: "templates/menu.html",
controller: 'AppCtrl',
resolve: {
auth: function($auth) {
return $auth.validateUser();
}
}
})
资源:
factory('Post', ['railsResourceFactory', 'apiUrl', function (railsResourceFactory, apiUrl) {
return railsResourceFactory({
url: apiUrl + '/posts',
name: 'post'
});
}]).
在 PostsCtrl 中:
$scope.loadFeed = function() {
Post.query().then(function (posts) {
$scope.posts = posts;
}, function (error) {
console.log( 'Did not get posts!'); ### THIS FIRES
}).finally(function() {
// Stop the ion-refresher from spinning
$scope.$broadcast('scroll.refreshComplete');
});
};
登录响应对象:
{"data":{"id":1,"provider":"email","uid":"1234","phone":null,"name":"Admin","image":null,"username":"admin"}}
ApplicationController 顶部:
class ApplicationController < ActionController::Base
include DeviseTokenAuth::Concerns::SetUserByToken
before_filter :add_allow_credentials_headers
before_filter :cors_preflight_check
after_filter :cors_set_access_control_headers
before_action :configure_permitted_parameters, if: :devise_controller?
..yadayada...
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :phone
devise_parameter_sanitizer.for(:sign_up) << :username
devise_parameter_sanitizer.for(:sign_up) << :session
devise_parameter_sanitizer.for(:sign_in) << :phone
devise_parameter_sanitizer.for(:sign_in) << :username
devise_parameter_sanitizer.for(:sign_in) << :session
end
以及 rails 端用户的一些默认模型。
Rails 日志:
Started GET "/posts" for 192.168.83.26 at 2015-02-24 23:29:02 -0500
Processing by PostsController#index as JSON
Parameters: {"post"=>{}}
Filter chain halted as :authenticate_user! rendered or redirected
Completed 401 Unauthorized in 1ms (Views: 0.2ms | ActiveRecord: 0.0ms)
如果有人能提供一些见解,那就太好了。我很乐意根据需要 post 更多片段。
您是否尝试过为 $authProvider 添加配置。这个例子在 https://github.com/lynndylanhurley/devise_token_auth.
的自述文件中
angular.module('myApp', ['ng-token-auth'])
.config(function($authProvider) {
$authProvider.configure({
apiUrl: 'http://api.example.com'
authProviderPaths: {
github: '/auth/github' // <-- note that this is different than what was set with github
}
});
});
至于我的情况,我使用 cookie 来存储令牌。每当我们在 Angular 应用程序中执行 $auth
方法时,某些方法将尝试转到您在 Rails 路由器和 match/validate 中定义的设计路由存储在 header 的任何请求中的令牌。 (每次你尝试做 http 请求时!如果你要通过 GET
/validate_token
(https://github.com/lynndylanhurley/devise_token_auth#usage-tldr))
由于您没有提及您的路线,我们可以假设 /auth
。
而 $auth
提供的那些 $http
请求应该包含要在 Rails 的 Devise 中进行身份验证的令牌,并在我们任何时候捕获并将其存储到浏览器的 cookie 中做 $auth.submitLogin()
.
这是我之前项目中的示例。
app.factory('authInterceptor', ['$q', 'ipCookie', '$location', function($q, ipCookie, $location) {
return {
request: function(config) {
config.headers = config.headers || {};
if (ipCookie('access-token')) {
config.headers['Access-Token'] = ipCookie('access-token');
config.headers['Client'] = ipCookie('client');
config.headers['Expiry'] = ipCookie('expiry');
config.headers['Uid'] = ipCookie('uid');
}
return config;
},
responseError: function(response) {
if (response.status === 401) {
$location.path('/login');
ipCookie.remove('access-token');
}
return $q.reject(response);
}
};
}])
并将令牌格式设置为如下所示(或根据需要自定义)
$authProvider.configure({
tokenValidationPath: '/auth/validate_token',
signOutUrl: '/auth/sign_out',
confirmationSuccessUrl: window.location.href,
emailSignInPath: '/auth/sign_in',
storage: 'cookies',
tokenFormat: {
"access-token": "{{ token }}",
"token-type": "Bearer",
"client": "{{ clientId }}",
"expiry": "{{ expiry }}",
"uid": "{{ uid }}"
}
});
不要忘记将 ipCookie
(查找 angular-cookie
而不是 angular-cookies
)注入拦截器,因为这是 ng-token-auth 用于 cookie 的 cookie 库管理。
如果我说得不够清楚,请在下方评论提出问题。 :D
此信息可能与您相关。
我怀疑您尝试登录的用户无效。发生这种情况时,授权 header 为空,响应中不会发回任何访问令牌。
事实证明,解决方案相当简单。似乎在每个人提供的大多数示例中,他们都忽略了允许 access-token
,以及所有其他 CORS headers.
我们为此使用了 rack-cors,在 config.ru
的底部:
require 'rack/cors'
use Rack::Cors do
# allow all origins in development
allow do
origins '*'
resource '*',
:headers => :any,
:expose => ['access-token', 'expiry', 'token-type', 'uid', 'client'],
:methods => [:get, :post, :delete, :put, :options]
end
end
然后在ApplicationController.rb:
before_filter :add_allow_credentials_headers
skip_before_filter :verify_authenticity_token
before_filter :cors_preflight_check
after_filter :cors_set_access_control_headers
def cors_set_access_control_headers
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT, DELETE, OPTIONS'
headers['Access-Control-Allow-Headers'] = 'Origin, Content-Type, Accept, Authorization, Token'
headers['Access-Control-Max-Age'] = '1728000'
end
def cors_preflight_check
if request.method == 'OPTIONS'
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT, DELETE, OPTIONS'
headers['Access-Control-Allow-Headers'] = 'X-Requested-With, X-Prototype-Version, Token'
headers['Access-Control-Max-Age'] = '1728000'
render :text => '', :content_type => 'text/plain'
end
end
def add_allow_credentials_headers
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#section_5
#
# Because we want our front-end to send cookies to allow the API to be authenticated
# (using 'withCredentials' in the XMLHttpRequest), we need to add some headers so
# the browser will not reject the response
response.headers['Access-Control-Allow-Origin'] = request.headers['Origin'] || '*'
response.headers['Access-Control-Allow-Credentials'] = 'true'
end
也许为时已晚,
但问题是因为您无法对 cookie 进行身份验证(仅 Android)。因此,您可以尝试使用 localStorage 来保存您的会话信息(在 iOS & Android)
例如
.config(function($authProvider) {
$authProvider.configure({
apiUrl: 'http://myprivateapidomain/api',
storage: 'localStorage'
});
})
您可以在文档的具体问题中阅读更多内容:https://github.com/lynndylanhurley/ng-token-auth/issues/93
有点晚了,但对于任何可能尝试在 Ionic 应用程序中使用 ng-token-auth 的人来说,我所做的使其工作(在我的情况下)是为我的模块设置下一个配置:
app.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.withCredentials = true;
}]);
(我没有在我的 http 请求中发送任何 cookie)
我有一个 Rails 和 Ionic 项目。后端使用devise_token_authGem,前端使用ng-token-auth;这些应该可以工作 "seamlessly".
就注册和登录而言,我已经完成了所有工作,returns 这是一个有效的响应对象。但是,在我使用 $state.go('app.somepage') 之后的任何进一步请求都会导致 401 未经授权的响应。
我感觉我实际上并没有将令牌存储在任何地方。有人可以帮忙吗?
以下是一些片段:
.controller('LoginCtrl',['$scope', '$auth', '$state', function($scope, $auth, $state) {
$scope.loginForm = {}
$scope.handleLoginBtnClick = function() {
console.log($scope.loginForm);
$auth.submitLogin($scope.loginForm)
.then(function(resp) {
$state.go('app.feed');
})
.catch(function(resp) {
console.log(resp.errors);
});
};
状态定义:
.state('app', {
url: "/app",
abstract: true,
templateUrl: "templates/menu.html",
controller: 'AppCtrl',
resolve: {
auth: function($auth) {
return $auth.validateUser();
}
}
})
资源:
factory('Post', ['railsResourceFactory', 'apiUrl', function (railsResourceFactory, apiUrl) {
return railsResourceFactory({
url: apiUrl + '/posts',
name: 'post'
});
}]).
在 PostsCtrl 中:
$scope.loadFeed = function() {
Post.query().then(function (posts) {
$scope.posts = posts;
}, function (error) {
console.log( 'Did not get posts!'); ### THIS FIRES
}).finally(function() {
// Stop the ion-refresher from spinning
$scope.$broadcast('scroll.refreshComplete');
});
};
登录响应对象:
{"data":{"id":1,"provider":"email","uid":"1234","phone":null,"name":"Admin","image":null,"username":"admin"}}
ApplicationController 顶部:
class ApplicationController < ActionController::Base
include DeviseTokenAuth::Concerns::SetUserByToken
before_filter :add_allow_credentials_headers
before_filter :cors_preflight_check
after_filter :cors_set_access_control_headers
before_action :configure_permitted_parameters, if: :devise_controller?
..yadayada...
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :phone
devise_parameter_sanitizer.for(:sign_up) << :username
devise_parameter_sanitizer.for(:sign_up) << :session
devise_parameter_sanitizer.for(:sign_in) << :phone
devise_parameter_sanitizer.for(:sign_in) << :username
devise_parameter_sanitizer.for(:sign_in) << :session
end
以及 rails 端用户的一些默认模型。
Rails 日志:
Started GET "/posts" for 192.168.83.26 at 2015-02-24 23:29:02 -0500
Processing by PostsController#index as JSON
Parameters: {"post"=>{}}
Filter chain halted as :authenticate_user! rendered or redirected
Completed 401 Unauthorized in 1ms (Views: 0.2ms | ActiveRecord: 0.0ms)
如果有人能提供一些见解,那就太好了。我很乐意根据需要 post 更多片段。
您是否尝试过为 $authProvider 添加配置。这个例子在 https://github.com/lynndylanhurley/devise_token_auth.
的自述文件中angular.module('myApp', ['ng-token-auth'])
.config(function($authProvider) {
$authProvider.configure({
apiUrl: 'http://api.example.com'
authProviderPaths: {
github: '/auth/github' // <-- note that this is different than what was set with github
}
});
});
至于我的情况,我使用 cookie 来存储令牌。每当我们在 Angular 应用程序中执行 $auth
方法时,某些方法将尝试转到您在 Rails 路由器和 match/validate 中定义的设计路由存储在 header 的任何请求中的令牌。 (每次你尝试做 http 请求时!如果你要通过 GET
/validate_token
(https://github.com/lynndylanhurley/devise_token_auth#usage-tldr))
由于您没有提及您的路线,我们可以假设 /auth
。
而 $auth
提供的那些 $http
请求应该包含要在 Rails 的 Devise 中进行身份验证的令牌,并在我们任何时候捕获并将其存储到浏览器的 cookie 中做 $auth.submitLogin()
.
这是我之前项目中的示例。
app.factory('authInterceptor', ['$q', 'ipCookie', '$location', function($q, ipCookie, $location) {
return {
request: function(config) {
config.headers = config.headers || {};
if (ipCookie('access-token')) {
config.headers['Access-Token'] = ipCookie('access-token');
config.headers['Client'] = ipCookie('client');
config.headers['Expiry'] = ipCookie('expiry');
config.headers['Uid'] = ipCookie('uid');
}
return config;
},
responseError: function(response) {
if (response.status === 401) {
$location.path('/login');
ipCookie.remove('access-token');
}
return $q.reject(response);
}
};
}])
并将令牌格式设置为如下所示(或根据需要自定义)
$authProvider.configure({
tokenValidationPath: '/auth/validate_token',
signOutUrl: '/auth/sign_out',
confirmationSuccessUrl: window.location.href,
emailSignInPath: '/auth/sign_in',
storage: 'cookies',
tokenFormat: {
"access-token": "{{ token }}",
"token-type": "Bearer",
"client": "{{ clientId }}",
"expiry": "{{ expiry }}",
"uid": "{{ uid }}"
}
});
不要忘记将 ipCookie
(查找 angular-cookie
而不是 angular-cookies
)注入拦截器,因为这是 ng-token-auth 用于 cookie 的 cookie 库管理。
如果我说得不够清楚,请在下方评论提出问题。 :D
此信息可能与您相关。
我怀疑您尝试登录的用户无效。发生这种情况时,授权 header 为空,响应中不会发回任何访问令牌。
事实证明,解决方案相当简单。似乎在每个人提供的大多数示例中,他们都忽略了允许 access-token
,以及所有其他 CORS headers.
我们为此使用了 rack-cors,在 config.ru
的底部:
require 'rack/cors'
use Rack::Cors do
# allow all origins in development
allow do
origins '*'
resource '*',
:headers => :any,
:expose => ['access-token', 'expiry', 'token-type', 'uid', 'client'],
:methods => [:get, :post, :delete, :put, :options]
end
end
然后在ApplicationController.rb:
before_filter :add_allow_credentials_headers
skip_before_filter :verify_authenticity_token
before_filter :cors_preflight_check
after_filter :cors_set_access_control_headers
def cors_set_access_control_headers
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT, DELETE, OPTIONS'
headers['Access-Control-Allow-Headers'] = 'Origin, Content-Type, Accept, Authorization, Token'
headers['Access-Control-Max-Age'] = '1728000'
end
def cors_preflight_check
if request.method == 'OPTIONS'
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT, DELETE, OPTIONS'
headers['Access-Control-Allow-Headers'] = 'X-Requested-With, X-Prototype-Version, Token'
headers['Access-Control-Max-Age'] = '1728000'
render :text => '', :content_type => 'text/plain'
end
end
def add_allow_credentials_headers
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#section_5
#
# Because we want our front-end to send cookies to allow the API to be authenticated
# (using 'withCredentials' in the XMLHttpRequest), we need to add some headers so
# the browser will not reject the response
response.headers['Access-Control-Allow-Origin'] = request.headers['Origin'] || '*'
response.headers['Access-Control-Allow-Credentials'] = 'true'
end
也许为时已晚,
但问题是因为您无法对 cookie 进行身份验证(仅 Android)。因此,您可以尝试使用 localStorage 来保存您的会话信息(在 iOS & Android)
例如
.config(function($authProvider) {
$authProvider.configure({
apiUrl: 'http://myprivateapidomain/api',
storage: 'localStorage'
});
})
您可以在文档的具体问题中阅读更多内容:https://github.com/lynndylanhurley/ng-token-auth/issues/93
有点晚了,但对于任何可能尝试在 Ionic 应用程序中使用 ng-token-auth 的人来说,我所做的使其工作(在我的情况下)是为我的模块设置下一个配置:
app.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.withCredentials = true;
}]);
(我没有在我的 http 请求中发送任何 cookie)