为Android游戏接入第三方登录功能
1. “游戏客户端”调用“SDK客户端”的登录功能向“SDK服务端”进行身份认证
2. 验证通过后,“游戏客户端”可得到用户信息,根据游戏逻辑可将用户信息传给“游戏服务器”进行验证
3. “游戏服务器”通过客户端传来的用户信息,一般还需要向“SDK服务器”请求验证用户信息
4. 验证通过后,“游戏服务器”从数据库中查询用户信息,不存在的话直接插入新的用户信息,然后将验证结果和用户信息返回给“游戏客户端”
5. 如果使用了登录会话管理,用户登录后会生成一个新的会话token字符串,把这个token传给“游戏客户端”即可,使用这个token即可查出对应的用户信息
6. 在使用第三方账号登录时,需要为每个第三方账号ID建立一个新的玩家ID,使这两个ID产生关联即可

上图是一个典型的登录过程,关键点是游戏服务端如何做第三方玩家账号与游戏中玩家账号的数据库映射。
客户端示例代码(客户端代码比较简单,可以点此下载完整客户端登录代码):
public void authenticate(final AuthenticationInterface authenticationInterface, final AuthenticationRequest request)
{
// save the request
m_authenticationInterface = authenticationInterface;
m_request = request;
// call third party login method
OppoClientHelper m_oppoSdk = OppoClientHelper.getInstance();
m_oppoSdk.setLoginCallback(new OppoLoginCallback()
{
@Override
public void onSuccess(String userId, String accessToken) {
doAuthenticate(userId, accessToken);
}
@Override
public void onFailure() {
m_authenticationInterface.onAuthenticationCancel();
}
});
m_oppoSdk.sdkLogin();
}
登录服务端采用Java的Servlet技术实现,下面是doPost部分代码。这里主要省略了从数据库中获取用户信息和从第三方SDK获取用户信息的代码。
public class Authenticate extends HttpServlet
{
public static class RequiredParameters{
public String gameId;
public String deviceId;
public String userId;
public String token;
}
public static class OppoUserInfo
{
@JsonIgnoreProperties({ "sex", "profilePictureUrl", "emailStatus", "email" })
public static class BriefUser
{
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String id;
private String name;
}
@JsonProperty("BriefUser")
public BriefUser getBriefUser() {
return briefUser;
}
public void setBriefUser(BriefUser briefUser) {
this.briefUser = briefUser;
}
private BriefUser briefUser;
}
private static final long serialVersionUID = 1L;
private static final String authenticateTypeName = "oppo";
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
// check request parameters
RequiredParameters parameters = new RequiredParameters();
Map<String, String[]> parameterMap = request.getParameterMap();
if (parameterMap.get("userId") == null || parameterMap.get("token") == null){
AuthenticationUtilities.sendError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "internal error");
AuthenticationUtilities.logWarning(authenticateTypeName, "wrong parameters:" + parameterMap.toString());
return;
}
else{
parameters.userId = parameterMap.get("userId").toString();
parameters.token = parameterMap.get("token").toString();
}
try {
// query userInfo from third party SDK
String gcUserInfo = "{{jsonstring}}";
// check returned JSON string from SDK
ObjectMapper mapper = new ObjectMapper();
OppoUserInfo userInfo = mapper.readValue(gcUserInfo, OppoUserInfo.class);
if (!parameters.userId.equals(userInfo.getBriefUser().getId())){
AuthenticationUtilities.sendError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "internal error");
AuthenticationUtilities.logWarning(authenticateTypeName, "verify userId failed:" + parameters.userId.toString());
return;
}
try{
// do with database and get a returned token
String authenticationToken = "";
boolean databaseResult = false;
if (!databaseResult){
AuthenticationUtilities.sendError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "internal_error");
AuthenticationUtilities.logSevere(authenticateTypeName, "database returned R_failure_internal_error.");
return;
}
// if success, send response to client
response.getWriter().print("authenticationToken=" + authenticationToken);
}
catch (final Exception exception){
AuthenticationUtilities.sendError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "internal_error");
AuthenticationUtilities.logSevere(authenticateTypeName, exception.toString());
return;
}
}
catch (Exception e) {
AuthenticationUtilities.sendError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "internal error");
AuthenticationUtilities.logSevere(authenticateTypeName, e.getMessage());
return;
}
}
}
下面是使用到的日志功能代码:
public class AuthenticationUtilities
{
private static final Logger ms_logger = Logger.getLogger("spacetime-authentication");
public static void sendError(final HttpServletResponse response, final int responseCode, final String errorMessage) throws IOException{
response.setStatus(responseCode);
response.getWriter().print(errorMessage);
}
public static void logInfo(final String authenticationTypeName, final String message){
ms_logger.info(authenticationTypeName + ":" + message);
}
public static void logWarning(final String authenticationTypeName, final String message){
ms_logger.warning(authenticationTypeName + ":" + message);
}
public static void logSevere(final String authenticationTypeName, final String message){
ms_logger.severe(authenticationTypeName + ":" + message);
}
}
这里对数据库的操作也很重要,下面是使用postgresql中用到的部分Sql代码。
如果用户第一次登录,还需要在数据库中建立新用户,然后产生会话token。如果可以查到用户信息,直接产生token并返回结果。
CREATE TABLE authentication_token
(
token UUID NOT NULL,
account_id UUID NOT NULL,
authentication_time TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now(),
PRIMARY KEY (token)
);
CREATE INDEX authentication_token_index_account_id ON authentication_token (account_id);
CREATE INDEX authentication_token_index_authentication_time ON authentication_token (authentication_time);
-- --------------------------------------------------------------------
CREATE TABLE auth_account
(
account_id UUID NOT NULL,
creation_time TIMESTAMP DEFAULT now() NOT NULL,
PRIMARY KEY (account_id)
);
ALTER TABLE authentication_token ADD CONSTRAINT authentication_token_account_id_fkey FOREIGN KEY (account_id) REFERENCES auth_account (account_id) ON UPDATE CASCADE ON DELETE CASCADE;
-- ----------------------------------------------------------------------
CREATE TABLE spacetime_account_oppo
(
oppo_account_id ) NOT NULL,
account_id UUID NOT NULL,
creation_time TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now(),
PRIMARY KEY(oppo_account_id)
);
ALTER TABLE spacetime_account_oppo ADD CONSTRAINT spacetime_account_oppo_account_id_fkey FOREIGN KEY (account_id) REFERENCES auth_account (account_id) ON UPDATE CASCADE ON DELETE NO ACTION;
ALTER TABLE spacetime_account_oppo ADD CONSTRAINT spacetime_account_oppo_account_id_unique UNIQUE(account_id);
-- --------------------------------------------------------------------
CREATE OR REPLACE FUNCTION get_new_auth_account_id_0007
(
v_account_id OUT UUID
)
RETURNS UUID
AS $$
BEGIN
<<retry>>
LOOP
v_account_id := public.uuid_generate_v4();
BEGIN
INSERT INTO auth_account (account_id) VALUES (v_account_id);
EXIT retry;
EXCEPTION
WHEN UNIQUE_VIOLATION THEN
END;
END LOOP;
END;
$$
LANGUAGE plpgsql;
-- ----------------------------------------------------------------------
CREATE FUNCTION cleanup_expired_authentication_tokens_0001
(
)
RETURNS VOID
AS $$
BEGIN
DELETE FROM authentication_token WHERE authentication_time < now() - interval '10 minutes';
END;
$$
LANGUAGE plpgsql;
-- --------------------------------------------------------------------
CREATE FUNCTION create_authentication_token_0001
(
v_account_id IN UUID,
v_token OUT UUID
)
RETURNS UUID
AS $$
BEGIN
PERFORM cleanup_expired_authentication_tokens_0001();
v_token := public.uuid_generate_v4();
UPDATE
authentication_token
SET
token = v_token,
authentication_time = now()
WHERE
account_id = v_account_id;
IF NOT FOUND THEN
BEGIN
INSERT INTO authentication_token
(token, account_id)
VALUES
(v_token, v_account_id);
EXCEPTION
WHEN UNIQUE_VIOLATION THEN
UPDATE
authentication_token
SET
token = v_token,
authentication_time = now()
WHERE
account_id = v_account_id;
END;
END IF;
END;
$$
LANGUAGE plpgsql;
-- ----------------------------------------------------------------------
CREATE OR REPLACE FUNCTION get_authentication_token_oppo_0008
(
v_oppoId IN VARCHAR,
v_result OUT INTEGER,
v_token OUT UUID
)
RETURNS RECORD
AS $$
DECLARE
t_account_id UUID;
BEGIN
SELECT account_id INTO t_account_id FROM spacetime_account_oppo WHERE oppo_account_id = v_oppoId;
IF NOT FOUND THEN
t_account_id := get_new_auth_account_id_0007();
BEGIN
INSERT INTO spacetime_account_oppo
(oppo_account_id, account_id)
VALUES
(v_oppoId, t_account_id);
EXCEPTION
WHEN UNIQUE_VIOLATION THEN
BEGIN
RAISE EXCEPTION 'spacetime_account_oppo: oppo_account_id [%] or account_id [%] already exist', v_oppoId, t_account_id;
END;
END;
END IF;
v_result :; -- R_success
v_token := (SELECT f.v_token FROM create_authentication_token_0001(t_account_id) f);
END;
$$
LANGUAGE plpgsql;
-- --------------------------------------------------------------------
为Android游戏接入第三方登录功能的更多相关文章
- Android 实现QQ第三方登录
Android 实现QQ第三方登录 在项目中需要实现QQ第三方登录,经过一番努力算是写出来了,现在总结以下,以防以后遗忘,能帮到其他童鞋就更好了. 首先肯定是去下载SDK和DEMO http://wi ...
- laravel5实现微信第三方登录功能
背景 最近手头一个项目需要实现用户在网站的第三方登录(微信和微博),后端框架laravel5.4. 实现过程以微信网页版第三方登录,其他于此类似,在此不做重复. 准备工作 网站应用微信登录是基于OAu ...
- Android Studio 通过一个登录功能介绍SQLite数据库的使用
前言: SQLite简介:是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中.它是D.RichardHipp建立的公有领域项目.它的设计目标是嵌入式的,而且目前已经在 ...
- 网站如何接入第三方登录,微信登录和QQ登录:注册认证篇
第三方登录平台接入 (QQ\微信登录) QQ登录接入 第一步成为QQ应用开发者,审核期限七天 一.所需材料 1.公司注册相关信息 2.营业执照扫描件 微信登录接入 第一步成为微信开发平台开发者,认证费 ...
- Android应用接入第三方登陆之新浪微博
众所周知,移动互联网在这几年经历了蓬勃发展,到目前为止,移动互联网发展仍然很强劲.其中移动设备系统以android占据主导地位,之前是加拿大的黑莓系统占据主导,但后来随着android系统的问世,黑莓 ...
- Android学习笔记_65_登录功能本身没有任何特别
对于登录功能本身没有任何特别,使用httpclient向服务器post用户名密码即可.但是为了保持登录的状态(在各个Activity之间切换时要让网站知道用户一直是处于登录的状态)就需要进行cooki ...
- .netcore 急速接入第三方登录,不看后悔
新年新气象,趁着新年的喜庆,肝了十来天,终于发了第一版,希望大家喜欢. 如果有不喜欢看文字的童鞋,可以直接看下面的地址体验一下: https://oauthlogin.net/ 前言 此次带来得这个小 ...
- Android 集成支付宝第三方登录
前言: 在集成支付宝支付的时候遇到一点小麻烦,先在此记录供大家参考 1.授权 支付宝第三方登录需要在后台进行授权,在查看授权的时候我们一定要看清楚时候真的已经获得了权限(我在没有获取权限的情况下集成的 ...
- Owin+ASP.NET Identity浅析系列(五)接入第三方登录
在今天,读书有时是件“麻烦”事.它需要你付出时间,付出精力,还要付出一份心境.--仅以<Owin+ASP.NET Identity浅析系列>来祭奠那逝去的…… OK,用户角色实现后,我们回 ...
随机推荐
- cocos2dx开发笔记
1.帧动画:SpriteTest=>SpriteAnimationSplit 2.sourceinsight显示代码行 option->document option->editin ...
- VMware下Ubantu与Windows共享文件夹的方法
刚刚接触linux的同学往往喜欢在windows系统下安装一个虚拟机,然后在虚拟机上进行操作,但是windows和虚拟机上的linux系统之间的文件互传往往不太方便,今天就总结一个小技巧在window ...
- HDU 4746 (莫比乌斯反演) Mophues
这道题看巨巨的题解看了好久,好久.. 本文转自hdu4746(莫比乌斯反演) 题意:给出n, m, p,求有多少对a, b满足gcd(a, b)的素因子个数<=p,(其中1<=a<= ...
- 解决IE6下浮动层固定定位的经典方法
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- android webview js alert对话框 不能弹出 解决办法
在配置了webview的 setting属性后,以前设置的都是可以直接弹出来的,今天写一个小demo时候莫名其妙的发现alert怎么也出来,即使设置了这么多也不行: webSettings.setJa ...
- Java [Leetcode 228]Summary Ranges
题目描述: Given a sorted integer array without duplicates, return the summary of its ranges. For example ...
- linux下useradd -p 添加用户并设定密码
useradd 有个参数-p是设定密码,我试过,不能登录,今天想到这个问题,搜索得:-p 后面跟的是密文,也就是经过加密之后的那个串. 附上网上的破解shadow口令的链接,都是牛人啊.. http: ...
- IPicture、BITMAP、HBITMAP和CBitmap的关系
1.有关IPicture加载图片后直接Render到内存DC的问题(HBITMAP转换IPicture)Picture的方法get_Handle可以直接得到图片的句柄 IPicture *pIPict ...
- DirectX截图黑屏的解决办法
好久没有更新博客了,今天开始继续耕耘. 生活要继续 工作要继续 梦想也一定要继续! 之前写过一篇关于DirectX截屏的文章,其中有网友留言提到了截图黑屏的问题,于是这些日子研究了一下,与大家一同分享 ...
- SpringMVC——注解的使用与结果跳转方式
1.项目结构 2.源代码 package com.zhengbin.controller; import java.io.IOException; import javax.servlet.Servl ...