2014年1月9日 星期四

Google App Engine 使用Facebook帳號登入

上一篇「Google App Engine 連接資料庫(GAE Datastore)」已經有學習過如何將資料寫進資料庫中了,現在要來學習如何使用Facebook做登入動作,並且取得使用者FB的相關資訊。

首先,先到Facebook開發者平台這邊註冊一個應用程式
https://developers.facebook.com/



















之後會要求輸入Display Name、Namespace,Display Name是可以輸入中文,然後用來當作標題之類的文字,Namespace就有點像是id,不能跟別人重複的唯一識別碼。














之後會導到該應用程式的管理頁面,會看到應用程式編號應用程式密鑰等資訊






























左方有一個設定,我們必須去裡面新增平台,由於GAE是屬於網頁的應用程式,所以我們要選擇新增網頁,在網站URL那邊打上網域,因為目前我們是在自己的電腦測試,所以網域僅要打上http://127.0.0.1:8888/即可,之後上架到GAE則需要改成http://yourAppname.appspot.com/































之後回到Eclipse開始來建立專案

主要由以下這幾個檔案組成:

FunctionCall.java (將所有的Java功能全部寫在這個裡面,可以做呼叫)
PMF.java (主要是要使用到PersistenceManagerFactory這功能)
store.java (這個檔案有點類似像資料庫中的資料表)
hello.jsp (這頁包含一個簡單的Link,連結到FB)
login.jsp (這要主要是與FB做溝通,來取得相關資料的)

FunctionCall.java

package com.FBconnect.study2fun;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;

import javax.jdo.Query;
import javax.jdo.PersistenceManager;

public class FunctionCall {

 public static void adddata(String name){
  //宣告一個PersistenceManager
  PersistenceManager pm = PMF.get().getPersistenceManager();
  //創建一個資料表物件,並同時將資料寫入
  store db = new store(name);
  try{
   //透過PersistenceManager將資料表放入DataStore
   pm.makePersistent(db);
  }catch(Exception e){
   e.printStackTrace();
  }finally{
   pm.close();
  }
 }
 
 //這邊是一個查詢功能,會資料料庫中的所有FBid的list回傳
 public static List<store> queryFBid(){
  PersistenceManager pm = PMF.get().getPersistenceManager();
  //此處可以做query的動作,有點類似SQL語法
  Query query = pm.newQuery(store.class);
  List<store> result = (List<store>) query.execute();
   return result;
    
  
 } 
 //將FB回傳的網址解析出來
 public static String geturl(String url){
  StringBuilder sb = new StringBuilder();
  try{
   URL graphURL = new URL(url);
   HttpURLConnection conn = (HttpURLConnection)graphURL.openConnection();
   BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
   String line = in.readLine();
  while(line !=null){
   sb.append(line);
   line = in.readLine();
  }
  
  }catch(MalformedURLException e){
   e.printStackTrace();
  }catch(IOException e){
   e.printStackTrace();
  }
  return sb.toString();
 }
}

PMF.java

package com.study2fun.datastore;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;

public class PMF {

 private static final PersistenceManagerFactory pmfInstance = JDOHelper.getPersistenceManagerFactory("transactions-optional");
 private PMF(){}
 public static PersistenceManagerFactory get(){
  return pmfInstance;
 }
}


store.java

package com.FBconnect.study2fun;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

//這個地方有點像一個資料表的意思
@PersistenceCapable(identityType=IdentityType.APPLICATION)
public class store {

 //可以設定主鍵,這邊是讓系統自動產生流水號
 @PrimaryKey
 @Persistent(valueStrategy=IdGeneratorStrategy.IDENTITY)
 Long id;
 
 //各欄位的名稱和屬性
 @Persistent
 String FBid;
  
 //各欄位的名稱和屬性
 //@Persistent
 //int No;
 
 //設定建構子,將值傳進來
 public store(String name){
  super();
  this.FBid = name;
 }
 
 
 public Long get_id(){return id;}
 public String get_FBid(){return FBid;}
 

}




hello.jsp

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<%@page import="com.FBconnect.study2fun.FunctionCall"%>
<%@page import="com.FBconnect.study2fun.store"%>
<%@page import="java.util.List"%>
<%@page import="com.restfb.types.User" %>
<%@page import="com.restfb.DefaultFacebookClient" %>
<%@page import="com.restfb.FacebookClient" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>FBconnect</title>
</head>

<body>
<%
//這邊是一個debug的技巧,雖然程式是在網頁上面跑,但是在Console那邊還是可以檢視到System.out.println的資料,就可以知道程式是否有執行到這邊了
System.out.println("test-show");
//取得授權碼
String code = request.getParameter("code");
//應用程式ID
String client_id = "562850973805812";
//程式密鑰
String secret = "f752230afd606dd0f386246e06fc9b48";
String redirect_uri = "http://127.0.0.1:8888/hello.jsp";
String authURL = "https://graph.facebook.com/oauth/access_token?"+
"client_id="+ client_id+
"&redirect_uri="+redirect_uri+
"&client_secret="+secret+
"&code="+code;
String resp = FunctionCall.geturl(authURL);
String access_token = resp.substring(resp.indexOf("=")+1,resp.lastIndexOf("&"));
FacebookClient fbClient = new DefaultFacebookClient(access_token);
User user = fbClient.fetchObject("me",User.class);
//這邊的話,因為FB的資訊不僅有id而已,還有其他的資訊,生日、Email等等,這些資訊皆可以抓取,雖然有時好像抓不太到~"~
String name = user.getName();
//這邊我們將取得的FBid寫進到我們的資料庫中
FunctionCall.adddata(name);
%>
<!--  下面這邊要直接印出一個變數,可以直接用個=變數即可-->
Hello, <%=name %><p>

<p>資料庫中所有的FBid如下<p>
<%
List<store> result = FunctionCall.queryFBid();
for(store fs : result){
 //如果在Java Code裡面要包含網頁語法的話,就可以使用out.println,用字串的方式輸出網頁語法來呈現
 out.println(fs.get_FBid()+"<p>");
  }
%>



</body>
</html>


login.jsp

<%@ page language="java" contentType="text/html; charset=BIG5"
    pageEncoding="BIG5"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=BIG5">
<title>Insert title here</title>
</head>
<body>
<!-- 使用FB登入的方式便是透過一個FB的連結去取得驗證 -->
<%
String fbloginurl = "https://www.facebook.com/dialog/oauth?"+
"client_id=562850973805812" +
"&redirect_uri=http://127.0.0.1:8888/hello.jsp";
%>
<a href="<%=fbloginurl %>">Facebook登入</a>

</body>
</html>

一開始我們先到登入頁面














之後會跳到FB那邊取得授權



















最後會到達轉址後的頁面(前提是授權要過,接受的意思)























其實像最後一步這邊,可以寫一個簡單的功能去判斷,該使用者是否已經有記錄過登入資訊了,如果已經有記錄過的話就不用再將他寫進資料庫裡面了。

完整專案檔下載:下載


Google App Engine 連接資料庫(GAE Datastore)

Google App Engine中使用的資料庫並不是一般的SQL,而是有點類似SQL的GQL。

底下將會介紹五個不同的檔案,分別是
FunctionCall.java (將所有的Java功能全部寫在這個裡面,可以做呼叫)
PMF.java (主要是要使用到PersistenceManagerFactory這功能)
store.java (這個檔案有點類似像資料庫中的資料表)
WriteDataToDatastore.html (這是我寫的測試網頁,用來post資料給result.jsp)
result.jsp(處理post過來的資料,並且寫入到資料庫中)


FunctionCall.java
package com.study2fun.datastore;

import java.util.List;
import javax.jdo.Query;
import javax.jdo.PersistenceManager;

public class FunctionCall {

 public static void adddata(String name,String num){
  //宣告一個PersistenceManager
  PersistenceManager pm = PMF.get().getPersistenceManager();
  //創建一個資料表物件,並同時將資料寫入
  store db = new store(name,num);
  try{
   //透過PersistenceManager將資料表放入DataStore
   pm.makePersistent(db);
  }catch(Exception e){
   e.printStackTrace();
  }finally{
   pm.close();
  }
 }
 
 //這邊是一個查詢功能,爾後會在連結Facebook那邊寫詳細一點
 public static List<store> sqlfile(String sql){
  PersistenceManager pm = PMF.get().getPersistenceManager();
  //此處可以做query的動作,有點類似SQL語法
  Query query = pm.newQuery(store.class);
  List<store> result = (List<store>) query.execute();
  
  return result;
  /*
   * for(filestore fs : result){
   * System.out.println(fs.???)
   * }
   * 
  */
 } 
}

PMF.java
package com.study2fun.datastore;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;

public class PMF {

 private static final PersistenceManagerFactory pmfInstance = JDOHelper.getPersistenceManagerFactory("transactions-optional");
 private PMF(){}
 public static PersistenceManagerFactory get(){
  return pmfInstance;
 }
}



store.java
package com.study2fun.datastore;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

//這個地方有點像一個資料表的意思
@PersistenceCapable(identityType=IdentityType.APPLICATION)
public class store {

 //可以設定主鍵,這邊是讓系統自動產生流水號
 @PrimaryKey
 @Persistent(valueStrategy=IdGeneratorStrategy.IDENTITY)
 Long id;
 
 //各欄位的名稱和屬性
 @Persistent
 String name;
 
 @Persistent
 String num;
 
 //各欄位的名稱和屬性
 //@Persistent
 //int No;
 
 //設定建構子,將值傳進來
 public store(String name,String num){
  super();
  this.name = name;
  this.num = num;
  
 }
 
 
 public Long get_id(){return id;}
 public String get_name(){return name;}
 public String get_num(){return num;}

}


WriteDataToDatastore.html
<!DOCTYPE html>
<html>
<head>
<meta charset="BIG5">
<title>寫入資料庫測試</title>
</head>
<body>
<!-- 這邊簡單寫一個表單傳送的HTML網頁,並將值post到result.jsp這頁面 -->
<FORM action="result.jsp" method="post">
 名字:<INPUT name="name" size=10 maxlength=20><P>
 學號:<INPUT name="no" size=10 maxlength=20><P>
 
 <INPUT type="submit" value="送出表單">
 <INPUT type="reset" value="重新輸入">
 
</body>
</html>

result.jsp
<%@ page language="java" contentType="text/html; charset=BIG5" pageEncoding="BIG5"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=BIG5">
<title>Test</title>
</head>
<body>
<!-- 這邊是屬於JSP網頁,我們需要把該頁所用到的Java功能import進來 -->
<%@page import="com.study2fun.datastore.FunctionCall"%>

<!-- 在JSP頁面中使用JAVA Code需要使用<%/*...*/%>包起來,如下面 -->
<%
//這邊會接收剛剛post過來的資料,並且要設定好相對應的名稱(如上一頁的的參數name="?")
String name = request.getParameter("name");
String no = request.getParameter("no");
//呼叫Java功能,這邊是做寫入的動作
FunctionCall.adddata(name, no);
%>

<a href="http://127.0.0.1:8888/_ah/admin/datastore">查看本地資料庫</a>


</body>
</html>

執行本地端瀏覽器瀏覽結果
在要瀏覽的網頁檔上按下右鍵>>Run As>>Web Application





























底下的Development Mode那邊會顯示一個網址,可以點擊後瀏覽。
而右上角有一個雙黃色的箭頭「Reload web server」則是當有做任何修改的話,只需存檔後,按下此紐,就會重新載入網頁,無須再Run As......











檢視本地資料庫的方式為,請在網址那邊打上http://127.0.0.1:8888/_ah/admin/可以進入到本機端管理介面,此時我們可以先看到資料庫是沒有資料的。


















之後我們來嘗試新增一筆資料













新增完畢後,我們再到管理介面,並且列出資料




此時會發現剛剛新增的中文部分會顯示亂碼,英文則是正常的顯示,關於這點,目前在本地端不太要緊,之後上架到GAE平台後,可以支援中文的~



























完整專案檔下載:下載