为Apigility认证Angular JS模块

21

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

但是,我使用过的所有angular身份验证模块都与Apigility oAuth 2实现不匹配:

  1. https://github.com/lynndylanhurley/ng-token-auth 这个模块的问题是它不允许CORS。但是,可以在存放angular代码的服务器上使用Guzzle编写的代理发送CORS请求。但是,使用代理后,即使所有身份验证数据都为假,ng-token-auth也会成功发送两次请求。

  2. https://github.com/sahat/satellizer 此模块需要实现JWT,但在Apigility身份验证部分中我没有看到任何文档。

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


@JerinKAlexander,您需要与第三方认证器(Google、FB等)集成吗?您是否需要特定的OAuth,还是需要一种验证用户的方法? - Dave Alperovich
@JerinKAlexander,您可以使用ng-token-auth来实现您的目标。在阅读了ng-auth-token源代码后,我已经成功将其与Apigility配合使用。如果您对我的代码感兴趣,我可以提供完整的答案,告诉您如何实现它。 - hermannovich
你应该肯定地发布你的解决方案。 - Dave Alperovich
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问题 answer

使用以下代码行格式化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中转换响应的最佳方式,但它解决了与ng-token-auth一起使用的OAuth2响应格式化问题。

最后,为了使用身份验证令牌向服务器发送请求并自动刷新令牌,需要更改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,我希望这些步骤能帮助你以比我更好的方式解决问题。


2
您可以使用一种相当简单但巧妙的解决方法,使satellizerApigility配合使用。请看这里:http://adam.lundrigan.ca/2014/11/06/using-oauth2-jwt-with-apigility/和这里:https://github.com/adamlundrigan/LdcOAuth2CryptoToken/blob/master/src/Factory/CryptoTokenServerFactory.php。 Apigility为其所有内部服务定义了服务工厂。基本思想是简单地定义一个服务管理器代理工厂,该工厂注入必要的配置。请注意保留HTML标签。
<?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 的帮助 :)

Jester,该链接解决了Apigility不支持的加密令牌问题。但是您没有解释如何从Apigility端点请求令牌,也没有解释如何配置Apigility以创建/发送OAuth令牌,要求/接受OAuth令牌。 - Dave Alperovich
@DaveAlperovich 抱歉,我曾经认为他试图解决的核心问题是Angular身份验证模块与Apigility OAuth2不匹配。此外,配置apigility OAuth2是一项非常简单的任务,在Apigility文档中有很好的记录(https://apigility.org/documentation/auth/authentication-oauth2)。 - jester
公平地说,原帖缺乏细节,投资者也没有添加任何细节。我怀疑需要更多的信息。但是如果没有反馈,我只是在猜测。你可能是正确的。 - Dave Alperovich

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应用程序访问您的API后端。使用本帖提供的信息,您可以使用https://github.com/sahat/satellizer

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接