Spring MVC and Scribe (the simple OAuth Java lib)
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:
- JDK 1.6
- Apache Maven
- 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.
LinkedIn , Twitter , Facebook
##### 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.
(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