Spring MVC and Scribe (the simple OAuth Java lib)
Join the DZone community and get the full member experience.
Join For FreeBefore 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.
Opinions expressed by DZone contributors are their own.
Trending
-
Effortlessly Streamlining Test-Driven Development and CI Testing for Kafka Developers
-
Auditing Tools for Kubernetes
-
Design Patterns for Microservices: Ambassador, Anti-Corruption Layer, and Backends for Frontends
-
A Complete Guide to AWS File Handling and How It Is Revolutionizing Cloud Storage
Comments