Как обновить статус в Facebook, LinkedIn & Twitter при помощи spring-social

    Не так давно я уже писал о новом проекте SpringSource: spring-social. Сегодня я хочу рассказать (вернее показать на примере) как можно данную библиотеку использовать. В качестве примера используется простейшее приложение, которое позволяет залогиниться во все три сервиса и обновлять статус одновременно во всех трех (осторожно — под катом много букв и кода и совсем нет картинок).
    imageimageimageimage

    Обновление статуса было взято в качестве примера потому что:
    * это просто и понятно;
    * эта функция не поддерживается spring-social для LinkedIn-а, то есть можно посмотреть как добавлять реализацию APIшых методов сервисов, не реализованных в spring-social по умолчанию.
    В принципе на месте обновления статуса может быть любая другая функциональность.

    Сами разработчики советуют смотреть в качестве примера использования библиотеки их приложение GreenHouse — но, приложение достаточно большое и вычленить имено «социальную» часть не так легко. Было так же опубликовано пара примеров — но достаточно примитивных — не дающих «полной картины». Потому я и решил написать небольшой пример (самому разобраться и другим показать) который бы с одной стороны представлял с собой некоторое законченный пример от логина в сервис до отправки статуса, а с другой стороны был бы максимально простым. Потому — извините что html голый без каких либо стилей — плюс java-код не везде «красив» — в некоторых местах код можно было бы сделать «правильней» например путем наследования, но это бы «зымылило» бы пример — пришлось бы разбираться еще с дополнительной иерархией классов.

    Данный пост является вольным переводом моего англоязычного поста.
    Все исходники доступны в svn: http://svn.emforge.net/svnroot/akakunin-experiments/update-status
    Исходники так же можно просмотреть на сайте EmForge

    Базовое приложение


    В качестве «базы» мы используем простейшее приложение на spring framework и spring-mvc. В данном приложении используется стандартный джентльменский набор:
    * Maven для сборки;
    * Spring Framework 3.0.5;
    * Spring-Security 3.0.5;
    * JPA (через Hibernate);
    * HSQL в качестве базы.

    В принципе все максимально стандартно и просто. Есть единственная Entity — net.emforge.updatestatus.entity.User — которая хранит пользователей (userName, password). Для каждого пользователя так же сохраняются токены, которые используются для работы с каждым из сервисов.
    Есть DAO-класс UserDao, и сервис UserServiceImpl.
    В приложении целых две странички — главная (index) с формой для логина и регистрации (обрабатывается UserController-ом), и страничка status доступная только залогиненым пользователям — о ней мы поговорим позже.

    Все вместе это представляет собой простейшее приложение где пользователь может зарегистрироваться или залогиниться, и на страничке status законнектиться с одним из сервисов и обновить статус.

    Регистрация приложения


    Что бы данный пример работал, вам будет необходимо сначала зарегистрировать свое приложение в тех сервисах, которые вы собираетесь использовать:
    * Регистрация на Facebook-е осуществляется на странице http://developers.facebook.com/setup/ — только просьба использовать в качестве SiteURL localhost:8080/update-status/ — иначе callback работать не будет;
    * Регистрация в Twitter: http://dev.twitter.com/apps/new;
    * Регистрация в LinkedIn: https://www.linkedin.com/secure/developer

    В каждом случае вы получите два ключа — ключ приложения (или id приложения) & security-token — их надо сохранить в src/main/resources/config.properties

    Запустить по быстрому


    Если вы хотите по быстрому посмотреть как это работает и у вас есть java & maven (я надеюсь что это так :) ) — то просто возьмите исходники из svn и выполните (после того как пропишите свои ключи в config.properties)
    mvn tomcat:run

    После чего приложение должно быть доступно по адресу http://localhost:8080/update-status
    Создайте нового пользователя — и вперед, а мы посмотрим как же это все реализовано

    Коннект в LinkedIn и Twitter


    Для работы с обоими сервисами используется OAuth и процедура соединения аналогична — так что покажу на примере twitter-а.
    Для работы с сервисом пишем простейший класс TwitterProvider главный метод в котором — getOAuthService:
    public OAuthService getOAuthService() {
      OAuthConfig config = new OAuthConfig();
        config.setRequestTokenEndpoint("https://api.twitter.com/oauth/request_token");
        config.setAccessTokenEndpoint("https://api.twitter.com/oauth/access_token");
        config.setAccessTokenVerb(Verb.POST);
        config.setRequestTokenVerb(Verb.POST);
        config.setApiKey(apiKey);
        config.setApiSecret(apiSecret);
        config.setCallback(callbackUrl);
      
        return new OAuth10aServiceImpl(
            new HMACSha1SignatureService(),
            new TimestampServiceImpl(),
            new BaseStringExtractorImpl(),
            new HeaderExtractorImpl(),
            new TokenExtractorImpl(),
            new TokenExtractorImpl(),
            config);
      }


    apiKey, apiSecret & callbackUrl инжектятся из config.properties (куда мы из сначала прописываем).

    Для коннекта добавляем ссылку /connet/twitter и вешаем обработчик (в SocialController):
      @RequestMapping(value = "/connect/twitter", method = RequestMethod.GET)
      public String requestConnectionToTwitter(WebRequest request) {
        // get request token
        Token requestToken = twitterProvider.getOAuthService().getRequestToken();
        // store request token in session
        request.setAttribute("twitter_request_token", requestToken, WebRequest.SCOPE_SESSION);
       
        return "redirect:" + twitterProvider.getAuthorizeUrl() + "?oauth_token=" + requestToken.getToken();
      }



    По сути дела данный обработчик делает редирект на Twitter для прохождения логина в нем. В случае успешного логина Twitter сделает редирект на callbackUrl и передаст туда ключи пользователя. Повесим в SocialController обработчик для callBack-а:
      /** Callback from twitter on success login
       *
       * @param verifier
       * @param request
       * @return
       */
      @RequestMapping(value = "/callback/twitter", method = RequestMethod.GET, params = "oauth_token")
      public String authorizeTwitterCallback(@RequestParam(value = "oauth_verifier", defaultValue = "verifier") String verifier,
                      WebRequest request) {
        // get request token from session
        Token requestToken = (Token)request.getAttribute("twitter_request_token", WebRequest.SCOPE_SESSION);
       
        // get access token
        Token accessToken = twitterProvider.getOAuthService().getAccessToken(requestToken, new Verifier(verifier));
        String userName = getCurrentUser().getName();
        userService.updateTwitterAuthentication(userName, accessToken.getToken(), accessToken.getSecret());
       
        return "redirect:/status";
      
      }

    В этом обработчике мы получаем токены и сохраняем их для пользователя для дальнейшего использования.

    Коннект в Facebook


    В Facebook-ом все немного хитрее. Для того что бы законнектиться в Facebook мы размещаем на нашу страничку status.jsp кнопку «Connect to Facebook» используя следующий код:
    <form id="fb_signin" action="<c:url value="/connect/facebook" />" method="post">
      <div class="formInfo">
      </div>
      <div id="fb-root"></div>  
      <p><fb:login-button perms="email,publish_stream,offline_access" onlogin="$('#fb_signin').submit();" v="2" length="long">Connect to Facebook</fb:login-button></p>
    </form>

    <facebook:init />


    Тут важно обратить внимание на следующие моменты:
    * facebook:init тег реализован в библиотеке spring-social и ему требуется, что бы в спринговом контексте был бин ${facebookProvider} — у нас он реализован в классе FacebookProvider. По сути дела из этого бина тег берет только ключи;
    * fb:login-button генерит кнопку «Connect to Facebook»;
    * form-action /connect/facebook будет использован в качестве callback-а в случае успешного выполнения логина.

    Так же для того что бы этот код заработал необходимо подключить tagLib
    <%@ taglib uri="http://www.springframework.org/spring-social/facebook/tags" prefix="facebook" %>

    и подключить jQuery — в моем случае я просто сделал



    Ну и надо написать обработчик callback-а, который придет на /connect/facebook:
      @RequestMapping(value="/connect/facebook", method=RequestMethod.POST)
      public String connectAccountToFacebook(@FacebookAccessToken String accessToken,
                          @FacebookUserId String facebookUserId) {
        if (facebookUserId != null && accessToken != null) {
          // store facebook information
          String userName = getCurrentUser().getName();
          userService.updateFacebookAuthentication(userName, accessToken, facebookUserId);     
        }
        return "redirect:/status";
      }

    * This source code was highlighted with Source Code Highlighter.


    В данном примере мы увидим две интересные аннотации: @FacebookAccessToken и @FacebookUserId — это одна из фичек spring-social, но что бы она работала, нам необходимо добавить в проект специальный WebArgResolver. Для этого добавляем в applicationContext.xml:
        <bean id="facebookWebArgResolver" class="org.springframework.social.facebook.FacebookWebArgumentResolver">
          <constructor-arg name="apiKey" value="${facebook.appId}"/>
        </bean>

    * This source code was highlighted with Source Code Highlighter.

    Обработка callback-а аналогична Tiwtter & Linkedin — сохраняем ключи (в нашем случае facebookUserId & facebookToken) для дальнейшего использования.

    Отправка статуса в Twitter & Facebook


    Законнектиться-законнектились, теперь можно и статус обновлять. На страничке статус у нас есть textArea где пользователь вводит текст и отправляет сообщение. Обработчик формы выглядит следующим образом:
      @RequestMapping(value = "/status", method = RequestMethod.POST)
      public String sendStatus(@Valid StatusForm statusForm, BindingResult result, ModelMap modelMap) {
        User user = getCurrentUser();
       
        LinkedInTemplateExt linkedInTemplate = linkedInProvider.createTemplate(user);
        FacebookTemplate facebookTemplate = facebookProvider.createTemplate(user);
        TwitterTemplate twitterTemplate = twitterProvider.createTemplate(user);
       
        // send message to LinkedIn
        if (linkedInTemplate != null) {
          linkedInTemplate.updateStatus(statusForm.getStatus());
        }
       
        // send message to Facebook
        if (facebookTemplate != null) {
          facebookTemplate.updateStatus(statusForm.getStatus());
        }
       
        // send message to Twitter
        if (twitterTemplate != null) {
          twitterTemplate.updateStatus(statusForm.getStatus());
        }
        return "redirect:/status";
      }

    * This source code was highlighted with Source Code Highlighter.

    В данном обработчике мы получаем FacebookTemplate & TwitterTemplate (оба класса из spring-social) используя ключи текущего пользователя и вызываем их метод updateStatus

    Отправка статуса в LinkedIn


    С LinkedIn-ом получилось немного сложней — тот LinkedInTemplate, которые реализован в spring-social не содержит метода для обновления статуса — так что нам придется написать его самим (используя REST-API вызов api.linkedin.com/v1/people/~/person-activities). Для этого мы пишем класс LinkedInTemplateExt — наследник «оригинального» LinkedInTemplate в котором:
    * Инициализируем собственный объект класса RestOperations — при этом используется специальный OAuth1RequestSignerFactory.getRequestSigner для «правильного» кодирования вызовов (еще одна фича spring-social);
    * Реализуем сам метод updateStatus:
      public void updateStatus(String message) {
        LinkedInPersonActivity personActivity = new LinkedInPersonActivity(message);
       
        restOperationsExt.postForLocation("http://api.linkedin.com/v1/people/~/person-activities", personActivity);
      }

    * This source code was highlighted with Source Code Highlighter.


    нам так же потребуется написать класс LinkedInPersonActivity — для передачи статуса:
    @XmlRootElement(name = "activity")
    public class LinkedInPersonActivity {
      public LinkedInPersonActivity() {
      }
      public LinkedInPersonActivity(String body) {
        this.body = body;
      }
     
      @XmlElement(name = "content-type")
      String contentType = "linkedin-html";
     
      @XmlElement(name = "body")
      String body;
    }

    * This source code was highlighted with Source Code Highlighter.


    Все — метод готов — как видно — не смотря на то что LinkedInTemplate изначально поддерживает очень мало функций из API — добавление новых функций не такая уж и сложная задача.

    Вот вроде и все — надеюсь этот пример окажется полезным.
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 7

      0
      Одна из вещей, которая меня бесит — читать одни и те же статусы сначала в твиттере, потом на фейсбуке, потом еще где-нить… косспостинг — зло.

      Хочу агрегатор, который умеет выпиливать одинаковые статусы.
        0
        ну — посылка статуса была выбрана как простейший пример. Хотелось просто показать как коннектиться к сервисам и использовать Template-ы предоставляемые spring-social.
        А как использовать — это уже дело вкуса.
          +1
          fb довольно давно поддерживает красивый чистый простой oauth2, и не надо этих js мерзостей
            –2
            уже давно настроил статус в контакте-> твиттер -> статус в фейсе
            причем посредствам встроенных возможностей
              0
              Написать такое же для вконтакте не хотите?
                0
                вконтакте не поддерживается spring-social — цель данного примера была именно в демонстрации того что умеет spring-social.
                А так — да — хотелось бы иметь какой-нибудь пример работы с контактом из java приложения
                  0
                  " такое же" — это написать темплейты для вконтакта, а не такую же статью для вконтакта

                Only users with full accounts can post comments. Log in, please.