Using Spring social to connect your online profiles
Some time a go I wrote an item on my personal blog about a sample that I created with the spring social project. I explained how to connect to linkedin. In this blog post I want to go one step further. I want to create an application that a user need to log in to. With that application I want to gather events from different online communities that are provided by spring-social out of the box. I will also provide some notes on upgrading my sample from Spring social M1 to Spring social M2.
Upgrading the sample was some work. But in the end it was all about making things easier. So it was a good upgrade. In short I had to change some dependencies. Moved to spring core 3.1 M1, had to introduce spring security of which I also took the 3.1 RC1 version. Next to these upgrades I had to add some dependencies. I had to add the spring-social modules for linkedin, twitter and web.
Source code
The sources are hosted on github: https://github.com/jettro/SpringOAuthLinkedinIntegrationSample
What is removed?
In the first version of the sample I had to create a ConnectController with methods that handled the OAuth calls. With the M2 release this has all become a lot easier. Now we have a ConnectController provided by the spring-social-web module that does all this OAuth handling for you. The ConnectController uses a ServiceProvider based on the provider id. The provider id is taken from the url of the request. As an example: /connect/linkedin connects to linked in. You did not see that one coming did you? Before we move on to the service providers I want to mention one last thing. The ConnectController wants a application.url paramter to provide as a callback parameter.
Connecting to service providers
Service providers like the ones linkedin and twitter provide an api to connect to and read data from. Sometimes you can also provide data. The key component for this kind of interactions is the ServiceProvider
class. All concrete implementations of this ServiceProvider extend the OAuth1 or the OAuth2 abstract super class. I do not want to go into to much details. Please refer to the reference manual for more details.
Usually a connection requires an application secret key and public key. That enables the application to interact with the service provider. When your visitors want to use the application to connect to their profile they have to provide you access. After the authentication process you obtain a key that you can keep for the next session. Storing these keys is also provided by the ConnectionRepository, more on this later on.
You can use the java configuration of spring, or the xml configuration. For now I prefer the xml. The following code block shows the configuration of one of these service providers.
<bean class="org.springframework.social.twitter.connect.TwitterServiceProvider"> <constructor-arg value="${twitter.consumerKey}" /> <constructor-arg value="${twitter.consumerSecret}" /> <constructor-arg ref="connectionRepository" /> </bean>
As you can see from the sourcecode, we provide the consumer key and the consumer secret. Next to these two parameters we also provide the ConnectionRepository. These parameters are usually obtained through a developer page on the site the service provider is connecting to. Examples are http://dev.twitter.com/ and http://developer.linkedin.com/community/apis.
If you want to have a better understanding of the OAuth connections that are going on, have a look at my other blogpost.
Connection Repository
In my previous version of the sample, you had to connect every time again to the service provider. The actual keys that you need to connect were not store. With the M2 release of spring social this has become so easy, it would be silly not to use it. You need a datasource, can use a password encoder and you need the ConnectionRepository. The following code block shows the xml configuration.
<bean id="connectionRepository" class="org.springframework.social.connect.jdbc.JdbcConnectionRepository"> <constructor-arg ref="dataSource"/> <constructor-arg ref="textEncryptor"/> </bean> <bean id="textEncryptor" class="org.springframework.security.crypto.encrypt.Encryptors" factory-method="noOpText"/> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean>
Before you can actually use the jdbc connection repository you need a database with a schema with a Connections table. The sql query is provided in the source code of spring-social. I wanted to use mysql, therefore I have created the following create statement.
Database schema
CREATE TABLE `Connection` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `accountId` varchar(255) NOT NULL DEFAULT '', `providerId` varchar(255) NOT NULL DEFAULT '', `accessToken` varchar(255) NOT NULL DEFAULT '', `secret` varchar(255) DEFAULT NULL, `refreshToken` varchar(255) DEFAULT NULL, `providerAccountId` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;;
Handling user interaction
The service providers need a few jsps for interacting with the visitors. These jsps contain a form to submit a connect request and another jsp that the user gets to see if he has already connected. Check the source code if you want to see these jsps.
Finally I want to have a short look at the usage of the api. I’ll briefly go through the api of linkedin and the one of twitter.
Linkedin API
I have created the LinkedinController
that handles all api calls to linked in. I have two sample, one to obtain all contacts and one to obtain your profile. I’ll show the method for obtaining all your contacts.
@RequestMapping(value = "/connect/linkedin/connections", method = RequestMethod.GET) public String connections(WebRequest request) { request.setAttribute("connections", linkedinApi().getConnections(), WebRequest.SCOPE_REQUEST); return "linkedin/connections"; } private LinkedInApi linkedinApi() { User principal = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); return linkedInServiceProvider.getConnections(principal.getUsername()).get(0).getServiceApi(); }
One important thing to notice is in the last line. You can see that the user can have multiple connections. For the result we only take the first one. That is it, really.
Twitter API
If you have seen the linkedin sample, the twitter sample should not be to hard either. The following code block shows you how to obtain information about your twitter profile.
@RequestMapping(value = "/connect/twitter/profile", method = RequestMethod.GET) public String obtainProfile(WebRequest request) { request.setAttribute("profile", twitterApi().getUserProfile(), WebRequest.SCOPE_REQUEST); return "twitter/profile"; } private TwitterApi twitterApi() { User principal = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); return twitterServiceProvider.getConnections(principal.getUsername()).get(0).getServiceApi(); }
Just like in the linkedin sample, we obtain the name of the logged in user using the spring security SecurityContextHolder.
Concluding
I am still amazed to see how easy it is to connect to social media like linkedin and twitter using the spring-social project. After reading this blog I hope you agree that the library is very effective. I will do some more stuff with the spring-social project, if I find other interesting things I’ll let you know.