face api协议分析
来源:互联网 发布:java 天气预报接口 编辑:程序博客网 时间:2024/06/05 06:02
1675194565824091
Account Kit 应用密匙
91f1de0b6fb697a92e6a89761d40dbb3
Account Kit 客户端口令
3fd5c567b4996d4f45ed43cebc535a2a
先有两个大问题,一个是这个线程交互实现的是什么。
另一个是从主线恢复类功能,并尽可能少的使用函数。
登录资料和头像自动填写
批量注册gmail
EMAIL_LOGIN_COMPLETE
event {
{
name EMAIL_LOGIN_COMPLETE
hashcode 1116123282
offset 0
count 20
}
ordinal 3
}
if(contentController instanceof EmailLoginContentController) {
manager2 = intent.getStringExtra(EXTRA_EMAIL);//这里也是获取之前putextra()打包内容
EmailLoginFlowManager manager5 = (EmailLoginFlowManager)AccountKitActivity.this.loginFlowManager;
((ActivityEmailHandler)manager5.getActivityHandler()).onEmailLoginComplete(AccountKitActivity.this, manager5, manager2);
}
public void onEmailLoginComplete(AccountKitActivity activity, EmailLoginFlowManager emailManager, String email) {
activity.pushState(LoginFlowState.SENDING_CODE, (OnPushListener)null);
emailManager.setEmail(email);//这里只是设置参数的
emailManager.logInWithEmail(this.configuration.getResponseType(), this.configuration.getInitialAuthState());
}
参数一是个句柄没啥用,参数二控制流似乎只是控制跳转方式控制的 参数三 请求的email地址这个重要
public ResponseType getResponseType() {
return this.responseType;//responseType; "TOKEN"
}
responseType
value="token"
name="TOKEN"
ordinal=1
public String getInitialAuthState() {
return this.initialAuthState;//null
}
public void logInWithEmail(ResponseType responseType, @Nullable String initialAuthState) {//TOKEN null
if(this.isValid() && this.email != null) {
AccountKitController.logInWithEmail(this.email, responseType.getValue(), initialAuthState);//这三个值分别是取得的email token null
}
}
public static EmailLoginModel logInWithEmail(String email, String responseType, @Nullable String initialAuthState) {
if(getCurrentAccessToken() != null) {
logOut();
}
return initializer.getLoginManager().logInWithEmail(email, responseType, initialAuthState);
}
LoginManager getLoginManager() {
Validate.sdkInitialized();
return this.data.loginManager;
}
static void sdkInitialized() {
if(!AccountKit.isInitialized()) {
throw new AccountKitException(Type.INITIALIZATION_ERROR, InternalAccountKitError.SDK_NOT_INITIALIZED);
}
}
public static boolean isInitialized() {
return AccountKitController.isInitialized();
}
public static boolean isInitialized() {
return initializer.isInitialized();
}
public boolean isInitialized() {
return this.state == Initializer.State.INITIALIZED;//这里检测一下状态 state:"INITIALIZED"
}
这里比较重要了com.facebook.accountkit.internal;final class LoginManager
EmailLoginModelImpl logInWithEmail(@NonNull String email, @NonNull String responseType, @Nullable String initialAuthState) {
Utility.assertUIThread();
this.cancelExisting();
EmailLoginModelImpl loginModel = new EmailLoginModelImpl(email, responseType);//一个类
EmailLoginController loginHandler = new EmailLoginController(this.accessTokenManager, this, loginModel);
loginHandler.logIn(initialAuthState);
this.onLoginStart(loginModel);
this.currentLoginController = loginHandler;
return loginModel;
}
onLoginStart
public void logLoginModel(String eventName, LoginModelImpl loginModel) {
AccountKitController这个类是最开始进入的internal包
然后是LoginManager的loginwithEmail;这里看下初始化的时候参数是啥。。。。。。。。
看了下协议中很多界面交互的和数据传输的设计,跟核心的服务器交互关系不大,关系最紧的是那个graphrequest和graphresponse,但这个应该不是直接调用的关键。我现在想跟踪email的具体执行流,把这个调用request和response的核心类给找出来应该对分析有很大帮助。
accountpreference
appevent
EmailLoginController //目测这个类里面的login函数是登录函数需要动态跟进一下
ExperimentionConfiguration
LoginController
LoginManager
emailLoginController.class
login函数里
((EmailLoginModelImpl)this.loginModel).setInitialAuthState(initialAuthState);
AccountKitGraphRequest graphRequest = this.buildGraphRequest("start_login", parameters);//这里结合上面的赋值初始化了graphRequest
AccountKitGraphRequestAsyncTask.cancelCurrentAsyncTask();
AccountKitGraphRequestAsyncTask task = AccountKitGraphRequest.executeAsync(graphRequest, requestCallback);
AccountKitGraphRequestAsyncTask.setCurrentAsyncTask(task);
下面这个函数通过异步和回调调用,貌似是连接服务器的接受数据的函数,位置AccountKitGraphResponse类
static AccountKitGraphResponse fromHttpConnection(HttpURLConnection connection, AccountKitGraphRequest request) {
InputStream stream = null;
AccountKitGraphResponse var4;
try {
if(connection.getResponseCode() >= 400) {
stream = connection.getErrorStream();
} else {
stream = connection.getInputStream();
}
AccountKitGraphResponse exception = createResponseFromStream(stream, connection, request);
return exception;
} catch (AccountKitException var9) {
ConsoleLogger.log(LoggingBehavior.REQUESTS, "AccountKitGraphResponse", "Response <ERROR>: %s", new Object[]{var9});
var4 = new AccountKitGraphResponse(request, connection, new AccountKitRequestError(var9));
} catch (IOException | SecurityException | JSONException var10) {
ConsoleLogger.log(LoggingBehavior.REQUESTS, "AccountKitGraphResponse", "Response <ERROR>: %s", new Object[]{var10});
var4 = new AccountKitGraphResponse(request, connection, new AccountKitRequestError(new AccountKitException(Type.SERVER_ERROR, var10)));
return var4;
} finally {
Utility.closeQuietly(stream);
}
return var4;
}
AccountKitGraphRequestAsyncTsk类中通过一系列设置,启动异步线程执行数据交互
protected AccountKitGraphResponse doInBackground(Void... params) {
try {
return this.connection == null?this.request.executeAndWait():AccountKitGraphRequest.executeConnectionAndWait(this.connection, this.request);
} catch (Exception var3) {
this.exception = var3;
return null;
}
}
我们这里看到executeConnectionAndWait执行连接是要有一个connect,和request的,但这里是连接后的返回结果,我们需要找到请求的过程。
应该是个回调函数。。。。。。。。。
public interface Callback {
void onCompleted(AccountKitGraphResponse var1);
}
这个是request类里的回调接口。
emailLoginController.class
login函数里
((EmailLoginModelImpl)this.loginModel).setInitialAuthState(initialAuthState);
AccountKitGraphRequest graphRequest = this.buildGraphRequest("start_login", parameters);//这里结合上面的赋值初始化了graphRequest
AccountKitGraphRequestAsyncTask.cancelCurrentAsyncTask();
AccountKitGraphRequestAsyncTask task = AccountKitGraphRequest.executeAsync(graphRequest, requestCallback);
AccountKitGraphRequestAsyncTask.setCurrentAsyncTask(task);
我们有回到了这个登录管理流
buildgraph目测只是实现了初始化各种参数
第二句应该是取消之前执行的异步执行
第三句函数通过这两个参数初始化了类,也没有回调函数的定义
那么我们就要看下这个requestCallback到底是哪里来的了。
一看之下发现,回调函数正是紧挨上面的代码,也在login函数中
Callback requestCallback = new Callback() {
public void onCompleted(AccountKitGraphResponse response) {
LoginManager loginManager = EmailLoginController.this.getLoginManager();
if(loginManager != null) {
try {
if(response.getError() != null) {
Pair result1 = Utility.createErrorFromServerError(response.getError());
EmailLoginController.this.onError((AccountKitError)result1.first);
} else {
JSONObject result = response.getResponseObject();
if(result == null) {
EmailLoginController.this.onError(Type.LOGIN_INVALIDATED, InternalAccountKitError.NO_RESULT_FOUND);
} else {
String privacyPolicy = result.optString("privacy_policy");
if(!Utility.isNullOrEmpty(privacyPolicy)) {
((EmailLoginModelImpl)EmailLoginController.this.loginModel).putField("privacy_policy", privacyPolicy);
}
String termsOfService = result.optString("terms_of_service");
if(!Utility.isNullOrEmpty(termsOfService)) {
((EmailLoginModelImpl)EmailLoginController.this.loginModel).putField("terms_of_service", termsOfService);
}
String expiresInString;
long expiresIn;
try {
boolean e = result.getBoolean("can_attempt_seamless_login");
expiresInString = result.getString("expires_at");
expiresIn = Long.parseLong(expiresInString) * 1000L;
if(e && expiresIn > System.currentTimeMillis()) {
((EmailLoginModelImpl)EmailLoginController.this.loginModel).setStatus(LoginStatus.ACCOUNT_VERIFIED);
return;
}
} catch (JSONException var17) {
;
}
try {
String e1 = result.getString("login_request_code");
((EmailLoginModelImpl)EmailLoginController.this.loginModel).setLoginCode(e1);
expiresInString = result.getString("expires_in_sec");
expiresIn = Long.parseLong(expiresInString);
((EmailLoginModelImpl)EmailLoginController.this.loginModel).setExpiresInSeconds(expiresIn);
String intervalSecondsString = result.getString("interval_sec");
int intervalSeconds = Integer.parseInt(intervalSecondsString);
((EmailLoginModelImpl)EmailLoginController.this.loginModel).setInterval(intervalSeconds);
((EmailLoginModelImpl)EmailLoginController.this.loginModel).setStatus(LoginStatus.PENDING);
loginManager.handle(EmailLoginController.this.loginModel);
} catch (NumberFormatException | JSONException var16) {
EmailLoginController.this.onError(Type.LOGIN_INVALIDATED, InternalAccountKitError.INVALID_GRAPH_RESULTS_FORMAT);
}
}
}
} finally {
EmailLoginController.this.broadcastLoginStateChange();
}
}
}
};
回调函数中有个关键参数AccountKitGraphResponse response。这个应该包含了对服务器的请求过程,应为整个回调函数中只是对相应的各种判断。
回调函数的返回值Callback requestCallback恰巧是AccountKitGraphRequestAsyncTask task = AccountKitGraphRequest.executeAsync(graphRequest, requestCallback);的第二个参数,上面我们知道第一个参数仅仅包含一些参数的初始化。
这里又到了异步请求的类,这里的参数result会被引用到callback回调函数作为参数,需要找到
protected void onPostExecute(AccountKitGraphResponse result)
根据异步任务调用原则,我们的onPostExecute在执行的参数将会是DoInBackgroud的返回值。如下:
protected AccountKitGraphResponse doInBackground(Void... params) {
try {
return this.connection == null?this.request.executeAndWait():AccountKitGraphRequest.executeConnectionAndWait(this.connection, this.request);//这里这个三目运算符比较奇怪,按照定义,条件一为空则代表条件一为0.我们总是会执行条件三的表达式,哪只写一个表达式不就行了
} catch (Exception var3) {
this.exception = var3;
return null;
}
}
正常情况下不会返回空。
AccountKitGraphResponse executeAndWait() {
HttpURLConnection connection;
connection = toHttpConnection(this);//参数初始化
AccountKitGraphResponse response = executeConnectionAndWait(connection, this);//启动请求
return response;
}
static HttpURLConnection toHttpConnection(AccountKitGraphRequest request) {
URL url;
String connection = request.getUrlForSingleRequest();
url = new URL(connection);
HttpURLConnection connection1 = createConnection(url);
serializeToUrlConnection(request, connection1);//请求参数初始化
return connection1;
}
static AccountKitGraphResponse executeConnectionAndWait(HttpURLConnection connection, AccountKitGraphRequest request) {
AccountKitGraphResponse response = AccountKitGraphResponse.fromHttpConnection(connection, request);//启动请求
Utility.disconnectQuietly(connection);
return response;
}
static AccountKitGraphResponse fromHttpConnection(HttpURLConnection connection, AccountKitGraphRequest request) {
InputStream stream = null;
AccountKitGraphResponse var4;
try {
if(connection.getResponseCode() >= 400) {
stream = connection.getErrorStream();
} else {
stream = connection.getInputStream();//这里是请求的关键,连接到了服务器得到流
}
AccountKitGraphResponse exception = createResponseFromStream(stream, connection, request);//创建了响应流
return exception;
} finally {
Utility.closeQuietly(stream);
}
return var4;
}
这里比较可疑
stream = connection.getInputStream();
这个HttpURLConnection connection是个java.net包里的函数。所以貌似可疑伪造
http://blog.csdn.net/it_oracle/article/details/7076636
private static void serializeToUrlConnection(AccountKitGraphRequest request, HttpURLConnection connection) throws IOException, JSONException {
ConsoleLogger consoleLogger = new ConsoleLogger(LoggingBehavior.REQUESTS, "Request");
HttpMethod connectionHttpMethod = request.httpMethod;
connection.setRequestMethod(connectionHttpMethod.name());
boolean isMultipart = isMultiPart(request.parameters);
setConnectionContentType(connection, isMultipart);
URL url = connection.getURL();
我们看到所有的参数都在控制范围内,但有一个参数例外
request.parameter
这个参数我分析了很久
他经过了很多复杂的查询和赋值,本来我想尽量少的改动源代码,可惜。这个赋值涉及到很多android源码的加密解密,和类操作,根本抠不出来。
当然不计代价,肯定能实现,但这里我不会这么做。
我们动态跟踪下
parameter里面有很多成员,我们把关于android系统相关的元素忽略。然后可以看到有个,mMap如下:
"fb_app_events_enabled" -> "false"
"response_type" -> "token"
"credentials_type" -> "email"
"redirect_uri" -> "ak964234977027033://authorize"
"email" -> "inquisiter@163.com"
"locale" -> "zh_CN"
"sdk" -> "android"
"access_token" -> "AA|964234977027033|799de49b357b77afede08488ced2f721"
"logging_ref" -> "2fc145f1-1137-43ee-a1ae-1cc945ccc5a4"
"fields" -> "terms_of_service,privacy_policy"
这里有很多有实际意义的参数
但我没只关心email的值,其他的没有必要动态生成,直接设置成固定参数
所以思路来了
URL=https://graph.accountkit.com/v1.2/start_login
isMultipart=false
private String getUrlForSingleRequest() throws MalformedURLException {
URL builder = new URL("https://graph.accountkit.com");//这里对URL进行编码
Matcher matcher = versionPattern.matcher(this.graphPath);
/*if(!matcher.matches()) {
builder.appendPath(this.version);
}
builder.appendPath(this.graphPath);
//this.addCommonParameters();
if(this.httpMethod != HttpMethod.POST) {
this.appendQueryParametersToUri(builder);
}
*/
return builder.toString();
}
我们发现toHttpConnection调用getUrlForSingleRequest
static HttpURLConnection toHttpConnection(AccountKitGraphRequest request) {
URL url;
try {
String connection = request.getUrlForSingleRequest();
url = new URL(connection);
} catch (MalformedURLException var6) {
throw new AccountKitException(Type.INTERNAL_ERROR, InternalAccountKitError.CANNOT_CONSTRUCT_URL, var6);
}
try {
HttpURLConnection connection1 = createConnection(url);
serializeToUrlConnection(request, connection1);
return connection1;
} catch (UnknownHostException var4) {
throw new AccountKitException(Type.NETWORK_CONNECTION_ERROR, InternalAccountKitError.NO_NETWORK_CONNECTION);
} catch (JSONException | IOException var5) {
throw new AccountKitException(Type.INTERNAL_ERROR, InternalAccountKitError.CANNOT_CONSTRUCT_MESSAGE_BODY, var5);
}
}
这一段有很多的请求参数
而getUrlsingleRequest明显是用来获取服务器相应目录资源的。这里说实话动态构造这个比较麻烦。
我们根据多方的分析这里应该是请求参数构造的关键
不过没有必要非按这个还原,因为确实比较麻烦。我们的思路是构建url连接就可以了,把关键参数抠出来就行了。
唯一要确定的是有没有什么序列化的对象呗传输到接口中了。
所以我把重点还是要放在serializeToUrlConnection这个函数上。
最终我们得到的核心交互位于这里
AccountKitGraphResponse executeAndWait() {
HttpURLConnection connection;
connection = toHttpConnection(this);//参数初始化,这里可能有序列化的东西
AccountKitGraphResponse response = executeConnectionAndWait(connection, this);//启动请求并返回数据,这里可以参看我们的请求参数到底有些什么
return response;
}
toHttpConnection--》serializeToUrlConnection--》connection.getOutputStream();
executeConnectionAndWait---》fromHttpConnection--》connection.getInputStream();
这里其实就圆满了。我们把这个动态跟踪并伪造相应的结构发出请求。大功告成。
如果有序列化的东西,我们需要寻找OutputStream outputStream()对象的write函数,应该在getoutstream()附近。
try {
OutputStream outputStream1 = connection.getOutputStream();
outputStream = new BufferedOutputStream(outputStream1);
if(!isMultipart) {
outputStream = new GZIPOutputStream((OutputStream)outputStream);
}
processRequest(request, (OutputStream)outputStream, isMultipart);
然而我们只找到个processRequest,
private static void processRequest(AccountKitGraphRequest request, OutputStream outputStream, boolean isMultipart) throws IOException {
AccountKitGraphRequest.Serializer serializer = new AccountKitGraphRequest.Serializer(outputStream, !isMultipart);
serializeParameters(request.parameters, serializer);
if(request.requestObject != null) {
processRequestObject(request.requestObject, serializer);
}
}
private static class Serializer implements AccountKitGraphRequest.KeyValueSerializer {
private boolean firstWrite = true;
private final OutputStream outputStream;
private boolean useUrlEncode = false;
Serializer(OutputStream outputStream, boolean useUrlEncode) {
this.outputStream = outputStream;
this.useUrlEncode = useUrlEncode;
}
这里有个接口Serializers实现,服了。
writeObject(String key, Object value)
writeString(String key, String value)
writeBitmap(String key, Bitmap bitmap)
writeBytes(String key, byte[] bytes)
writeContentUri(String key, Uri contentUri, String mimeType)
writeFile(String key, ParcelFileDescriptor descriptor, String mimeType)
这几个接口貌似是实现序列化写入的。
并且不约而同的调用了函数
writeContentDisposition(String name, String filename, String contentType)
而此函数有调用了outputstreaml类的write函数。虽然这其中还有一些小波折,但总体是这个意思。
这里还有两个封装了write的函数
void write(String format, Object... args) throws IOException {
if(!this.useUrlEncode) {
if(this.firstWrite) {
this.outputStream.write("--".getBytes());
this.outputStream.write("3i2ndDfv2rTHiSisAbouNdArYfORhtTPEefj3q2f".getBytes());
this.outputStream.write("\r\n".getBytes());
this.firstWrite = false;
}
this.outputStream.write(String.format(format, args).getBytes());
} else {
this.outputStream.write(URLEncoder.encode(String.format(Locale.US, format, args), "UTF-8").getBytes());
}
}
void writeLine(String format, Object... args) throws IOException {
this.write(format, args);
if(!this.useUrlEncode) {
this.write("\r\n", new Object[0]);
}
}
}
接着分析发现
private static void processRequest(AccountKitGraphRequest request, OutputStream outputStream, boolean isMultipart) throws IOException {
AccountKitGraphRequest.Serializer serializer = new AccountKitGraphRequest.Serializer(outputStream, !isMultipart);
serializeParameters(request.parameters, serializer);
if(request.requestObject != null) {
processRequestObject(request.requestObject, serializer);
}
}
我们的outputstream流会通过serializeParameters把bundle 类 request.parameters参数赋进去,也就是说我们的序列化对象通过serializeParameters传输
还有个参数request.requestObject ,这是一个JSONObject类,我们知道这个协议通信就是基于这东西的。虽然目前我们无法迅速找到这个参数赋值的情况,但我们只需动态调试一下,把我们看到的参数分析一下就行了。
private static void processRequestObject(JSONObject requestObject, AccountKitGraphRequest.KeyValueSerializer serializer) throws IOException {
Iterator keyIterator = requestObject.keys();
while(keyIterator.hasNext()) {
String key = (String)keyIterator.next();
Object value = requestObject.opt(key);
processRequestObjectProperty(key, value, serializer);
}
}
private static void processRequestObjectProperty(String key, Object value, AccountKitGraphRequest.KeyValueSerializer serializer) throws IOException {
Class valueClass = value.getClass();
if(!String.class.isAssignableFrom(valueClass) && !Number.class.isAssignableFrom(valueClass) && !Boolean.class.isAssignableFrom(valueClass)) {
if(Date.class.isAssignableFrom(valueClass)) {
Date date = (Date)value;
SimpleDateFormat iso8601DateFormat = new SimpleDateFormat("yyyy-MM-dd\'T\'HH:mm:ssZ", Locale.US);
serializer.writeString(key, iso8601DateFormat.format(date));
}
} else {
serializer.writeString(key, value.toString());
}
}
- face api协议分析
- Face++ API调用
- face++API使用中的问题
- php调用face++ API使用
- Python2,python3调用face++api
- Face book API,Twitter API 及其调用
- c++实现Face++ API的调用
- 人脸识别- 接入face++api
- Android Google Face API 增强现实教程
- Android Google Face API 增强现实教程
- 使用face++的API接口-手势识别
- Face Detection(OpenCV) Using Hadoop Streaming API
- Face Recognition(face_recognition) Using Hadoop Streaming API
- Python调用Face++人脸检测API
- FACE
- Face++
- Face.com API替代版:Lambda Labs推出开源Face API
- App层face detection init分析
- mysql的sql执行计划
- Java 中的 List,Set 和 Map 的区别
- Leetcode445. 链表相加
- The proxy server is refusing connections – Fix for Firefox Browser
- 日期及时间处理包 Carbon 在 Laravel 中的简单使用
- face api协议分析
- POJ 1065 Wooden Sticks 解题报告-用动态规划方法解决(LIS变式)
- CodeForces 873C(贪心)
- SQL优化准则
- C++——选择排序
- 10小时入门大数据-全面掌握Hadoop开发的核心技能
- Tomcat安装
- 边缘检测
- 111