Harmonic Development

RSS
Dec 2

Adding Spring Social to a Spring MVC and Spring Security Web App, Part 3

So far we have implemented and configured the classes for persisting social connection information for users, and tied Spring Social’s Web support in to our sign in and sign up processes in Spring MVC and Spring Security. We have a few last changes to make to our Spring MVC, Java web app, and Spring Security configuration. Then we’ll actually make all this awesome new functionality available in the views of our application. Then we’ll be done! Suddenly way more people will be using your Web application because they can sign in quickly and easily using their Facebook and Twitter accounts. And your app can get social by acting on behalf of your users on their social networks. And best of all it will be trivial for you to add support for other social network providers (e.g. LinkedIn, GitHub, Foursquare, etc.).

Spring MVC Configuration Changes

You will need to modify your Spring MVC configuration to add the two controllers provided by Spring Social Web, ProviderSignInController and ConnectController. We’ve already discussed ProviderSignInController. This controller handles the requests when users sign in to your app using Facebook/Twitter/etc. The ConnectController allows current users to associate their Facebook/Twitter accounts with their accounts in your application, so that they can then also sign in to your application using Facebook/Twitter.

Here is some example XML for configuring these controllers. You could also configure them in a @Configuration class.

  <bean class=”org.springframework.social.connect.web.ConnectController”>
    <!— relies on by-type autowiring for the constructor-args —>
    <property name=”applicationUrl” value=”${site.url}” />
  </bean>

  <bean class=”org.springframework.social.connect.web.ProviderSignInController”>
    <!— relies on by-type autowiring for the constructor-args —>
    <property name=”applicationUrl” value=”${site.url}” />
    <property name=”signUpUrl” value=”/register” />
    <property name=”signInUrl” value=”/login” />
  </bean>

I recommend that you make the “applicationUrl” value a configurable property, so that you can use “http://localhost:8080/context” in development, and your application’s real URL in production. Note that Facebook will not redirect users to “localhost”, so as far as I could figure it’s not possible to test Facebook integration on localhost. (If it is possible, feel free to leave details in a comment.) You will also want to provide your application’s sign up and sign in URLs (mine are “/register” and “/login”, as you can see above).

You must also declare your SignInAdapter implementation bean. This should be straightforward as you should be able to have all their dependencies provided by autowiring.

web.xml Changes

You may need to change your application’s web.xml file. If you configure your Spring context using XML and you list your configuration XML files in your web.xml file using the “contextConfigLocation” context-param, you will need to add your social configuration XML file to the list. If you plan on allowing your users to remove associations between their Facebook/Twitter accounts and their accounts in their applications, you will also need to add a Spring filter, HiddenHttpMethodFilter. This filter enables support for HTTP methods beyond just GET and POST from Web browsers, which do not ordinarily support other HTTP methods. Other methods are simulated by providing a hidden input with the other method (e.g. DELETE) as the value. HiddenHttpMethodFilter will modify incoming requests so that your controller methods are invoked as if the browser used the correct HTTP method. Here is how you would declare that filter:

  <filter>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <url-pattern>/connect/*</url-pattern>
  </filter-mapping>

You only need to apply it to the “/connect/*” url-pattern as it only applies to the ConnectController, which handles requests to that url-pattern.

Spring Security Configuration Changes

If you are planning to allow your existing users to add and remove associations between their Facebook/Twitter accounts and their accounts in your application, you will want to make sure to secure requests to the “/connect/*” path so that they can only be made by logged in users. For example:

    <intercept-url pattern=”/connect/**” access=”IS_AUTHENTICATED_REMEMBERED”/>

Sign In With … View Changes

For your users to sign in with Facebook/Twitter, you will need to provide forms that POST to URLs of the format “/signin/(provider name)”, e.g. “/signin/facebook” and “/signin/twitter”, for Facebook and Twitter respectively.

For Facebook signin, you will also need to add a hidden input named “scope” in which you provide a comma separated list of the Facebook permissions your application will need. You will almost certainly want to use the “offline_access” permission, which instructs Facebook to give your application an access token that will not expire. Other useful permissions include “publish_stream” (for posting to a user’s wall) and “manage_pages” (for managing the pages to which a user has admin access).

Here’s an example Facebook sign in form:

<form id=”fb_signin” action=”<c:url value=”/signin/facebook”/>” method=”POST”><input
                  type=”hidden” name=”scope” value=”publish_stream,offline_access”><a
                  href=”javascript:document.forms.fb_signin.submit()” title=”Log In With Facebook”><img
                  src=”<c:url value=”/img/facebook.gif”/>” width=”14” height=”14”/></a></form>

Showing Users Their Social Account Connection Status

You may want to show your users when they have connected one of their social accounts to their account in your application. To do this for a user, you will want to use your UsersConnectionRepository implementation to create a ConnectionRepository for that user. Then you can retrieve a user’s connections and populate model data for display in a view. Here is some example code for adding “twitterConnected” and “facebookConnected” booleans to the model that can be used to show a user whether or not they have connected their Twitter and Facebook accounts.

    ConnectionRepository connectionRepository = socialUserService.createConnectionRepository(user.getUsername());
    List connections = connectionRepository.findConnections(“twitter”);
    if (!connections.isEmpty()) {
      model.addAttribute(“twitterConnected”, true);
    }
    connections = connectionRepository.findConnections(“facebook”);
    if (!connections.isEmpty()) {
      model.addAttribute(“facebookConnected”, true);
    }

Enabling Existing Users to Connect/Disconnect Social Accounts

If you’re showing your users their social account connection status, you may also allow them to connect and disconnect their social accounts too. That way existing users of your application can then sign in using their social accounts, and your application can act on their behalf on their social networks.

This is done by using a form that makes a POST to “/connect/(provider id)”, e.g. “/connect/facebook” and “/connect/twitter”. In the case of connecting an account, you would also potentially need to include the hidden “scope” input mentioned previously, where you would list permissions that your application wants (e.g. “offline_access” for Facebook). In the case of disconnecting an account, you would need to add the hidden “_method” input with a value of “delete”. This will be picked up by the HiddenHttpMethodFilter mentioned earlier so that the POST request actually gets handled by a controller method expecting a DELETE request. Here are some example forms:

Connect:

<form id=”fb_signin” action=”<c:url value=”/connect/facebook”/>” method=”POST”>
<input type=”hidden” name=”scope” value=”publish_stream,offline_access”>
<input type=”submit” name=”submitBtn” value=”Connect”>
</form>

Disconnect:

<form id=”fb_signin” action=”<c:url value=”/connect/facebook”/>” method=”POST”>
<input type=”hidden” name=”_method” value=”delete”>
<input type=”submit” name=”submitBtn” value=”Disconnect”>
</form>

When users submit these forms, the ConnectController will send them to views named “connect/(provider id)Connected” when connecting an account and “connect/(provider id)Connect” when disconnecting an account. You will need to create these two views for every provider your app supports.

OMG, You’re Done!!!

Whew! It took a long way to get here, but you are finally done adding Spring Social to your Spring MVC and Spring Security Web app. Awesome!

The first time you go through this process, it can be a bit overwhelming, but here are two great things about using Spring Social: 1) Once you’ve done this process in one app, you can just repeat it to add social features to other apps, and it gets much easier every time you do it. 2) You can add support for new social providers very quickly and easily, if there are Spring Social implementations for that provider. The list of supported providers is already pretty long and it’s growing all the time thanks to an active community. Another great aspect of Spring Social is how makes it relatively straightforward to write code supporting a new provider. I myself wrote a Spring Social Tumblr implementation without too much difficulty (other than dealing with some non-standard aspects of Tumblr’s API). Maybe I will describe that process in a future post.

Thanks for reading along. Good luck and good coding!