尝试从FLash应用程序访问Google Authsub时出现403错误(Authsub for Actionscript)

时间:2011-01-08 15:58:42

标签: flex authsub

我正在尝试让我的Flex应用程序使用google中的说明进行身份验证:

http://code.google.com/apis/gdata/docs/as-authsub.html

我在那里复制了这个例子,并试图让它发挥作用。我在loca服务器上运行我的应用程序,所以我更改了下一个参数:

authSubParams['next'] = 'http://localhost/AuthSub.html';

获取第一个令牌工作正常,但当我尝试获取长期存在的令牌时,我收到错误:

getLongLivedToken: singleUseToken: 1%2Fa...
onHttpStatus: [HTTPStatusEvent type="httpStatus" bubbles=false cancelable=false eventPhase=2 status=403 responseURL=null]
onGetTokenFailed: Error #2032: Stream Error. URL: https://accounts.googleapis.com/accounts/AuthSubSessionToken

看起来我收到403错误。有什么想法吗?

整个应用程序:

<?xml version="1.0" encoding="utf-8"?>
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    minWidth="955" 
    minHeight="600"
    initialize=" onInitialized() "
    applicationComplete=" onLoaded() "
    >

    <fx:Script>
        <![CDATA[

            import flash.external.ExternalInterface;
            import flash.net.navigateToURL;

            import mx.controls.Alert;

            private function onInitialized() : void {
                // Load the cross domain policy file for each of the googleapis.com
                // domains used. At the very least, we need the ones for the API (photos)
                // and the one for AuthSub for ActionScript (accounts).
                Security.loadPolicyFile('http://photos.googleapis.com/data/crossdomain.xml');
                Security.loadPolicyFile('https://accounts.googleapis.com/crossdomain.xml');
            }

            private function onLoaded() : void {
                // Once the application has loaded, check to see if an AuthSub token was
                // placed into the current page's URL. If it was, the user has already
                // authenticated, we can continue to connect to the the service itself.
                // In a real application, the long-term cookie would also be stored and 
                // checked here, to remove the need for the user to authenticate for this 
                // application every time it is used.
                var searchPortion : String = ExternalInterface.call('window.location.search.toString');

                //searchPortion = "?token=tokenSavenFromPreviousAttempt";

                if (searchPortion.length > 0) {
                    // remove the ? from the token and extract the token.
                    searchPortion = searchPortion.substring(1);

                    // NOTE: Real applications should parse the URL properly.
                    if (searchPortion.indexOf('token=') == 0) {

                        log( "token found: " + searchPortion );

                        getLongLivedToken(searchPortion.substring(6));
                        return;
                    }
                }

                // No token found; redirect the user to the AuthSub page. Note that this URL
                // is on the google.com domain. We can contact the google.com domain because
                // this isn't a request from within Flash, but rather a page redirect.
                var getTokenPage : URLRequest = new URLRequest('https://www.google.com/accounts/AuthSubRequest');

                // Construct the parameters of the AuthSub request. These are the same parameters
                // as normal AuthSub, which can be found here: http://code.google.com/apis/accounts/docs/AuthSub.html#AuthSubRequest
                var authSubParams : URLVariables = new URLVariables();
                authSubParams['scope'] = 'http://photos.googleapis.com/data'; // photos API
                authSubParams['session'] = 1; // single-use token
                authSubParams['secure'] = 0; // non-secure apps
                authSubParams['next'] = 'http://localhost/AuthSub.html'; // The URL of this app.
                //authSubParams['next'] = 'http://localhost/'; // The URL of this app.

                log( "token not found, sending AuthSubRequest" );

                getTokenPage.data =  authSubParams;
                navigateToURL(getTokenPage, '_top');
            }

            private function getLongLivedToken(singleUseToken : String) : void {
                // Construct a call to the AuthSub for ActionScript endpoint on accounts.googleapis.com.
                // This call will exchange the single use token given to use by AuthSub for a long-term
                // token that we can use to make requests to endpoints such as Photos.
                var getTokenRequest : URLRequest = new URLRequest('https://accounts.googleapis.com/accounts/AuthSubSessionToken');

                // Due to a bug in Flash, a URLRequest with a GET request will
                // not properly send headers. We therefore use POST for this and *ALL*
                // requests.
                getTokenRequest.method = URLRequestMethod.POST;

                // Due to a bug in Flash, a URLRequest without a valid parameter will
                // not properly send headers. We therefore add a useless parameter to
                // make this code work.
                getTokenRequest.data = new URLVariables('pleaseignore=ignore');

                // Add the AuthSub for ActionScript headers.
                getTokenRequest.requestHeaders.push(new URLRequestHeader('Authorization', 'AuthSub token="' + singleUseToken + '"'));

                // Create the loader to get the token itself. The loader will callback
                // to the following event handlers if and when the server responds.
                var getToken : URLLoader = new URLLoader();
                getToken.addEventListener(Event.COMPLETE, onGetTokenResult);
                getToken.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onGetTokenFailed);
                getToken.addEventListener(IOErrorEvent.IO_ERROR, onGetTokenFailed);
                getToken.addEventListener( HTTPStatusEvent.HTTP_STATUS, onHttpStatus );

                log( "getLongLivedToken: singleUseToken: " + singleUseToken );

                try {
                    getToken.load(getTokenRequest);
                } catch (e : Error) {

                    log( "error:\n" + e.message );

                    Alert.show('Some error occurred: ' + e);

                }
            }

            private function onGetTokenResult(e : Event) : void {


                // Load the parameters from the response.
                var getToken : URLLoader = URLLoader(e.target);
                var params : URLVariables = new URLVariables(getToken.data);


                // Parse the session token from the result. Real applications
                // might at this point store the token in a long-term cookie so
                // that repeated usages of the application do not require this entire
                // authentication process.
                var sessionToken : String = params.Token;

                log( "onGetTokenResult: sessionToken: " + sessionToken );

                // Trim the newline from the end of the session token.
                sessionToken = sessionToken.substring(0, sessionToken.length - 1);

                Alert.show('session token: [' + sessionToken + ']');

                // Prepare a request to the photos API for the private album
                // of the user.
                var albumRequest : URLRequest = new URLRequest('http://photos.googleapis.com/data/feed/api/user/default');
                albumRequest.data = new URLVariables('access=private&amp;v=2&amp;err=xml');

                // Due to a bug in Flash, a URLRequest with a GET request will
                // not properly send headers. We therefore use POST for this and *ALL*
                // requests.
                albumRequest.method = URLRequestMethod.POST;

                var authsubHeader : String = 'AuthSub token="' + sessionToken + '"';

                // Add the Authorization header which uses the session token.
                albumRequest.requestHeaders.push(new URLRequestHeader('Authorization', authsubHeader));

                // The X-HTTP-Method-Override header tells the Photos API to treat this request
                // as a GET request, even though it is being conducted as a POST (due to the bug
                // mentioned above). This is very important, as GData APIs will react differently
                // to different HTTP request types.
                albumRequest.requestHeaders.push(new URLRequestHeader('X-HTTP-Method-Override', 'GET'));

                // We expect ATOM XML to be returned.
                albumRequest.requestHeaders.push(new URLRequestHeader('Content-Type', 'application/atom+xml'));

                var getAlbum : URLLoader = new URLLoader();
                getAlbum.addEventListener(Event.COMPLETE, onGetAlbumResult);
                getAlbum.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onGetAlbumFailed);
                getAlbum.addEventListener(IOErrorEvent.IO_ERROR, onGetAlbumFailed);

                try {
                    getAlbum.load(albumRequest);
                } catch (e : Error) {
                    Alert.show('Some error occurred: ' + e);  
                }
            }

            private function onGetAlbumResult(e : Event) : void {
                // Load the XML from the response.
                var getAlbum : URLLoader = URLLoader(e.target);
                Alert.show('Returned XML: ' + getAlbum.data);
            }

            private function onGetTokenFailed(e : ErrorEvent) : void {
                log( "onGetTokenFailed: " + e.text );
                Alert.show('Some error occurred: ' + e);
            }

            private function onGetAlbumFailed(e : ErrorEvent) : void {
                log( "onGetAlbumFailed: " + e.text );
                Alert.show('Some error occurred: ' + e);
            }

            private function onHttpStatus(e : HTTPStatusEvent) : void {
                log( "onHttpStatus: " + e );
                Alert.show('Some error occurred: ' + e);
            }

            private function log( message : String ) : void
            {
                trace( message );

                debugOutput.text = debugOutput.text ? debugOutput.text : "";

                debugOutput.text = debugOutput.text.length == 0 ? message : debugOutput.text + "\n" + message; 
            }

        ]]>
    </fx:Script>

    <s:TextArea
        id="debugOutput"
        left="5"
        right="5"
        top="5"
        bottom="5"
        />

</s:Application>

2 个答案:

答案 0 :(得分:0)

变化:

var searchPortion : String = ExternalInterface.call('window.location.search.toString');

var searchPortion : String = decodeURIComponent( ExternalInterface.call('window.location.search.toString'));

答案 1 :(得分:0)

私有函数onLoaded():void { //加载应用程序后,检查是否有AuthSub令牌 //放入当前页面的URL。如果是,用户已经 //经过身份验证,我们可以继续连接到服务本身。 //在实际应用程序中,也会存储长期cookie //在此处选中,以消除用户对此进行身份验证的需要 //应用程序每次使用时。 var searchPortion:String = ExternalInterface.call('window.location.search.toString');

// searchPortion =“?token = tokenSavenFromPreviousAttempt”;

if(searchPortion.length&gt; 0){    // 除掉 ?从令牌中提取令牌。    searchPortion = searchPortion.substring(1);

//注意:真正的应用程序应该正确解析URL。    if(searchPortion.indexOf('token =')== 0){

  log( "token found: " + searchPortion );

  getLongLivedToken(searchPortion.substring(6));
  return;

} }

令牌未正确解析。使用 var token:String = Application.application.parameters [“token”]; 代替;