-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add OAuth2 support for using Google Storage
- Loading branch information
Showing
9 changed files
with
317 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
src/main/java/com/lz2zg/qsl/servlet/OAuth2CallbackServlet.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package com.lz2zg.qsl.servlet; | ||
|
||
import java.io.IOException; | ||
import java.util.logging.Logger; | ||
|
||
import javax.servlet.ServletConfig; | ||
import javax.servlet.ServletContext; | ||
import javax.servlet.ServletException; | ||
import javax.servlet.http.HttpServlet; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
|
||
public class OAuth2CallbackServlet extends HttpServlet { | ||
|
||
static final Logger log = Logger.getLogger(OAuth2CallbackServlet.class.getName()); | ||
private static final long serialVersionUID = 1L; | ||
OAuth2Client oauth2client; | ||
|
||
@Override | ||
public void init(ServletConfig config) throws ServletException { | ||
super.init(config); | ||
ServletContext context = config.getServletContext(); | ||
oauth2client = (OAuth2Client) context.getAttribute("oauth2client"); | ||
} | ||
|
||
@Override | ||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { | ||
String authCode = req.getParameter("code"); | ||
log.info("authCode: " + authCode); | ||
oauth2client.fetchAccessAndRefreshTokens(authCode); | ||
resp.setContentType("text/html"); | ||
resp.getWriter().println("<html><body>Authorization in Google Storage successful</body></html>"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.lz2zg.qsl.servlet; | ||
|
||
|
||
public interface OAuth2Client { | ||
|
||
String CLIENT_ID = "348460834037.apps.googleusercontent.com"; | ||
|
||
// this is useless until coupled with refresh token, | ||
// so it safe to be hardcoded as a constant | ||
String CLIENT_SECRET = "11DrV8S8kCglvYrxlG5Gt61Y"; | ||
|
||
String REDIRECT_URI = "http://localhost:8080/admin/oauth2callback"; | ||
|
||
String getAccessToken(); | ||
|
||
String getAccessToken(String refreshToken); | ||
|
||
void fetchAccessAndRefreshTokens(String authCode); | ||
|
||
String getRefreshToken(); | ||
} |
129 changes: 129 additions & 0 deletions
129
src/main/java/com/lz2zg/qsl/servlet/OAuth2ClientImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
package com.lz2zg.qsl.servlet; | ||
|
||
import com.google.appengine.api.datastore.DatastoreService; | ||
import com.google.appengine.api.datastore.Entity; | ||
import com.google.appengine.api.datastore.EntityNotFoundException; | ||
import com.google.appengine.api.datastore.Key; | ||
import com.google.appengine.api.datastore.KeyFactory; | ||
import com.google.appengine.api.memcache.Expiration; | ||
import com.google.appengine.api.memcache.MemcacheService; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.IOException; | ||
import java.io.InputStreamReader; | ||
import java.net.URL; | ||
import java.net.URLConnection; | ||
import java.net.URLEncoder; | ||
import java.util.logging.Logger; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
public class OAuth2ClientImpl implements OAuth2Client { | ||
|
||
static final Logger log = Logger.getLogger(OAuth2ClientImpl.class.getName()); | ||
static final String OAUTH_URL = "https://accounts.google.com/o/oauth2/token"; | ||
final DatastoreService datastore; | ||
final MemcacheService memcacheService; | ||
final static String ENC = "UTF-8"; | ||
final static String ACCESS_TOKEN_KEY = "accessToken"; | ||
|
||
public OAuth2ClientImpl(DatastoreService datastore, MemcacheService memcacheService) { | ||
this.datastore = datastore; | ||
this.memcacheService = memcacheService; | ||
} | ||
|
||
@Override | ||
public String getAccessToken() { | ||
return (String) memcacheService.get(ACCESS_TOKEN_KEY); | ||
} | ||
|
||
@Override | ||
public String getAccessToken(String refreshToken) { | ||
try { | ||
String data = String.format("client_id=%s&client_secret=%s&refresh_token=%s&grant_type=refresh_token", | ||
URLEncoder.encode(CLIENT_ID, ENC), | ||
URLEncoder.encode(CLIENT_SECRET, ENC), | ||
URLEncoder.encode(refreshToken, ENC)); | ||
String response = doPost(data); | ||
log.info("Response after sending refreshToken: " + response); | ||
String accessToken = parseStringField("access_token", response); | ||
int expiresIn = parseIntField("expires_in", response); | ||
memcacheService.put(ACCESS_TOKEN_KEY, accessToken, Expiration.byDeltaSeconds(expiresIn)); | ||
return accessToken; | ||
} catch (IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
@Override | ||
public void fetchAccessAndRefreshTokens(String authCode) { | ||
try { | ||
String data = String.format("code=%s&client_id=%s&client_secret=%s&redirect_uri=%s&grant_type=authorization_code", | ||
URLEncoder.encode(authCode, ENC), | ||
URLEncoder.encode(CLIENT_ID, ENC), | ||
URLEncoder.encode(CLIENT_SECRET, ENC), | ||
URLEncoder.encode(REDIRECT_URI, ENC)); // what is this for? | ||
String response = doPost(data); | ||
log.info("Response after sending authCode: " + response); | ||
String accessToken = parseStringField("access_token", response); | ||
String refreshToken = parseStringField("refresh_token", response); | ||
int expiresIn = parseIntField("expires_in", response); | ||
memcacheService.put(ACCESS_TOKEN_KEY, accessToken, Expiration.byDeltaSeconds(expiresIn)); | ||
saveRefreshToken(refreshToken); | ||
} catch (IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
Integer parseIntField(String field, String text) { | ||
Pattern p = Pattern.compile(String.format("\"%s\":(\\d+)", field)); | ||
Matcher m = p.matcher(text); | ||
if (m.find()) { | ||
return Integer.parseInt(m.group(1)); | ||
} | ||
return null; | ||
} | ||
|
||
String parseStringField(String field, String text) { | ||
Pattern p = Pattern.compile(String.format("\"%s\":\"(.+?)\"", field)); | ||
Matcher m = p.matcher(text); | ||
if (m.find()) { | ||
return m.group(1); | ||
} | ||
return null; | ||
} | ||
|
||
String doPost(String data) throws IOException { | ||
URL url = new URL(OAUTH_URL); | ||
URLConnection conn = url.openConnection(); | ||
conn.setDoOutput(true); | ||
conn.getOutputStream().write(data.getBytes(ENC)); | ||
conn.getOutputStream().flush(); | ||
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream())); | ||
String line = null; | ||
String response = ""; | ||
while ((line = rd.readLine()) != null) { | ||
response += line; | ||
} | ||
rd.close(); | ||
return response; | ||
} | ||
|
||
@Override | ||
public String getRefreshToken() { | ||
Key key = KeyFactory.createKey("refreshToken", "GoogleStorage"); | ||
try { | ||
Entity entity = datastore.get(key); | ||
return (String) entity.getProperty("value"); | ||
} catch (EntityNotFoundException e) { | ||
return null; | ||
} | ||
} | ||
|
||
void saveRefreshToken(String refreshToken) { | ||
Key key = KeyFactory.createKey("refreshToken", "GoogleStorage"); | ||
Entity entity = new Entity(key); | ||
entity.setProperty("value", refreshToken); | ||
datastore.put(entity); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package com.lz2zg.qsl.servlet; | ||
|
||
import java.io.IOException; | ||
import java.net.URLEncoder; | ||
import java.util.logging.Logger; | ||
|
||
import javax.servlet.Filter; | ||
import javax.servlet.FilterChain; | ||
import javax.servlet.FilterConfig; | ||
import javax.servlet.ServletContext; | ||
import javax.servlet.ServletException; | ||
import javax.servlet.ServletRequest; | ||
import javax.servlet.ServletResponse; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
|
||
|
||
public class OAuth2Filter implements Filter { | ||
|
||
static final Logger log = Logger.getLogger(OAuth2ClientImpl.class.getName()); | ||
final static String GS_SCOPE = "https://www.googleapis.com/auth/devstorage.read_write"; | ||
OAuth2Client oauth2client; | ||
|
||
@Override | ||
public void init(FilterConfig config) throws ServletException { | ||
ServletContext context = config.getServletContext(); | ||
oauth2client = (OAuth2Client) context.getAttribute("oauth2client"); | ||
} | ||
|
||
@Override | ||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { | ||
HttpServletRequest req = (HttpServletRequest) request; | ||
HttpServletResponse resp = (HttpServletResponse) response; | ||
if (req.getRequestURL().toString().equals(OAuth2Client.REDIRECT_URI)) { | ||
log.info("callback has been requested, pass forward"); | ||
chain.doFilter(request, response); | ||
return; | ||
} | ||
String accessToken = oauth2client.getAccessToken(); | ||
if (accessToken != null) { | ||
log.info("accessToken has been found"); | ||
req.setAttribute("accessToken", accessToken); | ||
chain.doFilter(request, response); | ||
} else { | ||
String refreshToken = oauth2client.getRefreshToken(); | ||
if (refreshToken != null) { | ||
log.info("getting accessToken using refreshToken"); | ||
accessToken = oauth2client.getAccessToken(refreshToken); | ||
if (accessToken != null) { | ||
req.setAttribute("accessToken", accessToken); | ||
chain.doFilter(request, response); | ||
} else { | ||
resp.setStatus(401); | ||
resp.setContentType("text/html"); | ||
resp.getWriter().println("<html><body>Not Authorized</body></html>"); | ||
} | ||
} else { | ||
log.info("refreshToken not found, redirect to get authCode"); | ||
resp.sendRedirect(String.format("https://accounts.google.com/o/oauth2/auth?client_id=%s&redirect_uri=%s&scope=%s&response_type=code", | ||
OAuth2Client.CLIENT_ID, | ||
URLEncoder.encode(OAuth2Client.REDIRECT_URI, "UTF-8"), | ||
URLEncoder.encode(GS_SCOPE, "UTF-8"))); | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public void destroy() { | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.lz2zg.qsl.servlet; | ||
|
||
import java.io.IOException; | ||
|
||
import javax.servlet.ServletException; | ||
import javax.servlet.http.HttpServlet; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
|
||
public class UploadServlet extends HttpServlet { | ||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
@Override | ||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { | ||
String accessToken = (String) req.getAttribute("accessToken"); | ||
resp.setContentType("text/html"); | ||
resp.getWriter().println("Access token: " + accessToken); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# A default java.util.logging configuration. | ||
# (All App Engine logging is through java.util.logging by default). | ||
# | ||
# To use this configuration, copy it into your application's WEB-INF | ||
# folder and add the following to your appengine-web.xml: | ||
# | ||
# <system-properties> | ||
# <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/> | ||
# </system-properties> | ||
# | ||
|
||
# Set the default logging level for all loggers to WARNING | ||
.level = INFO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters