内容


使用 Bluemix 和 MEAN 堆栈构建自助发表 Facebook 信息的应用程序,第 1 部分:使用 Facebook 作为登录来源

Comments

有时用户希望当他们不在线时,服务器会代表他们向 Facebook 发表帖子。例如,业务页面的所有者可能希望在某款产品的库存不多时发布公告,鼓励顾客在还有货时尽快购买。或者一个人可能希望他的时间表以随机的间隔发布消息。

可以编写一个服务器应用来实现此目的,但这么做并不容易。在这个 3 教程系列文章中,我将展示如何使用 IBM Bluemix 作为云提供商来实现此目的。本系列还会介绍 MEAN 堆栈所有 4 个组件的基本知识。为了演示此功能,我将展示如何构建一个应用程序,在随机的时间代表用户发表笑话。

  • 第 1 部分(本教程)介绍如何使用 Facebook 作为登录来源和身份验证机制。
  • 第 2 部分 介绍如何配置 MongoDB 来存储从 Facebook 获取的用户信息。
  • 第 3 部分 介绍如何使用 Facebook REST API 让服务器充当用户。

备注:Bluemix 包含一个 Single Sign On 服务,该服务可从 Facebook 以及其他来源接收登录信息。但是,我在本教程中不会使用它。此处说明的应用程序类型与 Facebook 具有紧密的联系,所以让用户使用其他帐户登录没什么帮助。

构建您的应用程序所需的准备工作

Facebook API

有两个 API 可供 Facebook 应用程序开发人员使用:

  • 浏览器 API
  • Web 服务器 API

浏览器 API

这个 Facebook 标准 API 假设用户已通过浏览器登录到应用程序,并通过在浏览器中执行的 JavaScript 代码来完成大部分操作。

从安全的角度讲,这是一种出色的机制,因为它让 Facebook 承载的库能够直接联系用户来要求获得权限。但是它不适合本教程的用途。我们需要在用户未登录时系统能代表用户执行操作。

Web 服务器 API

还可以直接从服务器使用的另一个 API。它使用基于 HTTP 请求的 REST 架构,这使得它更难使用,但是可以确保您能从任何现代服务器编程中环境获得它。这是本系列文章中使用的主要接口。

第 1 步. 创建一个 Node.js Bluemix 应用程序

首先登录 Bluemix 并创建一个 Node.js 应用程序。请参阅 developerWorks 上的 IBM Bluemix 页面 获取帮助。

本教程假设您正在修改默认的 Node.js 入门应用程序。

第 2 步. 创建一个 Facebook 应用程序

按照以下步骤在 Facebook 中创建应用程序:

  1. 登录 Facebook。
  2. 找到 Facebook 应用程序仪表板,然后单击 Add a New App
  3. 选择 Website
  4. 命名应用程序(我使用的名称是 “JokeSender”),然后单击 Create New Facebook App ID
  5. 选择一个类别,然后单击 Create App ID
  6. 对于站点 URL 和移动站点 URL,输入您在 第 1 步 中创建的应用程序的主 URL。在我的例子中,URL 是 http://using-facebook.mybluemix.net/。
  7. 您将看到此时显示了一些 JavaScript 代码,其内容看起来应与下面的代码类似。复制这些代码并将其粘贴在某处;在后面的第 9 步中需要它。
    		<script>
    			window.fbAsyncInit = function() {
    				FB.init({
    					appId      : '833955603319525',
    					xfbml      : true,
    					version    : 'v2.3'
    				});
    			};
    
    			(function(d, s, id){
    				var js, fjs = d.getElementsByTagName(s)[0];
    				if (d.getElementById(id)) {return;}
    				js = d.createElement(s); js.id = id;
    				js.src = "//connect.facebook.net/en_US/sdk.js";
    				fjs.parentNode.insertBefore(js, fjs);
    			}(document, 'script', 'facebook-jssdk'));
    		</script>
  8. 单击 Next
  9. 在开发环境中,向代码中添加您刚复制的 Like 按钮的 HTML。
    <div class="fb-like", data-share="true" data-width="450", data-show-faces="true">
    </div>
  10. 将 public/index.html 文件的正文替换为第 9 步的结果,内容与下面这段代码类似。确保使用您自己的 appId 编号,而不是我的。
    <script>
    	window.fbAsyncInit = function() {
    		FB.init({
    			appId      : '833955603319525',
    			xfbml      : true,
    			version    : 'v2.3'
    		});
    	};
    
    	(function(d, s, id){
    		var js, fjs = d.getElementsByTagName(s)[0];
    		if (d.getElementById(id)) {return;}
    		js = d.createElement(s); js.id = id;
    		js.src = "//connect.facebook.net/en_US/sdk.js";
    		fjs.parentNode.insertBefore(js, fjs);
    	}(document, 'script', 'facebook-jssdk'));
    </script>	
    <div class="fb-like", data-share="true" data-width="450", data-show-faces="true">
    </div>
  11. 运行应用程序。确保它拥有 Like 按钮。

出现 Bluemix 服务器错误时的解决办法(在 Eclipse 中)

有时 Bluemix 和 Eclipse 不同步,而且在尝试上传和运行应用程序时会出现服务器错误。

  1. 在快速访问区域中,键入 server
  2. 选择 Views > Servers
  3. 右键单击 IBM Bluemix,然后选择 Update Password
  4. 在两个字段中键入您的 IBM 密码,然后单击 OK
  5. 此刻不要执行密码恢复,单击 No
  6. 尝试再次运行应用程序。
  7. 如果应用程序无法运行,请展开 Bluemix 服务器视图,删除该应用程序,然后再试一次。

第 3 步. 启用 Facebook 中的应用程序登录

下一步是让用户通过 Facebook 登录您的应用程序。

  1. 返回 Facebook 应用程序仪表板
  2. 选择您的应用程序。
  3. 单击 Show 并记下 App ID 和 App Secret。
  4. 单击 Advanced 选项卡。
  5. 在 OAuth Settings 标题下,将 Client OAuth Login 和 Embedded browser OAuth Login 都设为 Yes
  6. 在 Valid OAuth redirect URIs 字段中,键入:
    https://<application name>.mybluemix.net/index.html

    一般而言,加密对应用程序的访问是个不错的想法。通过仅指定 https,您可确保您的应用程序不支持未加密的登录。
  7. 单击 Save Changes

第 4 步. 运行初始应用程序

下一步是让用户使用客户端 JavaScript 接口,从应用程序的网页登录 Facebook。这也是您开始使用一些库的地方,比如 Angular 和 Bootstrap。我们对默认应用程序进行了太多的更改,无法一一列出,所以我在这里提供了修改后的代码并将解释放在注释中。

  1. 创建一个名为 public/scripts 的目录。
  2. 删除两个目录:public/images 和 public/stylesheets。
  3. 创建一个名为 public/scripts/datamodel.js 的文件夹,其中包含以下代码:
    // Angular data model
    
    // Create a new Angular application
    var myApp = angular.module("myApp", []);
    
    
    // Define the controller for Facebook interaction. The
    // controller also contains scope, which includes the
    // data model.
    myApp.controller("facebookCtrl", function($scope) {
    	
    	// For now, have one string in the data model,
    	// fbStatus. It will contain the status of the
    	// Facebook communication
    	$scope.fbStatus = "";
    });
    
    
    // This function sets the fbStatus variable to the parameter.
    // It is useful to have this function so that the rest of
    // the JavaScript code would be able to set the value of
    // $scope.fbStatus without having to know anything about
    // Angular.
    var setFacebookStatus = function(status) {
    	var scope = angular.element($("#facebookCtrl")).scope();
    	
    	// scope.$apply takes a function because of re-entrancy.
    	// The browser may not be able to handle changes in the
    	// scope variable immediately, in which case the function
    	// will be executed later.
    	scope.$apply(function() {
    		scope.fbStatus = status;
    	});
    };
  4. 创建一个名为 public/scripts/facebook.js 的文件,它将包含以下代码(一定要将 FB.init 中的 appID 改为您自己的应用程序值):
    // This function is called during initialization and after
    // the user clicks the logon button.
    function checkLoginState() {	
    	// Ask Facebook about the currently logged in user, and
    	// call statusChangeCallback when you get the response.
    	FB.getLoginStatus(function(response) {
    		statusChangeCallback(response);
    	});
    }
    
    
    // This function is called by FB.getLoginStatus after it gets the
    // results.
    function statusChangeCallback(response) {
    	// The response object is returned with a status field that lets the
    	// app know the current login status of the person.
    	//
    	// The response object can be found in the documentation
    	// for FB.getLoginStatus().
    	
    	if (response.status === 'connected') {
    		// Logged into your app and Facebook.
    		loggedOn();
    	} else if (response.status === 'not_authorized') {
    		setFacebookStatus("Please authorize this application");
    	} else {
    		// Not logged into Facebook
    		setFacebookStatus("Please log into Facebook");
    	}
    }
    
    
    // Facebook API initialization function. This function is called
    // after the Facebook API code is downloaded from Facebook's site.
    window.fbAsyncInit = function() {
    	
    	// Initialize the Facebook SDK. Make sure to change appId to your
    	// value.
    	FB.init({
    		appId      : '833955603319525',
    		cookie     : true,  // enable cookies to allow the server to access
    							// the session
    		xfbml      : true,  // parse social plugins on this page
    		version    : 'v2.2' // use version 2.2
    	});
    
    	// Check logged in status
    	checkLoginState();
    };
    
    
    // This code loads the Facebook SDK asynchronously. This way, the page
    // is displayed as soon as possible, and the Facebook elements are added
    // later when they are available.
    (function(d, s, id) {
    	var js, fjs = d.getElementsByTagName(s)[0];
    
    	// If there is already a facebook-jssdk element, do nothing.
    	// It means the Facebook SDK is already available.
    	if (d.getElementById(id)) return;
    	
    	// Create an element for the Facebook SDK and call it
    	// facebook-jssdk. Put the element into the variable
    	// js
    	js = d.createElement(s); js.id = id;
    	
    	// This is the script to get the source for the SDK.
    	js.src = "//connect.facebook.net/en_US/sdk.js";
    	
    	// Insert the script before the first element in the
    	// document
    	fjs.parentNode.insertBefore(js, fjs);
    }(document, 'script', 'facebook-jssdk'));
    
    
    
    
    // This function is called when we KNOW the user is logged on.
    var loggedOn = function() {
    	setFacebookStatus("You're in");
    }
  5. public/index.html 文件中 head 标签的主要用途是引用网页使用的所有库。将它替换为以下代码。
    <title>Using Facebook Sample App</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    	
    <!--  Use jQuery  -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js">
    </script>
    	
    <!-- Use the Bootstrap theme -->
    <link rel="stylesheet"
    	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
    <link rel="stylesheet"
    href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css" />
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js">
    </script>
    
    <!--  Use the Angular library  -->
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js">
    </script>
    	
    <!-- Use the application's scripts -->
    <script src="scripts/facebook.js">
    </script>
    <script src="scripts/datamodel.js">
    </script>
  6. 将 public/index.html 文件中 body 标签的内容替换为:
    <!-- Everything in the body is part of the myApp
    			Angular application -->
    <body ng-app="myApp">
    <!--  Everything in this div has access to the scope variables
    	  in facebookCtrl (defined in datamodel.js)  -->
    	<div ng-controller="facebookCtrl" id="facebookCtrl">
    	
    		<!-- The Facebook login button. The scope attribute
    		identifies the permissions we want the user
    		to give us  -->
    		<fb:login-button scope="public_profile,email"
    			onlogin="checkLoginState();">
    		Login
    		</fb:login-button>
    
    		<h3>Status</h3>
    		
    		<!-- When inside an Angular scope, such as facebookCtrl,
    		code encased in {{ }} is evaluated as JavaScript with
    		access to the scope variable ($scope). In this case,
    		it returns the value of $scope.fbStatus, which is
    		determined by setFacebookStatus().  -->
    		{{fbStatus}}
    	</div>
    </body>
  7. 运行应用程序,确保状态会根据您是否登录 Facebook 以及您是否向应用程序授权而发生变化。您可在 Facebook 中撤销应用程序权限,看看应用程序未授权时会发生什么。

第 5 步. 重定向到 HTTPS

通过未加密的通道(比如 HTTP)接受身份验证信息不是个好主意。现在,如果用户尝试通过 HTTP 登录,那么 Facebook 会拒绝该请求。

错误消息
错误消息

如果应用程序自动将浏览器重定向到 HTTPS,这样会对用户更加友好。要添加此特性,打开 app.js 文件并将 app.use 函数调用替换为以下代码。如果现在有 app.get 函数调用,请删除它。请注意,与其他 JavaScript 文件相反,app.js 文件在服务器上运行。这是响应 HTTP 请求并向浏览器发出文件的代码。

app.get('/*', function(req, res) {	
	// If the forwarded protocol isn't HTTPS, send a redirection
	if (req.headers["x-forwarded-proto"] != "https")
		// Always redirect to /. This is a single-page application
		// meaning users have only one URL they need to access
		// directly. Any changes on that page to display information
		// will be handled by client-side JavaScript
		res.redirect("https://" + req.headers.host + "/");
	else {  // Actually serve the request file
		
		// Get the path. req.params[0] is the first wildcard in the
		// file path. In the case, the file path in the get command
		// is "/*", so it is everything after the initial slash.
		//
		// If there is nothing there, then the user just asked
		// for the website, and we need to insert the index.html
		// file.
		var path = req.params[0] ? req.params[0] : 'index.html';
		
		// Actually send the file from the public directory.
		res.sendFile(path, {root: './public'});
	}		
});

查找请求字段

我最初不知道要使用的值是 req.headers["x-forwarded-proto"]。为了找到此值,我首先将此行代码放在函数中:

console.log(Object.keys(req));

控制台中的结果(可在 Eclipse 中看到)为我提供了 req 对象中的字段列表。然后我查看了头部,看看它包含哪些字段:

console.log(Object.keys(req.headers));

需要检查 x-forwarded-proto 的原因是,Bluemix 应用程序始终会接收 HTTP。在互联网与 HTTPS 隧道终端的应用程序之间的路径上有一个 DataPower 设备。因此,您不能依靠该协议来区分 HTTP 与 HTTPS。

结束语

现在您可从 Facebook 登录一个浏览器,该浏览器正在运行 Bluemix 上的应用程序。这还不是很有趣,在本系列的下期 “将用户信息存储在服务器上” 中,您将学习如何使用这种登录方法将从 Facebook 获取的用户信息存储在服务器上。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Cloud computing, Web development
ArticleID=1010790
ArticleTitle=使用 Bluemix 和 MEAN 堆栈构建自助发表 Facebook 信息的应用程序,第 1 部分:使用 Facebook 作为登录来源
publish-date=07132015