Highly motivated Senior Java/JEE Software Developer with 7+ years of solid industry experience. Excellent team player with an experience working in distributed international teams, using Agile/Scrum methodology. Artur has posted 9 posts at DZone. You can read more from them at their website. View Full User Profile

Spring MVC and Scribe (the simple OAuth Java lib)

12.01.2011
| 11147 views |
  • submit to reddit

Before Starting this article let's define what's OAuth.

OAuth: An open protocol to allow secure API authorization in a simple and standard method from desktop and web applications.

Today's most applications don't need to have authentication and authorization module, all they need is to be integrated with third party oauth service providers like LinkedIn, Facebook, Twitter, Google, etc...

Long time I was looking for a simple and powerfull oauth java library and I found two libraries spring-social and scribe, playing with both of them I really liked scribe and in this article we are going to cover how to use the Simplest OAuth Java lib Scribe with Spring MVC.
Many thanks to Pablo Fernandez for such an amazing library.

To bring the simple application to your attention I have used the following technologies and tools:

  1. JDK 1.6
  2. Apache Maven
  3. Apache Tomcat

We are going to sign-in with LinkedIn, Twitter and Facebook and here is our configuration:
In order to create real workign example you need to get your api keys from providers. LinkedInTwitterFacebook

##### oauth config #######

app.config.oauth.facebook.apikey=yourapikey
app.config.oauth.facebook.apisecret=yourapisecret
app.config.oauth.facebook.callback=yourwebappcallbackurl

app.config.oauth.twitter.apikey=yourapikey
app.config.oauth.twitter.apisecret=yourapisecret
app.config.oauth.twitter.callback=yourwebappcallbackurl

app.config.oauth.linkedin.apikey=yourapikey
app.config.oauth.linkedin.apisecret=yourapisecret
app.config.oauth.linkedin.callback=yourwebappcallbackurl

Bellow is our spring configuration xml file.

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="
        http://www.springframework.org/schema/beans     
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/util     
        http://www.springframework.org/schema/util/spring-util-3.0.xsd
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
	
	<!-- Configures the annotation-driven Spring MVC Controller programming model.
				Note that, with Spring 3.0, this tag works in Servlet MVC only!  -->
	<mvc:annotation-driven />
	
	<!-- Activates various annotations to be detected in bean classes -->
	<context:annotation-config />
	
	<util:properties id="applicationConfig" location="classpath:application.properties"/>
	<context:property-placeholder properties-ref="applicationConfig"/>
	
	<!-- Static resources -->
	<mvc:resources mapping="/resources/**" location="/resources/" />
	
	<!-- Scans the classpath for annotated components that will be auto-registered as Spring beans.
 			 For example @Controller and @Service. Make sure to set the correct base-package-->
	<context:component-scan base-package="edu.seua.scribe.web.controller" />
	
	<!-- View Resolver -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/pages/" p:suffix=".jsp" p:order="1" />
	
	<!-- Tiles Config -->
	<bean id="tilesviewResolver" class="org.springframework.web.servlet.view.tiles2.TilesViewResolver" p:order="0"/>
	<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer" p:definitions="/WEB-INF/tiles/tiles-defs.xml" />
	
	
	<!-- oauth -->
	<bean id="linkedInServiceConfig" class="edu.seua.scribe.OAuthServiceConfig">
		<constructor-arg value="${app.config.oauth.linkedin.apikey}" />
		<constructor-arg value="${app.config.oauth.linkedin.apisecret}"/>
		<constructor-arg value="${app.config.oauth.linkedin.callback}"/>
		<constructor-arg value="org.scribe.builder.api.LinkedInApi"/>
	</bean>
	
	<bean id="linkedInServiceProvider" class="edu.seua.scribe.OAuthServiceProvider">
		<constructor-arg name="config" ref="linkedInServiceConfig" />
	</bean>
	
	<bean id="facebookServiceConfig" class="edu.seua.scribe.OAuthServiceConfig">
		<constructor-arg value="${app.config.oauth.facebook.apikey}" />
		<constructor-arg value="${app.config.oauth.facebook.apisecret}"/>
		<constructor-arg value="${app.config.oauth.facebook.callback}"/>
		<constructor-arg value="org.scribe.builder.api.FacebookApi"/>
	</bean>
	
	<bean id="facebookServiceProvider" class="edu.seua.scribe.OAuthServiceProvider">
		<constructor-arg name="config" ref="facebookServiceConfig" />
	</bean>
	
	<bean id="twitterServiceConfig" class="edu.seua.scribe.OAuthServiceConfig">
		<constructor-arg value="${app.config.oauth.twitter.apikey}" />
		<constructor-arg value="${app.config.oauth.twitter.apisecret}"/>
		<constructor-arg value="${app.config.oauth.twitter.callback}"/>
		<constructor-arg value="org.scribe.builder.api.TwitterApi"/>
	</bean>
	
	<bean id="twitterServiceProvider" class="edu.seua.scribe.OAuthServiceProvider">
		<constructor-arg name="config" ref="twitterServiceConfig" />
	</bean>
	
	<!-- end of oauth -->
 
</beans>

In our application we are using two simple classes to configure oauth service:

package edu.seua.scribe;

import org.scribe.builder.api.Api;

public class OAuthServiceConfig {
	
	private String apiKey;
	private String apiSecret;
	private String callback;
	private Class apiClass;
	
	public OAuthServiceConfig() {
	}
	
	public OAuthServiceConfig(String apiKey, String apiSecret, String callback,
            Class apiClass) {
	    super();
	    this.apiKey = apiKey;
	    this.apiSecret = apiSecret;
	    this.callback = callback;
	    this.apiClass = apiClass;
    }
    // getters and setters ...
}

package edu.seua.scribe;

import org.scribe.builder.ServiceBuilder;
import org.scribe.oauth.OAuthService;

public class OAuthServiceProvider {
	
	private OAuthServiceConfig config;
	
	public OAuthServiceProvider() {
	}
	
	public OAuthServiceProvider(OAuthServiceConfig config) {
		this.config = config;
	}

	public OAuthService getService() {
		System.out.println(config);
		return new ServiceBuilder().provider(config.getApiClass())
							.apiKey(config.getApiKey())
						    .apiSecret(config.getApiSecret())
						    .callback(config.getCallback())
						    .build();
	}
	
}

Now it's time to see our Spring Controllers.
Bellow is Controller class for LinkedIn authentication.

package edu.seua.scribe.web.controller;


import org.scribe.model.*;
import org.scribe.oauth.OAuthService;
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import static org.springframework.web.context.request.RequestAttributes.*;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;

import edu.seua.scribe.OAuthServiceProvider;
import static edu.seua.scribe.web.SessionAttributes.*;

@Controller
public class LinkedInController {
	
	@Autowired
	@Qualifier("linkedInServiceProvider")
	private OAuthServiceProvider linkedInServiceProvider;
	
	@RequestMapping(value={"/login-linkedin"}, method = RequestMethod.GET)
	public String login(WebRequest request) {
		
		// getting request and access token from session
		Token requestToken = (Token) request.getAttribute(ATTR_OAUTH_REQUEST_TOKEN, SCOPE_SESSION);
		Token accessToken = (Token) request.getAttribute(ATTR_OAUTH_ACCESS_TOKEN, SCOPE_SESSION);
		if(requestToken == null || accessToken == null) {
			// generate new request token
			OAuthService service = linkedInServiceProvider.getService();
			requestToken = service.getRequestToken();
			request.setAttribute(ATTR_OAUTH_REQUEST_TOKEN, requestToken, SCOPE_SESSION);
			
			// redirect to linkedin auth page
			return "redirect:" + service.getAuthorizationUrl(requestToken);
		}
		return "welcomePage";
	}
	
	@RequestMapping(value={"/linkedin-callback"}, method = RequestMethod.GET)
	public ModelAndView callback(@RequestParam(value="oauth_verifier", required=false) String oauthVerifier, WebRequest request) {
		
		// getting request tocken
		OAuthService service = linkedInServiceProvider.getService();
		Token requestToken = (Token) request.getAttribute(ATTR_OAUTH_REQUEST_TOKEN, SCOPE_SESSION);
		
		// getting access token
		Verifier verifier = new Verifier(oauthVerifier);
		Token accessToken = service.getAccessToken(requestToken, verifier);
		
		// store access token as a session attribute
		request.setAttribute(ATTR_OAUTH_ACCESS_TOKEN, accessToken, SCOPE_SESSION);
		
		// getting user profile
		OAuthRequest oauthRequest = new OAuthRequest(Verb.GET, "http://api.linkedin.com/v1/people/~:(id,first-name,last-name,industry,headline)");
		service.signRequest(accessToken, oauthRequest);
		Response oauthResponse = oauthRequest.send();
		System.out.println(oauthResponse.getBody());

		ModelAndView mav = new ModelAndView("redirect:loginPage");
		return mav;
	}
}

The next one is Controller class for Facebook authentication.

package edu.seua.scribe.web.controller;


import org.scribe.model.*;
import org.scribe.oauth.OAuthService;
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import static org.springframework.web.context.request.RequestAttributes.*;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;

import edu.seua.scribe.OAuthServiceProvider;
import static edu.seua.scribe.web.SessionAttributes.*;

@Controller
public class FacebookController {
	
	@Autowired
	@Qualifier("facebookServiceProvider")
	private OAuthServiceProvider facebookServiceProvider;
	
	private static final Token EMPTY_TOKEN = null;
	
	@RequestMapping(value={"/login-facebook"}, method = RequestMethod.GET)
	public String login(WebRequest request) {
		
		// getting request and access token from session
		Token accessToken = (Token) request.getAttribute(ATTR_OAUTH_ACCESS_TOKEN, SCOPE_SESSION);
		if(accessToken == null) {
			// generate new request token
			OAuthService service = facebookServiceProvider.getService();
			request.setAttribute(ATTR_OAUTH_REQUEST_TOKEN, EMPTY_TOKEN, SCOPE_SESSION);
			
			// redirect to facebook auth page
			return "redirect:" + service.getAuthorizationUrl(EMPTY_TOKEN);
		}
		return "welcomePage";
	}
	
	@RequestMapping(value={"/facebook-callback"}, method = RequestMethod.GET)
	public ModelAndView callback(@RequestParam(value="code", required=false) String oauthVerifier, WebRequest request) {
		
		// getting request token
		OAuthService service = facebookServiceProvider.getService();
		Token requestToken = (Token) request.getAttribute(ATTR_OAUTH_REQUEST_TOKEN, SCOPE_SESSION);
		
		// getting access token
		Verifier verifier = new Verifier(oauthVerifier);
		Token accessToken = service.getAccessToken(requestToken, verifier);
		
		// store access token as a session attribute
		request.setAttribute(ATTR_OAUTH_ACCESS_TOKEN, accessToken, SCOPE_SESSION);
		
		// getting user profile
		OAuthRequest oauthRequest = new OAuthRequest(Verb.GET, "https://graph.facebook.com/me");
		service.signRequest(accessToken, oauthRequest);
		Response oauthResponse = oauthRequest.send();
		System.out.println(oauthResponse.getBody());

		ModelAndView mav = new ModelAndView("redirect:loginPage");
		return mav;
	}
}

and the last one is Controller class for Twitter authentication.

package edu.seua.scribe.web.controller;


import org.scribe.model.*;
import org.scribe.oauth.OAuthService;
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import static org.springframework.web.context.request.RequestAttributes.*;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;

import edu.seua.scribe.OAuthServiceProvider;
import static edu.seua.scribe.web.SessionAttributes.*;

@Controller
public class FacebookController {
	
	@Autowired
	@Qualifier("facebookServiceProvider")
	private OAuthServiceProvider facebookServiceProvider;
	
	private static final Token EMPTY_TOKEN = null;
	
	@RequestMapping(value={"/login-facebook"}, method = RequestMethod.GET)
	public String login(WebRequest request) {
		
		// getting request and access token from session
		Token accessToken = (Token) request.getAttribute(ATTR_OAUTH_ACCESS_TOKEN, SCOPE_SESSION);
		if(accessToken == null) {
			// generate new request token
			OAuthService service = facebookServiceProvider.getService();
			request.setAttribute(ATTR_OAUTH_REQUEST_TOKEN, EMPTY_TOKEN, SCOPE_SESSION);
			
			// redirect to facebook auth page
			return "redirect:" + service.getAuthorizationUrl(EMPTY_TOKEN);
		}
		return "welcomePage";
	}
	
	@RequestMapping(value={"/facebook-callback"}, method = RequestMethod.GET)
	public ModelAndView callback(@RequestParam(value="code", required=false) String oauthVerifier, WebRequest request) {
		
		// getting request token
		OAuthService service = facebookServiceProvider.getService();
		Token requestToken = (Token) request.getAttribute(ATTR_OAUTH_REQUEST_TOKEN, SCOPE_SESSION);
		
		// getting access token
		Verifier verifier = new Verifier(oauthVerifier);
		Token accessToken = service.getAccessToken(requestToken, verifier);
		
		// store access token as a session attribute
		request.setAttribute(ATTR_OAUTH_ACCESS_TOKEN, accessToken, SCOPE_SESSION);
		
		// getting user profile
		OAuthRequest oauthRequest = new OAuthRequest(Verb.GET, "https://graph.facebook.com/me");
		service.signRequest(accessToken, oauthRequest);
		Response oauthResponse = oauthRequest.send();
		System.out.println(oauthResponse.getBody());

		ModelAndView mav = new ModelAndView("redirect:loginPage");
		return mav;
	}
}

 

Who said OAuth was difficult? That's all enjoy coding.

You can find attached copy of full application and you can host your app at cloudbees (Java PaaS) even while you are coding. 

Legacy
Article Resources: 
Published at DZone with permission of its author, Artur Mkrtchyan.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Pratap Murukutla replied on Thu, 2012/03/15 - 4:51am

Hi thanks for your post.

i have a need to work this with localhost application.

 

will it work?  

my service is avaialable as local host application for student project. i have some domain with my blog name.

but i can not use domain for the service of my localhost application.

 

Please help me..

 

thank you. 

Artur Mkrtchyan replied on Sun, 2012/03/18 - 3:06pm in response to: Pratap Murukutla

Hi Pratap,

 

Actually you can't login by using locahost as Facebook, Linkedin and all others are  redirecting to your webpage after login.

 

 But you can use  cloudbees.com or other PaaS providers to host your application then you will have accessable url.

 

Thanks,

Artur

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.