App Engine中的Google Services身份验证,第1部分
項(xiàng)目源代碼
這篇文章背后的動(dòng)機(jī)是,我很難找到以前將這些技術(shù)真正聯(lián)系在一起的任何示例。 然而,這些技術(shù)確實(shí)代表了許多想要利用大量Google API服務(wù)的Web應(yīng)用程序的基礎(chǔ)。
為了簡(jiǎn)單起見,該演示僅允許用戶通過Google域登錄; 授權(quán)訪問用戶的Google文檔服務(wù); 并顯示用戶的Google Docs Word和電子表格文檔的列表。 在本教程中,我會(huì)對(duì)讀者的專業(yè)知識(shí)做出一些假設(shè),例如對(duì)Java的深入了解。
流程概述
在我們直接進(jìn)入教程/演示之前,讓我們簡(jiǎn)要介紹一下導(dǎo)航流程。
盡管它看起來很復(fù)雜,但是主要流程可以總結(jié)為:
可以使用相同的方法訪問其他Google服務(wù),例如YouTube(例如,您可以顯示用戶喜歡的視頻的列表)。
環(huán)境設(shè)定
對(duì)于本教程,我使用以下內(nèi)容:
- Eclipse Indigo Service Release 2以及適用于Eclipse的Google插件(請(qǐng)參閱設(shè)置說明 )。
- Google GData Java SDK Eclipse插件版本1.47.1(請(qǐng)參閱設(shè)置說明 )。
- Google App Engine 1.6.5版。 早期版本存在一些問題,因此我建議確保您正在使用它。 它應(yīng)該作為Eclipse的Google插件的一部分自動(dòng)安裝。
- Objectify版本3.1。 所需的庫已安裝在項(xiàng)目的war / WEB-INF / lib目錄中。
將項(xiàng)目導(dǎo)入Eclipse之后,您的構(gòu)建路徑應(yīng)類似于:
App Engine設(shè)置應(yīng)類似于:
您將需要設(shè)置自己的GAE應(yīng)用程序,并指定自己的應(yīng)用程序ID(請(qǐng)參閱Google GAE開發(fā)人員文檔 )。
描述如何使用OAuth訪問谷歌API服務(wù)我見過的最好的教程可以找到這里 。 我發(fā)現(xiàn)最令人困惑的方面之一是如何獲取放置OAuth請(qǐng)求時(shí)所需的必要的消費(fèi)者密鑰和消費(fèi)者秘密值。 我完成此操作的方式是:
擁有OAuth使用者密鑰和OAuth使用者密鑰之后 ,您將替換com.zazarie.shared.Constant文件中的以下值:
最后的靜態(tài)字符串CONSUMER_KEY =''; 最后的靜態(tài)字符串CONSUMER_SECRET =''; 哇,這似乎是很多工作! 但是,這是一次性交易,您不必再次大驚小怪。
代碼演練
既然我們已經(jīng)完成了OAuth的配置/設(shè)置,那么我們就可以深入研究代碼了。 讓我們從查看戰(zhàn)爭(zhēng)目錄的結(jié)構(gòu)開始,您的Web資產(chǎn)位于該目錄中:
listFiles.jsp是您首次進(jìn)入Web應(yīng)用程序時(shí)顯示的默認(rèn)JSP頁面。 現(xiàn)在讓我們看一下web.xml文件,看看如何配置它,以及對(duì)所有內(nèi)容都至關(guān)重要的servlet過濾器。
<?xml version='1.0' encoding='UTF-8'?><web-app xmlns:xsi='http:www.w3.org2001XMLSchema-instance'xsi:schemaLocation='http:java.sun.comxmlnsjavaee http:java.sun.comxmlnsjavaeeweb-app_2_5.xsd'version='2.5' xmlns='http:java.sun.comxmlnsjavaee'><!-- Filters --><filter><filter-name>AuthorizationFilter<filter-name><filter-class>com.zazarie.server.servlet.filter.AuthorizationFilter<filter-class><filter><filter-mapping><filter-name>AuthorizationFilter<filter-name><url-pattern>html*<url-pattern><filter-mapping><!-- Servlets --><servlet><servlet-name>Step2<servlet-name><servlet-class>com.zazarie.server.servlet.RequestTokenCallbackServlet<servlet-class><servlet><servlet-mapping><servlet-name>Step2<servlet-name><url-pattern>authSub<url-pattern><servlet-mapping><!-- Default page to serve --><welcome-file-list><welcome-file>htmllistFiles.jsp<welcome-file><welcome-file-list><web-app>每當(dāng)請(qǐng)求位于html目錄中的JSP文件時(shí),就會(huì)調(diào)用稱為AuthorizationFilter的servlet過濾器。 我們將在稍后介紹的過濾器負(fù)責(zé)確保用戶已登錄Google,如果是,則確保已為該用戶授予OAuth憑據(jù)(即,它將啟動(dòng)OAuth認(rèn)證過程(如果需要)。
步驟2的Servlet名稱代表已授予OAuth憑據(jù)時(shí)Google調(diào)用的Servlet-將其視為回調(diào)。 我們將對(duì)此進(jìn)行更詳細(xì)的介紹。
讓我們更詳細(xì)地看一下AuthorizationFilter。
AuthorizationFilter深潛
doFilter方法是在servlet過濾器中進(jìn)行工作的地方。 這是實(shí)現(xiàn):
@Overridepublic void doFilter(ServletRequest req, ServletResponse res,FilterChain chain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) req;HttpServletResponse response = (HttpServletResponse) res;HttpSession session = request.getSession();LOGGER.info('Invoking Authorization Filter');LOGGER.info('Destination URL is: ' + request.getRequestURI());if (filterConfig == null)return;get the Google userAppUser appUser = LoginService.login(request, response);if (appUser != null) {session.setAttribute(Constant.AUTH_USER, appUser);}identify if user has an OAuth accessToken - it not, will set in motionoauth procedureif (appUser.getCredentials() == null) {need to save the target URI in session so we can forward to it whenoauth is completedsession.setAttribute(Constant.TARGET_URI, request.getRequestURI());OAuthRequestService.requestOAuth(request, response, session);return;} elsestore DocService in the session so it can be reusedsession.setAttribute(Constant.DOC_SESSION_ID,LoginService.docServiceFactory(appUser));chain.doFilter(request, response);}除了通常的家務(wù)管理之外,主要邏輯還從以下幾行開始:
AppUser appUser = LoginService.login(request,response);
稍后我們將看到,LoginService負(fù)責(zé)將用戶登錄到Google,并在本地BigTable數(shù)據(jù)存儲(chǔ)區(qū)中創(chuàng)建用戶。 通過將用戶存儲(chǔ)在本地,我們可以存儲(chǔ)用戶的OAuth憑據(jù),從而無需用戶每次訪問受限制/過濾的頁面時(shí)都必須授予權(quán)限。
LoginService返回用戶(AppUser對(duì)象)后,我們將該用戶對(duì)象存儲(chǔ)到會(huì)話中(注意:要啟用會(huì)話,必須在appengine-web.xml文件中將session-enabled設(shè)置為:)
session.setAttribute(Constant.AUTH_USER,appUser);
然后,我們檢查OAuth憑據(jù)是否與該用戶相關(guān)聯(lián):
如果(appUser.getCredentials()== null){
session.setAttribute(Constant.TARGET_URI,request.getRequestURI());
OAuthRequestService.requestOAuth(請(qǐng)求,響應(yīng),會(huì)話);
返回;
}其他 session.setAttribute(Constant.DOC_SESSION_ID,LoginService.docServiceFactory(appUser));
如果getCredentials()返回null,則尚未為用戶分配OAuth憑據(jù)。 這意味著OAuth流程需要啟動(dòng)。 由于此過程分為兩步,即將請(qǐng)求發(fā)布到Google,然后通過回調(diào)(上面提到的Step2 servlet)檢索結(jié)果,因此我們需要存儲(chǔ)目標(biāo)URL,以便一旦授權(quán)后就可以將用戶重定向到該URL。處理完成。 這是通過使用setAttribute方法將請(qǐng)求的URL存儲(chǔ)到會(huì)話中來完成的。
然后,我們通過調(diào)用OAuthRequestService.requestOAuth()方法開始OAuth流程(詳細(xì)信息將在下面討論)。
如果getCredentials()返回一個(gè)非null值,則表明我們已經(jīng)從數(shù)據(jù)存儲(chǔ)區(qū)中的本地AppUser條目中獲取了用戶的OAuth憑據(jù),我們只需將其添加到會(huì)話中,以便以后使用。
LoginService深入研究
LoginService類有一個(gè)稱為login的主要方法,其后是一堆JPA幫助器方法,用于保存或更新數(shù)據(jù)存儲(chǔ)區(qū)中的本地用戶。 我們將重點(diǎn)放在login()上,因?yàn)檫@是大多數(shù)業(yè)務(wù)邏輯所在的位置。
public static AppUser login(HttpServletRequest req, HttpServletResponse res) {LOGGER.setLevel(Constant.LOG_LEVEL);LOGGER.info('Initializing LoginService');String URI = req.getRequestURI();UserService userService = UserServiceFactory.getUserService();User user = userService.getCurrentUser();if (user != null) {LOGGER.info('User id is: '' + userService.getCurrentUser().getUserId()+ ''');String userEmail = userService.getCurrentUser().getEmail();AppUser appUser = (AppUser) req.getSession().getAttribute(Constant.AUTH_USER);if (appUser == null) {LOGGER.info('appUser not found in session');see if it is a new userappUser = findUser(userEmail);if (appUser == null) {LOGGER.info('User not found in datastore...creating');appUser = addUser(userEmail);} else {LOGGER.info('User found in datastore...updating');appUser = updateUserTimeStamp(appUser);}} else {appUser = updateUserTimeStamp(appUser);}return appUser;} else {LOGGER.info('Redirecting user to login page');try {res.sendRedirect(userService.createLoginURL(URI));} catch (IOException e) {e.printStackTrace();}}return null;}我們要做的第一件事是使用Google UserService類確定用戶是否登錄到Google:
UserService userService = UserServiceFactory.getUserService();
用戶用戶= userService.getCurrentUser();
如果Google的調(diào)用返回的User對(duì)象為null,則不會(huì)登錄該用戶,并使用以下命令將其重定向到登錄頁面:
res.sendRedirect(userService.createLoginURL(URI));
如果用戶已登錄(即不為null),則下一步是確定該用戶是否存在于本地?cái)?shù)據(jù)存儲(chǔ)中。 這是通過使用appUser = findUser(userEmail)查找用戶及其登錄的Google電子郵件地址來完成的。 由于JPA / Objectify并不是本教程的主要討論點(diǎn),因此我將不介紹該方法的工作原理。 但是, Objectify網(wǎng)站上有一些很棒的教程/文檔。
如果用戶不在本地,則使用Google的電子郵件地址填充該對(duì)象,并使用appUser = addUser(userEmail)創(chuàng)建該對(duì)象。 如果用戶確實(shí)存在,我們僅出于登錄目的而更新登錄時(shí)間戳。
OAuthRequestService深入研究
您可能會(huì)回憶起,在本地設(shè)置了用戶之后,AuthorizationFilter便會(huì)檢查該用戶是否已授予OAuth憑據(jù)。 如果不是,則調(diào)用OAuthRequestService.requestOAuth()方法。 如下圖所示:
為了簡(jiǎn)化OAuth的使用,Google提供了一組我們正在使用的Java幫助器類。 我們需要做的第一件事是設(shè)置使用者憑據(jù)(獲取這些憑據(jù)之前已經(jīng)討論過):
GoogleOAuthParameters oauthParameters =新的GoogleOAuthParameters();
oauthParameters.setOAuthConsumerKey(Constant.CONSUMER_KEY);
oauthParameters.setOAuthConsumerSecret(Constant.CONSUMER_SECRET);
然后,我們使用以下命令設(shè)置OAuth請(qǐng)求的范圍:
oauthParameters.setScope(Constant.GOOGLE_RESOURCE);
Constant.GOOGLE_RESOURCE解析為https://docs.google.com/feeds/的位置。 發(fā)出OAuth請(qǐng)求時(shí),請(qǐng)指定您試圖獲得訪問權(quán)限的資源范圍。 在這種情況下,我們嘗試訪問Google文檔(每個(gè)服務(wù)的GData API都提供了作用域URL)。 接下來,我們確定希望Google返回答復(fù)的位置。
oauthParameters.setOAuthCallback(Constant.OATH_CALLBACK);
無論我們是以開發(fā)人員模式在本地運(yùn)行還是部署到Google App Engine,此值都會(huì)更改。 在Constant接口中定義值的方法如下:
//用于在GAE上運(yùn)行
//最終靜態(tài)字符串OATH_CALLBACK ='http://tennis-coachrx.appspot.com/authSub';
//用于本地測(cè)試
最后的靜態(tài)字符串OATH_CALLBACK ='http://127.0.0.1:8888/authSub';
然后,使用Google的幫助程序簽署請(qǐng)求時(shí):
GoogleOAuthHelper oauthHelper =新的GoogleOAuthHelper(新的OAuthHmacSha1Signer());
然后,我們生成用戶將導(dǎo)航到的URL,以授權(quán)對(duì)資源的訪問。 這是使用以下方法動(dòng)態(tài)生成的:
字符串rovalPageUrl = oauthHelper.createUserAuthorizationUrl(oauthParameters);
最后一步是向用戶提供鏈接,以便他們可以導(dǎo)航到該URL來批準(zhǔn)請(qǐng)求。 這是通過構(gòu)造一些簡(jiǎn)單HTML來完成的,這些HTML使用res.getWriter()。print()輸出。
用戶授予訪問權(quán)限后,Google便會(huì)調(diào)用由URL參數(shù)/ authSub標(biāo)識(shí)的servlet,該參數(shù)對(duì)應(yīng)于servlet類RequestTokenCallbackServlet。 接下來,我們將對(duì)此進(jìn)行檢查。
RequestTokenCallbackServlet深入研究
該servlet使用Google OAuth幫助程序類來生成所需的訪問令牌和秘密訪問令牌,這些訪問令牌和秘密訪問令牌將在隨后對(duì)Google API文檔服務(wù)的調(diào)用中被使用。 這是從Google接收回叫響應(yīng)的doGet方法:
Google GoogleOAuthHelper用于執(zhí)行填充我們感興趣的兩個(gè)值所需的內(nèi)務(wù)處理任務(wù):
字符串a(chǎn)ccessToken = oauthHelper.getAccessToken(oauthParameters);
字符串a(chǎn)ccessTokenSecret = oauthParameters.getOAuthTokenSecret();
獲得這些值之后,我們便從數(shù)據(jù)存儲(chǔ)區(qū)中重新查詢用戶對(duì)象,并將這些值保存到AppUser.OauthCredentials子類中:
appUser = LoginService.getById(appUser.getId());
appUser = LoginService.updateUserCredentials(appUser, 新的OauthCredentials(accessToken,accessTokenSecret)); req.getSession()。setAttribute(Constant.DOC_SESSION_ID, LoginService.docServiceFactory(appUser));
此外,您還會(huì)看到它們也存儲(chǔ)在會(huì)話中,因此當(dāng)向Google Docs發(fā)出API請(qǐng)求時(shí),我們可以隨時(shí)使用它們。
現(xiàn)在我們有了所需的一切,我們只需將用戶重定向到他們最初請(qǐng)求的資源即可:
RequestDispatcher調(diào)度程序= req.getRequestDispatcher((String)req
.getSession()。getAttribute(Constant.TARGET_URI));
dispatcher.forward(req,resp);
現(xiàn)在,當(dāng)他們?cè)L問列出他們的文檔的JSP頁面時(shí),一切都會(huì)正常!
這是最終產(chǎn)品的截屏演示:
希望您喜歡本教程和演示-期待您的評(píng)論!
繼續(xù)本教程的第二部分 。
參考:來自Jeff's SOA Ruminations博客的JCG合作伙伴 Jeff Davis 對(duì)Google App Engine中的Google Services進(jìn)行身份驗(yàn)證 。
翻譯自: https://www.javacodegeeks.com/2012/06/google-services-authentication-in-app.html
總結(jié)
以上是生活随笔為你收集整理的App Engine中的Google Services身份验证,第1部分的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最强安卓模拟器手机版(最强安卓模拟器)
- 下一篇: 增值税减免税备案期限是多久(增值税减免税