I had been using google oauth in one of my projects and that worked until I added a dependency that conflicted with google's oauth dependencies and gave me the following error:
java.lang.NoSuchMethodError: com.google.common.primitives.UnsignedInteger.asUnsigned(I)Lcom/google/common/primitives/UnsignedInteger;at com.google.api.client.util.Data.<clinit>(Data.java:81)at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:502)at com.google.api.client.json.JsonParser.parse(JsonParser.java:289)
I upgraded to the latest version of the google oauth libraries (1.22.0), however some aspects of the api had changed and the proper usage wasn't exactly clear. After some digging around I finally I got it working
First the maven dependencies:
<dependency> <groupId>com.google.api-client</groupId> <artifactId>google-api-client</artifactId> <version>1.22.0</version> </dependency> <dependency> <groupId>com.google.apis</groupId> <artifactId>google-api-services-oauth2</artifactId> <version>v2-rev124-1.22.0</version> </dependency> <dependency> <groupId>com.google.oauth-client</groupId> <artifactId>google-oauth-client</artifactId> <version>1.22.0</version> </dependency> <dependency> <groupId>com.google.oauth-client</groupId> <artifactId>google-oauth-client-servlet</artifactId> <version>1.22.0</version> <exclusions> <exclusion> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.google.oauth-client</groupId> <artifactId>google-oauth-client-jetty</artifactId> <version>1.22.0</version> <exclusions> <exclusion> <!-- I'm explicitly including jetty so don't need this --> <groupId>org.mortbay.jetty</groupId> <artifactId>jetty</artifactId> </exclusion> </exclusions> </dependency>
And my Jetty deps. Any other servlet container should work too
<dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-server</artifactId> <version>8.1.8.v20121106</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-servlet</artifactId> <version>8.1.8.v20121106</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-util</artifactId> <version>8.1.8.v20121106</version> </dependency>
The first time I started the app I got the following error:
java.lang.SecurityException: class "javax.servlet.FilterRegistration"'s signer information does not match signer information of other classes in the same package
There are two servlets needed. One initiates the oauth process by redirecting to google, while the other receives the redirect back from google with auth tokens. Since they both share code I created a separate class to avoid duplicating code
import java.io.IOException; import java.util.Arrays; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.api.client.extensions.servlet.auth.oauth2.AbstractAuthorizationCodeServlet; import org.apache.log4j.Logger; import com.google.api.client.auth.oauth2.AuthorizationCodeFlow; import com.google.api.client.http.HttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.JsonFactory; import com.google.api.client.json.jackson2.JacksonFactory; // some doc https://developers.google.com/api-client-library/java/google-api-java-client/oauth2public class Oauth2Servlet extends AbstractAuthorizationCodeServlet { private final Logger log = Logger.getLogger(this.getClass()); public final static HttpTransport HTTP_TRANSPORT = new NetHttpTransport(); public final static JsonFactory JSON_FACTORY = new JacksonFactory(); public final static List<String> SCOPES = Arrays.asList("https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email"); @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { log.debug("Auth successful"); response.sendRedirect(OauthCommon.SERVLET_CONTEXT + "/"); } @Override protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { return OAUTH_COMMON.getRedirectUri(req); } @Override protected AuthorizationCodeFlow initializeFlow() throws IOException { return OAUTH_COMMON.initializeFlow(); } @Override protected String getUserId(HttpServletRequest req) throws ServletException, IOException { return OAUTH_COMMON.getUserId(req); } }
Here's the callback servlet. When onSuccess is called we have a oauth token and can get make calls to oauth endpoints to get user info. In this case I've requested the userinfo and profile scopes so I can get basic user info simply by calling oauth2.userinfo().get().execute()
import java.io.IOException; import java.util.Date; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.api.services.oauth2.model.Userinfoplus; import org.apache.log4j.Logger; import com.google.api.client.auth.oauth2.AuthorizationCodeFlow; import com.google.api.client.auth.oauth2.AuthorizationCodeResponseUrl; import com.google.api.client.auth.oauth2.Credential; import com.google.api.client.extensions.servlet.auth.oauth2.AbstractAuthorizationCodeCallbackServlet; import com.google.api.services.oauth2.Oauth2; public class Oauth2ServletCallback extends AbstractAuthorizationCodeCallbackServlet { private final Logger log = Logger.getLogger(this.getClass()); @Override protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential) throws ServletException, IOException { log.debug("Callback success: token: " + credential.getAccessToken() + ", refresh token " + credential.getRefreshToken() + ", expires " + new Date(credential.getExpirationTimeMilliseconds())); Oauth2 oauth2 = new Oauth2.Builder(Oauth2Servlet.HTTP_TRANSPORT, Oauth2Servlet.JSON_FACTORY, credential).setApplicationName("").build(); Userinfoplus userinfo = null; try { userinfo = oauth2.userinfo().get().execute(); log.debug("User email is " + userinfo.getEmail() + ", name is " + userinfo.getName()); req.getSession().setAttribute("AUTH", credential); req.getSession().setAttribute("USER", userinfo.getName()); req.getSession().setAttribute("EMAIL", userinfo.getEmail()); // redirect back to our app resp.sendRedirect(OauthCommon.SERVLET_CONTEXT + "/"); return; } catch (Exception e) { log.warn("Unable to get userinfo", e); } } @Override protected void onError(HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse) throws ServletException, IOException { throw new RuntimeException("grrrrrrrrrr: " + errorResponse); } @Override protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { return OAUTH_COMMON.getRedirectUri(req); } @Override protected AuthorizationCodeFlow initializeFlow() throws IOException { return OAUTH_COMMON.initializeFlow(); } @Override protected String getUserId(HttpServletRequest req) throws ServletException, IOException { return OAUTH_COMMON.getUserId(req); } }
And lastly here's the shared code
import java.io.IOException; import java.util.UUID; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import com.google.api.client.auth.oauth2.AuthorizationCodeFlow; import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; import com.google.api.client.http.GenericUrl; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.client.util.store.MemoryDataStoreFactory; import com.google.api.services.oauth2.Oauth2; import com.google.api.services.oauth2.model.Userinfoplus; import org.apache.log4j.Logger; public class OauthCommon {private final Logger log = Logger.getLogger(this.getClass());public final static OauthCommon OAUTH_COMMON = new OauthCommon(); public final static String SERVLET_CONTEXT = "/app"; public final static String OAUTH_CALLBACK_PATH = "/oauthcallback"; public final static String OAUTH_PATH = "/oauth"; public final static String CALL_BACK_URI = SERVLET_CONTEXT + OAUTH_CALLBACK_PATH; public final static String OAUTH_URI = SERVLET_CONTEXT + OAUTH_PATH; protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { GenericUrl url = new GenericUrl(req.getRequestURL().toString()); url.setRawPath(CALL_BACK_URI); return url.build(); } public AuthorizationCodeFlow initializeFlow() throws IOException { return new GoogleAuthorizationCodeFlow.Builder( new NetHttpTransport(), JacksonFactory.getDefaultInstance(), "YOUR CLIENT ID GOES HERE", "YOUR SECRET GOES HERE", Oauth2Servlet.SCOPES ).setDataStoreFactory(new MemoryDataStoreFactory()).setAccessType("offline").build(); } public String getUserId(HttpServletRequest req) throws ServletException, IOException { String id = req.getSession().getId(); log.debug("Session id is " + id); return id; } }
If you acquired a auth token by another means and wanted to use the google oauth library to retrieve data, you could do like so
GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken); Oauth2 oauth2 = new Oauth2.Builder(new NetHttpTransport(), new JacksonFactory(), credential).setApplicationName( "Oauth2").build(); Userinfoplus userinfo = oauth2.userinfo().get().execute();
I'm using embedded Jetty with a self-signed cert and configuring the servlets as follows:
SslContextFactory sslContextFactory = new SslContextFactory("../keystore"); sslContextFactory.setKeyStorePassword("supersecret"); SslSelectChannelConnector selectChannelConnector = new SslSelectChannelConnector(sslContextFactory); selectChannelConnector.setPort(8443); server.setConnectors(new Connector[] { selectChannelConnector }); ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath(OauthCommon.SERVLET_CONTEXT); server.setHandler(context); context.addServlet(new ServletHolder(new MyAppServlet()),"/"); context.addServlet(new ServletHolder(new Oauth2Servlet()), OauthCommon.OAUTH_PATH); context.addServlet(new ServletHolder(new Oauth2ServletCallback()), OauthCommon.OAUTH_CALLBACK_PATH); context.addServlet(new ServletHolder(new LogoutServlet()), "/logout"); log.debug("Started Jetty"); server.start(); server.join();
No comments:
Post a Comment