app engine_App Engine中的Google Services身份验证,第2部分

app engine

在本教程的第一部分中, 我介绍了如何使用OAuth进行Google API服务的访问/身份验证。 不幸的是,正如我稍后发现的那样,我使用的方法是OAuth 1.0,显然现在Google正式弃用了OAuth 1.0,改用OAuth 2.0版本。 显然,发现这一点让我有些沮丧,并保证我将创建一个新博客条目,其中包含有关如何使用2.0的说明。 好消息是,有了2.0支持,Google添加了一些附加的帮助程序类,这些类使事情变得更加容易,特别是如果您使用的是Google App Engine,这正是我在本教程中使用的。

Google Developers网站现在对如何设置OAuth 2.0进行了很好的描述 。 但是,事实证明如何配置一个真实的示例如何完成它是一个挑战,因此我想记录下我学到的东西。
教程场景在上一个教程中,我创建的项目说明了如何访问用户的Google Docs文件列表。 在本教程中,我做了一些改动,而是使用YouTube的API来显示用户喜欢的视频的列表。 访问用户的收藏夹确实需要使用OAuth进行身份验证,因此这是一个很好的测试。

入门 (可在此处找到本教程的Eclipse项目)。

您必须做的第一件事是遵循Google官方文档中概述的有关使用OAuth 2.0的步骤。 由于我正在创建一个Web应用程序,因此您将需要遵循这些文档中名为“ Web服务器应用程序”的部分。 此外,我之前讨论的用于设置Google App Engine的步骤仍然很重要,因此我将直接进入代码并跳过这些设置步骤。

(注意:可以在这里找到Eclipse项目-我再次选择不使用Maven,以便使那些没有安装Maven或在Maven中了解很多的人变得更简单。)

应用程序流程非常简单(假设是首次使用用户):

  1. 用户访问Web应用程序时(假设您正在使用GAE开发人员模拟器在http:// localhost:8888在本地运行该Web应用程序),则他们必须首先使用其gmail或Google域帐户登录到Google。
  2. 登录后,用户将被重定向到一个简单的JSP页面,该页面具有指向其YouTube最喜欢的视频的链接。
  3. 当点击链接时,servlet将启动OAuth流程以获取对其YouTube帐户的访问权限。 此过程的第一部分将被重定向到Google Page,该页面会提示他们是否要授予应用程序访问权限。
  4. 假设用户回答是肯定的,将显示10个带有链接的收藏夹列表。
  5. 如果他们单击链接,将加载视频。

这是前3页流程的描述:

这是最后两页(假设用户单击了给定的链接):

尽管此示例特定于YouTube,但相同的通用原则适用于访问任何基于Google的云服务,例如Google +,Google Drive,Docs等。它们创建此类集成的关键推动因素显然是OAuth,因此让我们看看如何该过程有效。

OAuth 2.0处理流程

对于刚开始学习该技术的新开发人员而言,使用OAuth可能会有些不知所措。 其背后的主要前提是允许用户有选择地确定他们希望外部应用程序可以访问哪些“私有”资源,例如我们正在为本教程开发的资源。 通过使用OAuth,用户可以避免与第三方共享其登录凭据,而可以简单地向该第三方授予访问其某些信息的权限。

为了实现此功能,需要将用户导航到其私人数据所在的源(在本例中为YouTube)。 然后,他们可以允许或拒绝访问请求。 如果他们允许,则私有数据(YouTube)的源将一次性授权码返回给第三方应用程序。 由于每次需要访问权限时用户都必须授予访问权限是一件很麻烦的事情,因此可以进行另一次通话,将长期使用“一次性使用”授权进行“折衷”。 我们为本教程开发的Web应用程序的总体流程如下所示。

OAuth流程

进行的第一步是确定用户是否已经使用其gmail或Google Domain帐户登录到Google。 尽管不直接与OAuth流程相关联,但使用户能够使用其Google帐户登录非常方便,而不是要求用户登录您的网站。 这是对Google的第一个标注。 然后,一旦登录,应用程序将确定用户是否具有授予OAuth权限的本地帐户设置。 如果他们是第一次登录,则不会。 在这种情况下,将启动OAuth流程。

该过程的第一步是向OAuth提供程序(在本例中为Google YouTube)指定请求访问的“范围”。 由于Google提供了很多服务,因此它们具有很多范围。 您可以使用其OAuth 2.0沙箱轻松确定。

当您启动OAuth流程时,会向他们提供您想要访问的范围,以及Google为您提供的OAuth客户端凭据(这些步骤实际上是支持OAuth的任何提供程序所通用的)。 为了我们的目的,我们正在寻求访问该用户的YouTube帐户的权限,因此Google提供的范围是:https://gdata.youtube.com/。

如果最终用户授予对由范围标识的资源的访问权限,则Google会将授权码发回给应用程序。 这是在servlet中捕获的。 由于返回的代码只是“一次性”代码,因此将其交换为运行时间更长的访问令牌(和相关的刷新令牌)。 上面的步骤由标题为“请求访问和刷新令牌”的活动/框表示。

一旦有了访问令牌,应用程序便可以通过将API调用与令牌一起放置来访问用户的私有数据。 如果一切顺利,API将返回结果。

这不是一个可怕的复杂过程,它只涉及几个步骤。 让我们看一些具体的实现细节,首先从servlet过滤器开始,该过滤器确定用户是否已经登录Google和/或已授予OAuth访问权限。

授权过滤器

让我们看一下AuthorizationFilter的前几行(要了解如何将其配置为过滤器,请参阅web.xml文件)。

public void doFilter(ServletRequest req, ServletResponse res,FilterChain chain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) req;HttpServletResponse response = (HttpServletResponse) res;HttpSession session = request.getSession();if not present, add credential store to servlet contextif (session.getServletContext().getAttribute(Constant.GOOG_CREDENTIAL_STORE) == null) {LOGGER.fine('Adding credential store to context ' + credentialStore);session.getServletContext().setAttribute(Constant.GOOG_CREDENTIAL_STORE, credentialStore);}if google user isn't in session, add itif (session.getAttribute(Constant.AUTH_USER_ID) == null) {LOGGER.fine('Add user to session');UserService userService = UserServiceFactory.getUserService();User user = userService.getCurrentUser();session.setAttribute(Constant.AUTH_USER_ID, user.getUserId());session.setAttribute(Constant.AUTH_USER_NICKNAME, user.getNickname());if not running on app engine prod, hard-code my email address for testingif (SystemProperty.environment.value() == SystemProperty.Environment.Value.Production) {session.setAttribute(Constant.AUTH_USER_EMAIL, user.getEmail());} else {session.setAttribute(Constant.AUTH_USER_EMAIL, 'jeffdavisco@gmail.com');}}

前几行简单地将通用servlet请求和响应转换为它们对应的Http等值-这是必需的,因为我们要访问HTTP会话。 下一步是确定Servlet上下文中是否存在CredentialStore。 正如我们将看到的,它用于存储用户的凭证,因此在后续的servlet中随时可以使用它很方便。 当我们使用以下方法检查会话中是否已经存在该用户时,便开始了此事:

if (session.getAttribute(Constant.AUTH_USER_ID) == null) {

如果没有,我们将使用Google的UserService类获取其Google登录凭据。 这是可供GAE用户使用的帮助程序类,用于获取用户的Google用户ID,电子邮件和昵称。 从UserService获得此信息后,我们将在会话中存储一些用户的详细信息。

到目前为止,我们还没有对OAuth做任何事情,但是在接下来的代码行系列中会有所改变:
尝试{
Utils.getActiveCredential(request,credentialStore); } catch(NoRefreshTokenException e1){ //如果输入了该catch块,则需要执行oauth流程 LOGGER.info('未找到用户–授权URL为:'+ e1.getAuthorizationUrl()); response.sendRedirect(e1.getAuthorizationUrl()); }

大多数OAuth处理都使用一个称为Utils的帮助程序类。 在这种情况下,我们将调用静态方法getActiveCredential()。 稍后我们将看到,如果以前没有为用户捕获OAuth凭据,则此方法将返回NoRefreshTokenException。 作为自定义例外,它将返回URL值,该URL值用于将用户重定向到Google以寻求OAuth批准。

让我们更详细地了解getActiveCredential()方法,因为这是管理许多OAuth处理的地方。

public static Credential getActiveCredential(HttpServletRequest request, CredentialStore credentialStore) throws NoRefreshTokenException {String userId = (String) request.getSession().getAttribute(Constant.AUTH_USER_ID);Credential credential = null;try {if (userId != null) {credential = getStoredCredential(userId, credentialStore);}if ((credential == null || credential.getRefreshToken() == null) && request.getParameter('code') != null) {credential = exchangeCode(request.getParameter('code'));LOGGER.fine('Credential access token is: ' + credential.getAccessToken());if (credential != null) {if (credential.getRefreshToken() != null) {credentialStore.store(userId, credential);}}}if (credential == null || credential.getRefreshToken() == null) {String email = (String) request.getSession().getAttribute(Constant.AUTH_USER_EMAIL);String authorizationUrl = getAuthorizationUrl(email, request);throw new NoRefreshTokenException(authorizationUrl);}} catch (CodeExchangeException e) {e.printStackTrace();} return credential;}

我们要做的第一件事是从会话中获取Google userId(如果没有填充,则无法做到这一点)。 接下来,我们尝试使用Utils静态方法getStoredCredential()从CredentialStore获取用户的OAuth凭据(以相同名称存储在Google类中)。 如果找不到该用户的凭据,则调用名为getAuthorizationUrl()的Utils方法。 如下所示,此方法用于构造浏览器重定向到的URL,该URL用于提示用户授权访问其私有数据(该URL由Google提供,因为它将询问用户批准)。

private static String getAuthorizationUrl(String emailAddress, HttpServletRequest request) {GoogleAuthorizationCodeRequestUrl urlBuilder = null;try {urlBuilder = new GoogleAuthorizationCodeRequestUrl(getClientCredential().getWeb().getClientId(),Constant.OATH_CALLBACK,Constant.SCOPES).setAccessType('offline').setApprovalPrompt('force');} catch (IOException e) {TODO Auto-generated catch blocke.printStackTrace();}urlBuilder.set('state', request.getRequestURI());if (emailAddress != null) {urlBuilder.set('user_id', emailAddress);}return urlBuilder.build();}

如您所见,此方法使用的是GoogleAuthorizationCodeRequestUrl类(来自Google)。 它使用您注册使用OAuth时由Google提供的OAuth客户端凭据来构造HTTP调用(巧合的是,这些凭据存储在名为client_secrets.json的文件中。其他参数包括OAuth请求的范围和URL如果获得用户的批准,该用户将被重定向回。该URL是您在注册Google的OAuth访问时指定的URL:

现在,如果用户已经授予OAuth访问权限,则getActiveCredential()方法将改为从CredentialStore获取凭据。

回到接收OAuth凭证结果的URL(在本例中为http:// localhost:8888 / authSub),您可能想知道Google如何发布到该内部专用地址? 好吧,实际上是用户的浏览器发回了结果,因此在这种情况下,本地主机就可以很好地解决问题。 让我们看一下用于处理此回调的名为OAuth2Callback的servlet(有关如何完成authSub的servlet映射的信息,请参见web.xml)。

public class OAuth2Callback extends HttpServlet {private static final long serialVersionUID = 1L;private final static Logger LOGGER = Logger.getLogger(OAuth2Callback.class.getName());public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {StringBuffer fullUrlBuf = request.getRequestURL();Credential credential = null;if (request.getQueryString() != null) {fullUrlBuf.append('?').append(request.getQueryString());}LOGGER.info('requestURL is: ' + fullUrlBuf);AuthorizationCodeResponseUrl authResponse = new AuthorizationCodeResponseUrl(fullUrlBuf.toString());check for user-denied errorif (authResponse.getError() != null) {LOGGER.info('User-denied access');} else {LOGGER.info('User granted oauth access');String authCode = authResponse.getCode();request.getSession().setAttribute('code', authCode);response.sendRedirect(authResponse.getState());}}}

该课程最重要的收获是:
AuthorizationCodeResponseUrl authResponse =新的AuthorizationCodeResponseUrl(fullUrlBuf.toString()); Google提供了AuthorizationCodeResponseUrl类,以方便分析OAuth请求的结果。 如果该类的getError()方法不为null,则表示用户拒绝了该请求。 如果它为null(表示用户已批准该请求),则使用方法调用getCode()来检索一次性授权码。 此代码值放入用户的会话中,并且在重定向到用户的目标URL(通过过滤器)后调用Utils.getActiveCredential()时,它将使用该授权代码交换长期访问权限,并使用电话:
凭据= exchangeCode((String)request.getSession()。getAttribute('code')); Utils.exchangeCode()方法如下所示:

public static Credential exchangeCode(String authorizationCode)throws CodeExchangeException {try {GoogleTokenResponse response = new GoogleAuthorizationCodeTokenRequest(new NetHttpTransport(), Constant.JSON_FACTORY, Utils.getClientCredential().getWeb().getClientId(), Utils.getClientCredential().getWeb().getClientSecret(),authorizationCode, Constant.OATH_CALLBACK).execute();return Utils.buildEmptyCredential().setFromTokenResponse(response);} catch (IOException e) {e.printStackTrace();throw new CodeExchangeException();}}

此方法还使用称为GoogleAuthorizationCodeTokenRequest的Google类,该类用于调用Google以将一次性OAuth授权代码交换为持续时间较长的访问令牌。
现在,我们已经(终于)获得了YouTube API所需的访问令牌,我们可以向用户显示其视频收藏夹中的10个。

调用YouTube API服务

有了访问令牌,我们现在可以继续向用户显示其收藏夹列表。 为了做到这一点,调用了一个名为FavoritesServlet的servlet。 它将调用YouTube API,通过Jackson将生成的JSON-C格式解析为一些本地Java类,然后将结果发送到JSP页面进行处理。 这是servlet:

public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {LOGGER.fine('Running FavoritesServlet');Credential credential = Utils.getStoredCredential((String) request.getSession().getAttribute(Constant.AUTH_USER_ID), (CredentialStore) request.getSession().getServletContext().getAttribute(Constant.GOOG_CREDENTIAL_STORE));VideoFeed feed = null;if the request fails, it's likely because access token is expired - we'll refreshtry {LOGGER.fine('Using access token: ' + credential.getAccessToken());feed = YouTube.fetchFavs(credential.getAccessToken());} catch (Exception e) {LOGGER.fine('Refreshing credentials');credential.refreshToken();credential = Utils.refreshToken(request, credential);GoogleCredential googleCredential = Utils.refreshCredentials(credential);LOGGER.fine('Using refreshed access token: ' + credential.getAccessToken());retry feed = YouTube.fetchFavs(credential.getAccessToken());}  LOGGER.fine('Video feed results are: ' + feed);request.setAttribute(Constant.VIDEO_FAVS, feed);RequestDispatcher dispatcher = getServletContext().getRequestDispatcher('htmllistVids.jsp');dispatcher.forward(request, response);  }

由于这篇文章主要是关于OAuth流程的,因此我不会过多介绍API调用的放置方式,但是最重要的代码行是:feed = YouTube.fetchFavs(credential.getAccessToken()); feed是VideoFeed的实例。 如您所见,另一个名为YouTube的帮助程序类用于执行繁重的工作。 为了总结,我将展示fetchFavs()方法。

public static VideoFeed fetchFavs(String accessToken) throws IOException, HttpResponseException {HttpTransport transport = new NetHttpTransport();final JsonFactory jsonFactory = new JacksonFactory();HttpRequestFactory factory = transport.createRequestFactory(new HttpRequestInitializer() {@Overridepublic void initialize(HttpRequest request) {set the parserJsonCParser parser = new JsonCParser(jsonFactory);request.addParser(parser);set up the Google headersGoogleHeaders headers = new GoogleHeaders();headers.setApplicationName('YouTube Favorites1.0');headers.gdataVersion = '2';request.setHeaders(headers);}});build the YouTube URLYouTubeUrl url = new YouTubeUrl(Constant.GOOGLE_YOUTUBE_FEED);url.maxResults = 10;url.access_token = accessToken;build the HTTP GET requestHttpRequest request = factory.buildGetRequest(url);HttpResponse response = request.execute();execute the request and the parse video feedVideoFeed feed = response.parseAs(VideoFeed.class);return feed;}

它使用称为HttpRequestFactory的Google类来构造对YouTube的出站HTTP API调用。 由于我们使用的是GAE,因此我们只能使用哪些类来发出此类请求。 请注意以下代码行:
url.access_token = accessToken;
那就是我们使用通过OAuth流程获取的访问令牌的地方。

因此,尽管需要花费大量代码才能使OAuth内容正常运行,但是一旦安装妥当,您就可以准备调用各种Google API服务来进行滚动!

参考:我们的JCG合作伙伴 Jeff Davis在Jeff's SOA Ruminations博客上提供的Google服务认证,第2部分 。


翻译自: https://www.javacodegeeks.com/2012/06/google-services-authentication-in-app_20.html

app engine

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/354158.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Django知识总结(一)

壹 ● 有关http协议 一 ● 全称 超文本传输协议(HyperText Transfer Protocol) 二 ● 协议 双方遵循的规范 http协议是属于应用层的协议(还有ftp, smtp等), 即浏览器请求消息和服务器响应消息的一系列规则 三 ● http协议的特性 http是无状态、无连接的协议(stateless, connect…

mysql+if+x+mod+2_Windows 下 MantisBT 2.X + Apache 2.4 + PHP 7 + MySQL 5.7 的环境配置

Apache 2.4.25~ Visual C Redistributable for Visual Studio 2015PHP 7.1.3~ mod_fcgid-2.3.9Mantisbt-2.2.1MySQL Database 5.7.17~ NET Framework 3.5安装 Apache前期准备1.若计算机已安装了 Apache2.4 以前的版本,请自行卸载并删除安装目录。2.以管理员身份运行…

如何在Java中对文件进行模式匹配和显示相邻行

最近,我们在jOOλ0.9.9中发布了有关超棒的窗口函数支持的文章 ,我相信这是对我们所做的库的最佳补充。 今天,我们将在一个用例中研究窗口函数的出色应用,该用例受到以下堆栈溢出问题Sean Nguyen的启发: 如何从Java 8…

64位内核第二讲,进程保护之对象钩子

64位内核第二讲,进程保护. 一丶什么是保护. 什么是保护. 比如我们安装了xxx杀毒软件.那么此时你用任务管理器关闭.是关闭不了的.原因是内核已经做了保护. 那么去掉保护的前提就是你要给自己的软件做保护. 比如我们给计算器做保护. 例如下图. 做保护.以前的病毒作者.都是想要退出…

jpql hql_无需部署即可测试JPQL / HQL

jpql hql您是否曾经想在不完全部署应用程序的情况下测试JPQL / HQL? 我们今天在这里看到的是适用于任何JPA实现的简单解决方案:Hibernate,OpenJPA,EclipseLink等。 这篇文章中找到的基本源代码来自本书:“ Pro JPA 2&a…

eclipse 代码上传github 笔记

第一步 先share project 如图所示 第二步 如果所示 第三步 点击 下面的create 然后点击完成 第四步提交 第五步: 第六步: 转载于:https://www.cnblogs.com/a8457013/p/8410471.html

Keycloak SSO集成到jBPM和Drools Workbench中

介绍 单一登录(SSO)和相关令牌交换机制正在成为Web上不同环境中进行身份验证和授权的最常见方案,尤其是在迁移到云中时。 本文讨论了Keycloak与jBPM或Drools应用程序的集成,以便使用Keycloak上提供的所有功能。 Keycloak是用于浏…

接口 java性能_接口测试性能测试

接口测试 接口测试是测试系统组件间接口的一种测试,主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换,传递和控制管理过程,以及系统间的相互逻辑依赖关系等。 原理 通过测试程序模拟客户端向服务…

Redis 小结

一、redis简介 redis是一款基于C语言编写的,开源的非关系型数据库,由于其卓越的数据处理机制(按照规则,将常用的部分数据放置缓存,其余数据序列化到硬盘),大家也通常将其当做缓存服务器来使用。…

NetBeans Java EE技巧#1 –数据库中的实体类

NetBeans IDE是开发各种应用程序的绝佳选择。 具体来说,我每天都使用它来开发和维护Java EE应用程序。 在过去的几个发行版中,不仅Java EE的生产力提高了,而且NetBeans IDE还减少了开发应用程序的时间……使Java EE和NetBeans成为绝配&#x…

asp mysql添加数据_ASP:ado.net 实例向数据库添加数据。

我在这使用的是老师给的数据库1,web窗体设计。设计添加图书窗体,窗体属性有图书种类(下拉列表框控件),图书名称,作者,编号,出版社,价格均为文本框,日期(第三方日期控件),…

spring jmx_JMX和Spring –第1部分

spring jmx这是三篇文章的第一篇&#xff0c;这三篇文章将展示如何通过JMX支持为Spring应用程序赋能。 Maven配置 这是用于设置此示例代码的Maven pom.xml&#xff1a; <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/20…

JAVA有percentile函数吗_Python numpy.percentile函数方法的使用

numpy.percentilenumpy.percentile(a, q, axisNone, outNone, overwrite_inputFalse, interpolationlinear, keepdimsFalse) [source]计算沿指定轴的数据的第q个百分位数。返回数组元素的第q个百分点。参数 &#xff1a;a &#xff1a;array_like输入数组或可以转换为数组的…

JS制作日历小事件和数码时钟--JavaScript实例集锦(初学)

运用JS的innerHTML&#xff0c;和for循环实现日历小部件内容和日期的转换。 <!DOCTYPE html> <html> <head><title>日历小部件</title><style type"text/css">*{padding: 0;margin:0;}.tab{width:220px;background: #ccc;height…

Django-认证系统

知识预览 COOKIE 与 SESSION用户认证 回到顶部COOKIE 与 SESSION 概念 cookie不属于http协议范围&#xff0c;由于http协议无法保持状态&#xff0c;但实际情况&#xff0c;我们却又需要“保持状态”&#xff0c;因此cookie就是在这样一个场景下诞生。 cookie的工作原理是&…

使用JBoss Fuse和OpenShift进行Cloud Native Camel骑行

红帽公司最近发布了一个微服务集成工具包&#xff0c;用于在OpenShift v3的Docker和Kubernetes环境中运行我们的微服务。 为了帮助人们更好地理解这一点&#xff0c;我将Rider Auto应用程序迁移到一组微服务中&#xff0c;该应用程序已经存在了一段时间&#xff0c;该应用程序用…

Spark1——介绍

1、Spark是什么 Spark是一个用来实现快速而通用的集群计算的平台。 2、Spark是一个大一统的软件栈 Spark项目包含多个紧密集成的组件。首先Spark的核心是一个对由很多计算任务组成的、运行在多个工作机器或者是一个计算集群上的应用进行调度、分发以及监控的计算引擎。 Spark的…

java按输入顺序输出_java 输入3个数a,b,c,按大小顺序输出的实例讲解

java 输入3个数a,b,c&#xff0c;按大小顺序输出的实例讲解题目&#xff1a;输入3个数a,b,c&#xff0c;按大小顺序输出。代码&#xff1a;import java.util.Scanner;public class lianxi34 {public static void main(String[] args) {Scanner s new Scanner(System.in);Syste…

Java 8中HotSpot选项的改进文档

Oracle的 Java 8 的HotSpot实现中引入的一些小但受欢迎的功能之一是在启动器的文档中添加了许多常见的HotSpot Java启动器 &#xff08; java &#xff09;选项/标志。 过去&#xff0c;即使是对某些相当常见的HotSpot JVM选项也感兴趣的开发人员 &#xff0c;不得不潜在地寻找…

sql server数据库课程设计分析

课题&#xff1a;能源管理收费系统 系统功能的基本要求&#xff1a; &#xff08;1&#xff09;用户基本信息的录入&#xff1a;包括用户的单位、部门、姓名、联系电话、住址 &#xff1b; &#xff08;2&#xff09;用户水、电、气数据的录入&#xff08;每个月的数据的录入&a…