21

我使用 Apigility 创建了一个 API。我正在尝试为我目前正在使用 API 构建的前端应用程序设置身份验证系统。

但是我用于此身份验证系统的所有角度身份验证模块都与 Apigility oAuth 2 实现不匹配:

  1. https://github.com/lynndylanhurley/ng-token-auth该模块的问题是,它不允许 CORS。但是,它允许使用角度代码所在的服务器上的代理发送 CORS 请求,这是我使用 Guzzle 用 PHP 编写的。但是使用代理 ng-token-auth 发送请求两次成功,即使所有身份验证数据都是错误的。

  2. https://github.com/sahat/satellizer这个模块需要 JWT 的实现,但是在 Apigility Authentication 部分我没有看到任何关于它的文档。

我需要帮助来完成我的项目。

4

3 回答 3

5

我将尝试给出一个完整的方法,说明我如何使ng-token-auth与 ZF2 一起工作。首先,ng-token-auth 可以与 ruby​​ 模块一起正常工作。因此,要使其与 ZF2 一起使用:

使用以下代码行解决 CORS 问题:

//HttpProvider
$httpProvider.defaults.useXDomain = true;
$httpProvider.defaults.headers.common['Access-Control-Request-Method'] = "POST, GET, PUT, DELETE";
$httpProvider.defaults.headers.common['Origin'] = "http://xxxxxxxxxxxxxxx";
$httpProvider.defaults.headers.common['Accept'] = "application/json";
$httpProvider.defaults.headers.common['Content-Type'] = "application/json; text/html";
delete $httpProvider.defaults.headers.common['X-Requested-With'];

使用@josilber 和@sven-lauterbach回答中指出的ZFCORS 解决ZF2 上的CORS 问题

使用这些代码行格式化 ZF2 发送的响应以使其与 ng-token-auth 一起使用

$http.defaults.transformResponse = function(value, headerGetters){
    var response_header = headerGetters(),
    response_data   = JsonHelper.IsJsonString(value) ? JSON.parse(value) : value;
    if(response_data){
        if(response_data.access_token)
            response_header['access_token']  = response_data.access_token;
        if(response_data.expires_in){
            var now = new Date().getTime();
            response_header['expires_in']    = now + ( parseInt(response_data.expires_in, 10) * 1000 );
        } 
        if(response_data.token_type)
            response_header['token_type']    = response_data.token_type;
        if(response_data.refresh_token)
            response_header['refresh_token'] = response_data.refresh_token;
        if(response_data.scope)
            response_header['scope']         = response_data.scope;
        return response_data;
    }
};

可能这不是在 AngularJS 中转换响应的最佳方法,但它解决了格式化 OAuth2 响应的问题,该问题适用于 ng-token-auth

最后,要使用 auth token 向服务器发送请求并自动刷新 token,需要更改 ng-token-auth 的一些行为。我在 AngularJS 上使用了装饰模式,通过这些代码片段解决了这个问题:

在 app.js 中

//Change behavior of oauth2 module 
$provide.decorator("$auth", function($delegate, ApiAuthService){
    return ApiAuthService($delegate);
}); 

其中ApiAuthService是由这段代码定义的工厂:

AuthProviderService.factory('ApiAuthService', ['MeService', function( MeService ){
    return function($delegate){
        return {
            initialize: function(){ return $delegate.initialize(); },
            apiUrl: function(configName){ },
            retrieveData: function(key){ return $delegate.retrieveData(key); },
            getConfig: function(name){ return $delegate.getConfig(name); },
            getExpiry: function(){  return $delegate.getExpiry(); },
            setAuthHeaders: function(h){ return $delegate.setAuthHeaders(h); },
            /*persistData: function(key, val, configName){ return $delegate.persistData(key, val, configName); },
            retrieveData: function(key){ return $delegate.retrieveData(key); },*/
            rejectDfd: function(reason){ $delegate.rejectDfd(reason); },
            invalidateTokens: function(){ return $delegate.invalidateTokens(); },
            submitLogin: function(params, opts){ return $delegate.submitLogin(params, opts); },
            validateUser: function(opts){  
                result = $delegate.validateUser(opts);
                return result;
            },
            deleteData: function(key){  
                return $delegate.deleteData(key);
            }
        };
    };
}]).config(['$httpProvider', function($httpProvider) {

    $httpProvider.interceptors.push([
         '$injector', function($injector) {
           return {
             request: function(req) {
               $injector.invoke([
                 '$http', '$auth', function($http, $auth) {
                   var key, 
                       _ref, 
                       _results = [];
                   if (req.url.match($auth.apiUrl())) {
                     _ref = $auth.retrieveData('auth_headers');
                     //Inject value into body of request 
                     for (key in _ref) {
                         //Set Authorization request header.
                         if(key.match('access_token')){
                             if(req.headers){
                                 req.headers['Authorization'] = 'Bearer ' + _ref[key]; 
                             }else{
                                 req.headers = {'Authorization': 'Bearer ' + _ref[key]};
                             }
                         }
                         if(req.headers[key]){
                             delete req.headers[key];
                         }
                     }
                     return _results;
                   }
                 }
               ]);
               return req;
             }
           };
         }
       ]);
}]);

最后我的 ng-token-auth 配置是:

//OAuth2 Module configs
$authProvider.configure([ {
    "default": {
        apiUrl:                  API_URL,
        tokenValidationPath:     '/me',
        signOutUrl:              '/oauth',
        emailRegistrationPath:   '/oauth',
        accountUpdatePath:       '/oauth',
        accountDeletePath:       '/oauth',
        confirmationSuccessUrl:  window.location.href,
        passwordResetPath:       '/oauth',
        passwordUpdatePath:      '/oauth',
        passwordResetSuccessUrl: window.location.href,
        emailSignInPath:         '/oauth',
        forceHardRedirect: true,
        storage:                 'localStorage',
        proxyIf:                 function() { return false; },
        proxyUrl:                'proxy',
        authProviderPaths: {
            github:   '/auth/github',
            facebook: '/auth/facebook',
            google:   '/auth/google'
        },
        tokenFormat: {
            "access_token" : "{{ token }}",
            "token_type"   : "Bearer",
            "refresh_token": "{{ clientId }}",
            "expires_in"   : "{{ expiry }}",
            "scope"        : "{{ uid }}"
        },
        parseExpiry: function(headers) {
            var expires_in = parseInt(headers['expires_in'], 10) || null;
                return expires_in;
            },
            handleLoginResponse: function(response) {
                //Patch for persistant data as library retreive auth data from header.
                return response;
            },
            handleAccountResponse: function(response) {
                return response;
            },
            handleTokenValidationResponse: function(response) {
                return response;
            }
        }
} ]);

@JerinKAlexander 我希望这些步骤能帮助您找到比我做的更好的方式来解决您的问题。

于 2015-06-25T00:36:44.390 回答
2

实际上,您可以使用一种相当简单但简洁的解决方法satellizer来开始工作。Apigility看看这里:

http://adam.lundrigan.ca/2014/11/06/using-oauth2-jwt-with-apigility/

和这里:

https://github.com/adamlundrigan/LdcOAuth2CryptoToken/blob/master/src/Factory/CryptoTokenServerFactory.php

Apigility 为它的所有内部服务定义了服务工厂。这里的基本思想是简单地定义一个注入必要配置的服务管理器委托工厂。

<?php  
namespace LdcOAuth2CryptoToken\Factory;

use Zend\ServiceManager\DelegatorFactoryInterface;  
use Zend\ServiceManager\ServiceLocatorInterface;
class CryptoTokenServerFactory implements DelegatorFactoryInterface  
{
    public function createDelegatorWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName, $callback)
    {
        $server = call_user_func($callback);

        // do your thing to $server here

        return $server;
    }
}

感谢Adam Lundrigan :)

于 2015-06-18T10:17:46.553 回答
1

您想使用 Apigility 作为后端。您有一个在不同域上运行的 HTML 应用程序,并且此 HTML 应用程序应该使用 OAuth 身份验证调用 Apigility 后端?如果这是您要完成的任务,则必须设置 Apigility 以支持 CORS 调用,请查看https://apigility.org/documentation/recipes/allowing-request-from-other-domains

他们使用“ZfrCors”模块:

他们使用以下示例:

return array(
'zfr_cors' => array(
     /**
      * Set the list of allowed origins domain with protocol.
      */
     'allowed_origins' => array('http://www.sexywidgets.com'),

     /**
      * Set the list of HTTP verbs.
      */
     'allowed_methods' => array('GET', 'OPTIONS'),

     /**
      * Set the list of headers. This is returned in the preflight request to indicate
      * which HTTP headers can be used when making the actual request
      */
     'allowed_headers' => array('Authorization', 'Content-Type'),

     /**
      * Set the max age of the preflight request in seconds. A non-zero max age means
      * that the preflight will be cached during this amount of time
      */
     // 'max_age' => 120,

     /**
      * Set the list of exposed headers. This is a whitelist that authorize the browser
      * to access to some headers using the getResponseHeader() JavaScript method. Please
      * note that this feature is buggy and some browsers do not implement it correctly
      */
     // 'exposed_headers' => array(),

     /**
      * Standard CORS requests do not send or set any cookies by default. For this to work,
      * the client must set the XMLHttpRequest's "withCredentials" property to "true". For
      * this to work, you must set this option to true so that the server can serve
      * the proper response header.
      */
     // 'allowed_credentials' => false,
),
);

您所要做的就是将“allowed_origins”选项设置为您的 HTML 应用程序的域。

对于 OAuth 部分,您可以在此处获取更多信息:https ://apigility.org/documentation/auth/authentication-oauth2

您应该仔细查看“基于浏览器的应用程序”部分,因为您使用 HTML 应用程序来访问您的 apigility 后端。使用这篇文章中提供的信息,您可以使用https://github.com/sahat/satellizer

如果您需要更多信息,请告诉我。

于 2015-06-19T13:54:37.473 回答