Wicket ve Facebook Kullanımı
Facebook hiç kuÅŸkusuz günümüzün en çok kullanılan sosyal aÄŸlarının başında geliyor. Bence facebook’un bu kadar baÅŸarılı olmasının en büyük sebeplerinden biri kullanıcılarıda sisteme dahil etmeyi baÅŸarmasıdır. Yani geliÅŸtirici olarak sisteme katkıda bulunuyorsunuz. Bir eksik gördüğünüzde ya da olması gereken bir uygulama fikri geldiÄŸinde kendiniz geliÅŸtirip sisteme ekleyebiliyorsunuz.
Ben bu yazıda facebook üzerinde çalışacak bir uygulama yapmayacağım. Benim istediğim, kendi geliştirdiğim wicket uygulamasına kullanıcıların facebook üzeirnden giriş yapabilmeleri ve kayıt olabilmeleri.
Wicket uygulamama facebook kısmını eklerken olabildiÄŸince java API’sinden uzak durmaya çalışacağım. Bana kalırsa facebook java api’si böyle basit istekler için fazlasıyla karmaşık bir kütüphane. Onun yerine facebook developer sitesinde açıklanan http istekleriyle uygulamamı geliÅŸtirmek istiyorum.
İnternette biraz araÅŸtırma yapınca farkettim ki, bunu tek isteyen ben deÄŸilmiÅŸim. BaÅŸka geliÅŸtiricilerde benzer düşüncelerle, facebook java api’sinden olabildiÄŸince bağımsız uygulamalar geliÅŸtirmiÅŸler. TekerleÄŸi tekrardan icat etmek yerine bloglarında yayınladıkları örneklerden faydalanacağım. EÄŸer isterseniz yazının sonundaki Kaynaklar kısmından yararlandığım siteleride görebilirsiniz.
Öncelikle
Öncelikle ÅŸunu belirtmek istiyorum. Bu uygulamayı geliÅŸtirirken, wicket’ta uygulama geliÅŸtirebildiÄŸinizi ve geliÅŸtirdiÄŸiniz uygulamalarda kendi Session yapılarınızı kullanabildiÄŸinizi varsayacağım. EÄŸer bu konuda bilginiz yoksa wicket-stuff sitesinden nasıl yapılacağını öğrenebilirsiniz.
Aynı ÅŸekilde daha önce baÅŸka platformlarda facebook uygulaması geliÅŸtirdiÄŸinizi varsayıyorum. Ama yinede hiç bilginiz yoksa, öncelikle Facebook Developers sitesine kayıt oluyorsunuz. Siteye kayıt olduktan sonra kendinize bir uygulama yaratıyorsunuz. Uygulama yaratırken uygulamanın internet adresi kısmına “http://localhost:8080/WicketFacebookExample/” tarzı bir site ismi giriyorsunuz. Burda WicketFacebookExample benim bu gün size anlatacağım projemin adı. Geri kalanı ise adres bilgilerini içeriyor. Bu adres bilgisi gerçekten önemli. Çünkü uygulamamızın içerisinde, biz facebook’a login isteÄŸi gönderdiÄŸimizde, içerisine geri döneceÄŸi adres bilgisinide veriyoruz. EÄŸer verdiÄŸiniz adres bilgisi ile, uygulamayı yaratırken verdiÄŸiniz adresler benzer deÄŸilse facebook size izin vermiyor.Uygulamanızı yarattığınızda size bir API_KEY ve API_SECRET verilecek. Bu bilgileri birazdan anlatacağım sınıfta kullanacağız.
Şimdi ise facebook işlemlerimizin tamamında kullanacağımız Facebook sınıfımızı oluşturalım.
Facebook.java
package blg.bhdrkn.facebook.domain; import com.google.code.facebookapi.FacebookException; import com.google.code.facebookapi.FacebookJsonRestClient; import com.visural.common.StringUtil; public class Facebook { private static final String api_key = "[API_KEY]"; private static final String secret = "[SECRET]"; private static final String client_id = "[API_KEY_?]"; private static final String redirect_uri = "http://localhost:8080/WicketFacebookExample/fbauth"; private static final String[] perms = new String[] {"publish_stream", "email", "offline_access"}; public static String getAPIKey() { return api_key; } public static String getSecret() { return secret; } public static String getLoginRedirectURL() { return "https://graph.facebook.com/oauth/authorize?client_id=" + client_id + "&display=page&redirect_uri=" + redirect_uri+"&scope="+StringUtil.delimitObjectsToString(",", perms); } public static String getAuthURL(String authCode) { return "https://graph.facebook.com/oauth/access_token?client_id=" + client_id+"&redirect_uri=" + redirect_uri+"&client_secret="+secret+"&code="+authCode; } public static void sendToWall(String message, long userId){ FacebookJsonRestClient client = new FacebookJsonRestClient(Facebook.getAPIKey(), Facebook.getSecret()); try { client.stream_publish(message, null, null, null, userId); } catch (NumberFormatException e) { e.printStackTrace(); } catch (FacebookException e) { System.out.println("FACEBOOK EX: M: " + e.getMessage() + " C: " + e.getCause()); e.printStackTrace(); } } }
Bu sınıf ile ilgili olarak ilk dikkat etmeniz gereken kısım, API_KEY, SECRET ve CLIENT_IDkısımlarını kendinizinkiyle deÄŸiÅŸtirmeniz gerekiyor. Client id’nin tam olarak ne olduÄŸunu bilmediÄŸimden ben API_KEY kullandım ve baÅŸarılı sonuç aldım. Bir diÄŸer dikkat etmeniz gereken kısım, facebook java api’yi sadece, giriÅŸ iÅŸlemini yaptıktan sonra kullanacak olmam. Son olarak dikkat etmeniz gereken yer redirect adresim, “http://localhost:8080/WicketFacebookExample/fbauth“. Farkettiyseniz normal site adresimden farklı. Sonundaki fbauth ileride yazacağım filtre için gerekiyor. Tam olarak ne iÅŸe yarayacağını kafa karışıklığına sebep olmamak için anlatmayacağım.
Login Komutunun Gönderilmesi
Şimdi bundan sonra bir link üzerinden, getLoginRedirectURL() metoduyla istek göndermemiz gerekiyor. Ben bunu LoginPage.java sayfasının içerisine aşağıdaki linki ekleyerek giderdim. Siz isterseniz başka yöntemlerlede bu adrese istek gönderebilirsiniz.
LoginPage.java
package blg.bhdrkn.facebook.pages; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.markup.html.link.Link; import blg.bhdrkn.facebook.domain.Facebook; public class LoginPage extends WebPage{ public LoginPage() { add(new Link<String>("myLink") { @Override protected CharSequence getURL() { return Facebook.getLoginRedirectURL(); } @Override public void onClick() { System.out.println(getRequest().getUrl().toString()); } }); } }
Bu basit bir web sayfası. Uygulamam açıldıktan sonra bu sayfaya yönleniyor. Farkettiyeseniz link direk olarak benim getLoginRedirectURL() metodumun sonucuna yöneleniyor. Bu adrese yapacağımız isteÄŸin cevabı ise ileride bir filtreden geçecek. Geçeren gelen isteÄŸi inceleyeceÄŸiz gereken kısımlarını alıp yeni bir istek göndereceÄŸiz. Yani kullanıcı bir istek yapmış olacak fakat biz facebook’a senkron olarak iki istek göndereceÄŸiz.
Filtre
Åžimdi gelen isteÄŸin nasıl filitreleneceÄŸine bakalım. Bunun için bir Servlet yazacağız. Yazacağımız bu servlet ise /fbauth uzantılı istekleri filitreleyecek. Bu filtrelemenin amacı gelen isteÄŸi kullanarak ikinci bir istek üretip, facebook’tan asıl ihtiyacımız olan access_token bilgisini almak.
- Önce getRedirectURL() üzerinden ilk isteğimizi göndereceğiz. Gerekirse kullanıcı burada facebook girişi yapacak. İstediğimiz izinleri verecek.
- Kullanıcı işlemleri bittiğinde, facebook, sayfayı, bizim gönderdiğimiz isteğin içinde bulunan redirect_page adresine yönlendirecek. Ki o adreste bizim uygulamamız için http://localhost:8080/WicketFacebookExample/fbauth adresi olacak.
- Filitremiz /fbauth uzantılı istekleri filtreleyeceğinden. Bu istek doğruca filtremize gidecek. Filtremiz içerisinde, gelen cevabı kullanarak yeni bir istek oluşturacağız.
- İkinci istek sonucunda erişmek istediğimiz access_token bilgisine ulaşmış olacağız.
Filtre yaratmanın iki adımı var. İlk önce filtre sınıfımızı yazacağız. Sonra bu sınıfın neyi filtreleyeceğini web.xml içerisinde belirteceğiz.
FacebookAuthentication.java
package blg.bhdrkn.facebook.filter; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.wicket.Session; import blg.bhdrkn.facebook.application.LoginSession; import blg.bhdrkn.facebook.domain.Facebook; import com.visural.common.StringUtil; public class FacebookAuthentication implements Filter{ private static final long serialVersionUID = -460291618325615633L; public void doFilter(ServletRequest sr, ServletResponse sr1, FilterChain fc) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) sr; HttpServletResponse res = (HttpServletResponse) sr1; String code = sr.getParameter("code"); if (StringUtil.isNotBlankStr(code)) { String authURL = Facebook.getAuthURL(code); URL url = new URL(authURL); try { String result = readURL(url); String accessToken = null; Integer expires = null; String[] pairs = result.split("&"); for (String pair : pairs) { String[] kv = pair.split("="); if (kv.length != 2) { throw new RuntimeException("Unexpected auth response"); } else { if (kv[0].equals("access_token")) { accessToken = kv[1]; } if (kv[0].equals("expires")) { expires = Integer.valueOf(kv[1]); } } } if (accessToken != null){ LoginSession session = (LoginSession)Session.get(); if(session.authenticateFacebookLogin(accessToken)) res.sendRedirect("http://localhost:8080/WicketFacebookExample/SignedIn"); else res.sendRedirect("http://localhost:8080/WicketFacebookExample/"); } else { System.out.println("ACCESSTOKEN: " + accessToken + " EXPIRES: " + expires); throw new RuntimeException( "Access token and expires not found"); } } catch (IOException e) { throw new RuntimeException(e); } } } private String readURL(URL url) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); InputStream is = url.openStream(); int r; while ((r = is.read()) != -1) { baos.write(r); } return new String(baos.toByteArray()); } public void destroy() { } @Override public void init(FilterConfig arg0) throws ServletException { // TODO Auto-generated method stub } }
Ve web.xml dosyamızın son hali aşağıdaki gibi olmalı.
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>Wicket Facebook Application</display-name> <filter> <filter-name>Application</filter-name> <filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class> <init-param> <param-name>applicationClassName</param-name> <param-value>blg.bhdrkn.facebook.application.FacebookApplication</param-value> </init-param> </filter> <filter> <filter-name>FBOAuth</filter-name> <filter-class>blg.bhdrkn.facebook.filter.FacebookAuthentication</filter-class> </filter> <filter-mapping> <filter-name>Application</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>FBOAuth</filter-name> <url-pattern>/fbauth</url-pattern> </filter-mapping> </web-app>
Tek eksik olan kısım LoginSession içerisindeki siteye giriÅŸ yaptıracak olan metod. LoginSession’ıda verip durumu sonlandırıyorum.
LoginSession.java
package blg.bhdrkn.facebook.application; import java.net.URL; import org.apache.wicket.protocol.http.WebSession; import org.apache.wicket.request.Request; import org.json.JSONObject; import blg.bhdrkn.facebook.domain.User; import com.visural.common.IOUtil; public class LoginSession extends WebSession { private static final long serialVersionUID = 1L; private User user; public LoginSession(Request request) { super(request); } public boolean isSignedIn() { return user != null; } public User getUser() { return user; } public boolean authenticateFacebookLogin(String accessToken) { try{ if(user == null){ JSONObject resp = new JSONObject( IOUtil.urlToString(new URL("https://graph.facebook.com/me?access_token=" + accessToken))); if(resp == null){ return false; } user = new User(); System.out.println(resp.getString("id")); user.setFirstName(resp.getString("first_name")); user.setLastName(resp.getString("last_name")); user.setId(Long.valueOf(resp.getString("id"))); user.setEmail(resp.getString("email")); } }catch(Exception e){ return false; } return true; } }
Burada kullanılan User sınıfı, bildiğiniz sırada bir POJO, dört tane alanı var içerisinde. Filtremizden anlayacağınız üzere, eğer kullanıcının giriş işlemi başarılıysa yani access_token bilgisini alabildiysek SignInPage.java isimli başka bir sayfaya yönlenecek. Eğer başarılı değilse tekrardan ana sayfaya dönüş yapacak.
Alınan Bilgilerin Yorumlanması
Åžimdi ise SignInPage.java sınıfımıza bakalım. Bu sınıfta giriÅŸ yapanan kullanıcı bilgilerini ekrana basacağız. Hatta bir link koyup kullanıcıya hali hazırda bulunan bir mesajı duvarlarında yayınlamalarını saÄŸlayacağız. Aslında bunu yapacak metodu Facebook.java sınıfımızda yazmıştık. Burada yapacağımız tek ÅŸey kullanıcının facebook id’sini kullanıp bu metodu çağırmak olacak.
SignInPage.java
package blg.bhdrkn.facebook.pages; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.link.Link; import blg.bhdrkn.facebook.application.LoginSession; import blg.bhdrkn.facebook.domain.Facebook; import blg.bhdrkn.facebook.domain.User; public class SignedInPage extends WebPage implements AuthenticatedWebPage { private static final long serialVersionUID = -5476595548698214837L; private User signedInUser; private static final String Message = "Facebook Test Message..."; public SignedInPage() { publishUserInformation(); createLogOutLink(); createSendMessagetoFacebookWallLink(); } private void createSendMessagetoFacebookWallLink() { add(new Link<String>("send") { @Override public void onClick() { Facebook.sendToWall(Message, signedInUser.getId()); } }); } private void createLogOutLink() { add(new Link<String>("logout") { @Override public void onClick() { getSession().invalidate(); setResponsePage(LoginPage.class); } }); } private void publishUserInformation() { signedInUser = ((LoginSession) getSession()).getUser(); add(new Label("userId", String.valueOf(signedInUser.getId()))); add(new Label("firstName", signedInUser.getFirstName())); add(new Label("lastName", signedInUser.getLastName())); add(new Label("email", signedInUser.getEmail())); } }
Ve aynı sayfanın markup kısmı…
SignInPage.html
<!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=windows-1254"> <title>Sign In Page</title> </head> <body> <fieldset> <legend>Facebook User Information</legend> FACEBOOK ID: <span wicket:id="userId"></span><br /> FIRST NAME: <span wicket:id="firstName"></span><br /> LAST NAME: <span wicket:id="lastName"></span><br /> EMAIL: <span wicket:id="email"></span><br /> </fieldset> <br /> <br /> <br /> <fieldset> <legend>Operations</legend> <a wicket:id="logout">LOG OUT</a> <br /> <a wicket:id="send">SEND TEST MESSAGE</a><br /> </fieldset> </body> </html>
Eğer bilgileriniz doğru bir şekilde geldiyse ve kullanıcının duvarına yazabiliyorsanız başardınız demektir.
Kaynaklar
- Facebook Developers, Facebook İzin Bilgileri
- Wicket Authentication
- Facebook Kısmında Büyük Ölçüde Yararlandığım Yer – Richard Nichols
- Kullandığım Ek “Tool Belt”
GitHub Adres Bilgileri
Projenin tamamına aşığadaki GitHub Adresinden ulaşabilirsiniz.
End Of Line