程序员的英文代号
在本教程的第二部分中,我们将介绍Google的登录过程并获取唯一ID。 我们将尝试编写通用代码,以便以后在Facebook登录过程中重用。 但是首先让我们介绍一下``登录''实际上意味着什么...
当您处理自己的用户列表并且用户通过注册登录时,通常可以向该用户询问任何内容。 但是,当用户通过Facebook,Google或任何其他服务登录时,您将无法获得该服务的用户详细信息...这很痛苦,因为此类服务默认情况下甚至在登录时也不提供电子邮件地址有时可以在Facebook中访问它,但仅适用于未选择隐藏它的用户。
更糟糕的是,使用此类服务的主要原因之一是访问联系人....但是,Facebook不再允许开发人员访问您的Facebook朋友。 Facebook应用程序开发人员只能访问已安装该应用程序的朋友列表,并且要实现这一点,我们需要“邀请”人们使用/安装该应用程序。
入门–配置
这篇博客文章中讨论的几乎所有内容都在这里介绍 。 但是,这是一篇相当普通的文章,因此在本教程中,我将尝试更加具体。
首先转到Google开发者控制台: https : //console.developers.google.com/
通过点击创建按钮来创建一个新的应用程序:
只需输入应用程序的名称,例如,在本例中为其SocialChat,然后按“创建”:
现在可以选择API的部分,您应该在其中看到新的项目页面:
在该项目中,您应该点击“社交”部分中的Google+ API:
在凭证部分中,创建一个新的客户端ID,首先我们需要为Web应用程序创建一个。 这将由模拟器使用,因此我们可以在桌面上调试应用程序。 该端口还将用于JavaScript,桌面端口以及除iOS和Android以外的所有端口:
Concent屏幕用于提示用户输入权限,通常应为“现实世界”应用程序正确填充它,但在这种情况下,为简单起见,我们将其大部分留空:
现在,我们需要以与网络应用程序相同的方式添加Android / iOS本机应用程序绑定:
要构建本机Android应用,请确保为您的应用正确设置了密钥库。 如果您没有Android证书,则可以使用我们的可视向导或使用我们的签名教程中提到的命令行。
现在,您已经拥有一个证书,您的应用程序需要两个值,第一个是程序包名称,它必须与主类的程序包名称匹配(一个具有start,stop方法的程序包名称)。 它应该在“代号一个属性”部分列出。 确保使用对您来说100%唯一的名称并使用您自己的域,不要使用com.codename1前缀或类似的名称...
您还需要证书的SHA1值,Google对此进行了解释: https : //developers.google.com/+/mobile/android/getting-started
实际上,您需要运行以下命令行:
$ keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v
这将提示您输入密码,然后打印几行数据,其中一行应以SHA1
开头,这是Android应用程序所需的值。
iOS应用需要与包ID相同的包名称。 它还需要您可以从iTunes获取的应用程序商店ID,它应作为应用程序配置文件的前缀出现。 如果您的应用程序已正确配置,例如通过使用Codename One证书向导,则应在Codename One→iOS部分下的项目属性中看到它。
一切完成后,您应该看到类似以下内容:
项目配置
现在,我们需要在项目中设置一些重要的构建提示,以便其正常运行。 要设置构建提示,只需右键单击项目,选择项目属性,然后在Codename One部分中选择第二个选项卡。 将这些条目添加到表中:
android.includeGPlayServices=true
ios.gplus.clientId=your ios client ID
代码
所以现在所有这些都准备就绪了,我们希望它可以与我们的构建一起使用...
首先,为了使代码更通用,我们将定义可以同时用于Facebook和Google的接口,从而概括登录代码:
static interface UserData {
public String getName();
public String getId();
public String getImage();
public void fetchData(String token, Runnable callback);
}
该接口的工作方式是,我们将在登录过程完成后调用fetchData从Google / Facebook提取数据,然后为我们提供名称/ ID和图像。 请注意,由于Facebook并不总是提供电子邮件,因此我们无法可靠地将其用作用户的唯一ID ...
还要注意,fetchData是异步的,并在完成时将调用回调。 我们可以使其同步并使用invokeAndBlock
。
将其绑定到按钮的代码如下所示:
loginWithGoogle.addActionListener((e) -> {
Login gc = GoogleConnect.getInstance();
gc.setClientId("1013232201263-lf4aib14r7g6mln58v1e36ibhktd79db.apps.googleusercontent.com");
gc.setRedirectURI("https://www.codenameone.com/oauth2callback");
gc.setClientSecret("-------------------");
doLogin(gc, new GoogleData());
});
注意,我们隐藏了客户端的GoogleData
,并使用了GoogleData
类,该类实际上是UserData
接口的实现。 GoogleData
类如下所示:
static class GoogleData extends ConnectionRequest implements UserData {
private Runnable callback;
private Map<String, Object> parsedData;
@Override
public String getName() {
return (String) parsedData.get("displayName");
}
@Override
public String getId() {
java.util.List<Map<String, String>> emailList = (java.util.List<Map<String, String>>) parsedData.get("emails");
return emailList.get(0).get("value").toLowerCase();
}
@Override
public String getImage() {
Map<String, Object> imageMeta = ((Map<String, Object>) parsedData.get("image"));
return (String)imageMeta.get("url");
}
@Override
public void fetchData(String token, Runnable callback) {
this.callback = callback;
addRequestHeader("Authorization", "Bearer " + token);
setUrl("https://www.googleapis.com/plus/v1/people/me");
setPost(false);
NetworkManager.getInstance().addToQueue(this);
}
@Override
protected void readResponse(InputStream input) throws IOException {
JSONParser parser = new JSONParser();
parsedData = parser.parseJSON(new InputStreamReader(input, "UTF-8"));
}
@Override
protected void postResponse() {
callback.run();
}
}
通常,课堂上没有那么多。 fetchData
使用连接请求连接到Google+ API中的URL,该URL在设置令牌后返回有关用户的详细信息。 这些详细信息将作为JSON字符串返回,然后我们对其进行解析并将其设置为正确的变量。
JSONParser
类以列表和映射树的形式返回JSON数据,我们可以遍历该列表和映射以提取所需的数据。
临时观察者可能不知道的一个值是token
,它是一个字符串,其中包含用户帐户的“密钥”。 Facebook / Google等将用户密码散列在数据库中(实际上意味着即使他们也不知道密码),因此为了证明我们已获得用户访问其数据的许可,我们获得了一个唯一的令牌,看起来像一堆乱码,我们可以在访问它们各自的API时使用它来验证自己。 这是一种非常普遍的做法...但是,令牌偶尔会过期,因此您可能需要通过再次登录来``刷新''令牌,如下面的代码块所示。
现在,我们可以进入实际的登录过程,这要归功于Google和Facebook登录,这要归功于上述类和Codename One中的内置抽象:
private String fullName;
private String uniqueId;
private String imageURL;
void doLogin(Login lg, UserData data) {
if(lg.isUserLoggedIn()) {
showContactsForm();
return;
}
// if the user already logged in previously and we have a token
String t = Preferences.get("token", (String)null);
if(t != null) {
// we check the expiration of the token which we previously stored as System time
long tokenExpires = Preferences.get("tokenExpires", (long)-1);
if(tokenExpires < 0 || tokenExpires > System.currentTimeMillis()) {
// we are still logged in
showContactsForm();
return;
}
}
lg.setCallback(new LoginCallback() {
@Override
public void loginFailed(String errorMessage) {
Dialog.show("Error Logging In", "There was an error logging in: " + errorMessage, "OK", null);
}
@Override
public void loginSuccessful() {
// when login is successful we fetch the full data
data.fetchData(lg.getAccessToken().getToken(), ()-> {
// we store the values of result into local variables
uniqueId = data.getId();
fullName = data.getName();
imageURL = data.getImage();
// we then store the data into local cached storage so they will be around when we run the app next time
Preferences.set("fullName", fullName);
Preferences.set("uniqueId", uniqueId);
Preferences.set("imageURL", imageURL);
Preferences.set("token", lg.getAccessToken().getToken());
// token expiration is in seconds from the current time, we convert it to a System.currentTimeMillis value so we can
// reference it in the future to check expiration
Preferences.set("tokenExpires", tokenExpirationInMillis(lg.getAccessToken()));
showContactsForm();
});
}
});
lg.doLogin();
}
现在这是一大段代码,但实际上并没有做那么多。 它所做的全部工作都委托给UserData
接口并验证令牌。 它还存储返回的数据并显示下一个表单(我们现在将不讨论)。
本部分的最后一段代码是一个用于标记过期检测的小型实用程序方法:
/**
* token expiration is in seconds from the current time, we convert it to a System.currentTimeMillis value so we can
* reference it in the future to check expiration
*/
long tokenExpirationInMillis(AccessToken token) {
String expires = token.getExpires();
if(expires != null && expires.length() > 0) {
try {
// when it will expire in seconds
long l = Long.parseLong(expires) * 1000;
return System.currentTimeMillis() + l;
} catch(NumberFormatException err) {
// ignore invalid input
}
}
return -1;
}
这有效地使我们能够检测令牌在应用的未来执行中是否过期。
本系列其他文章
这是一系列持续不断的帖子,包括以下部分:
- 第1部分–初始用户界面
- 第2部分–使用Google登录
- 第3部分–使用Facebook登录(即将推出)
翻译自: https://www.javacodegeeks.com/2015/07/building-a-chat-app-with-codename-one-part-2.html
程序员的英文代号