Android使用Bmob移动后端云Restful API需要注意的问题

如果你自己想做一个客户端玩玩,但是又不想去搭建后台服务器,显然Bmob移动后端云是你的最佳选择。官方地址见bmob,文档地址见http://www.bmob.cn/docs。他提供了Android的sdk,同样也提供了Restful Api,但是个人建议Restful Api还是不适合直接在客户端使用,毕竟会暴露一下一些key的信息,但是本篇文章就是在android中使用它的restful api,原因嘛很简单,我想网络层自己控制,不想用它提供的android sdk,对于安全方面,同样给出了这种情况的解决方法。

新建应用

首先你得有个账号,然后你得有个应用,具体内容见http://docs.bmob.cn/restful/faststart/index.html?menukey=fast_start&key=start_restful

编写代码

我们使用OkHttp,还需要用到Gson,增加依赖

    compile ‘com.squareup.okhttp:okhttp:2.5.0‘
    compile ‘com.google.code.gson:gson:2.3.1‘

增加网络访问权限

" data-snippet-id="ext.a07a4cf67b90ad77df941de12b11b97b" data-snippet-saved="false" data-csrftoken="wlzdJGob-PUBlORu9Wxf4eDhkE0NYjNzHse4" data-codota-status="done">    <uses-permission android:name="android.permission.INTERNET"/>

编写一个网络的请求,插入一条数据

实体类

public class Person {
    private String name;
    private String address;
    private int age;

    public Person(String name, String address, int age) {
        this.name = name;
        this.address = address;
        this.age = age;
    }
    public Person() {
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name=‘" + name + ‘\‘‘ +
                ", address=‘" + address + ‘\‘‘ +
                ", age=" + age +
                ‘}‘;
    }
}

进行请求,这部分代码是java平台的,在android上你需要开启一个线程。

private static final String URL_INSERT ="https://api.bmob.cn/1/classes/Person";
private static final String APPLICATION_ID="8dcb9fee2f******14ab19e7dfd9d";
private static final String API_KEY="aebe3b71c9b2***********430ac2de560b1";
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
private static final Gson gson=new Gson();
private static final OkHttpClient client=new OkHttpClient();
Person person=new Person("张三","杭州",20);
RequestBody body = RequestBody.create(JSON, gson.toJson(person));
Request insert=new Request.Builder()
        .url(URL_INSERT)
        .addHeader("Content-Type","application/json")
        .addHeader("X-Bmob-Application-Id", APPLICATION_ID)
        .addHeader("X-Bmob-REST-API-Key",API_KEY)
        .post(body)
        .build();
try {
    Response execute = client.newCall(insert).execute();
    System.out.println(execute.body().string());
} catch (IOException e) {
    e.printStackTrace();
}

注意请求头里面的几个参数,必须设置。

这时候如果你进行请求,你会发现会报一个异常

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

原来是https请求,我们需要获得证书。

当然这时候你有两个选择,一个是信任所有证书。

public class MyX509TrustManager  implements X509TrustManager {

    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    }

    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    }

    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }
}

try {
        // 创建SSLContext对象,并使用我们指定的信任管理器初始化
    TrustManager[] tm = { new MyX509TrustManager() };
    SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
    sslContext.init(null, tm, new java.security.SecureRandom());
    // 从上述SSLContext对象中得到SSLSocketFactory对象
    SSLSocketFactory ssf = sslContext.getSocketFactory();
    client.setSslSocketFactory(ssf);
} catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
} catch (NoSuchProviderException e) {
    e.printStackTrace();
} catch (KeyManagementException e) {
    e.printStackTrace();
}

但是这种方法有安全隐患,我们还是使用证书吧。

首先用Chrome打开https://api.bmob.cn/,然后点击链接左边的锁的图形,切到连接项,点击证书信息,如下图

然后将证书导出即可,之后一直下一步即可。

这里假设导出的证书名字为bmob.cer,将其放到assets目录下。

然后编写一个https验证的工具类。


/**
 * User:lizhangqu([email protected])
 * Date:2015-09-23
 * Time: 17:45
 */
public class SSLUtil {
    //使用命令keytool -printcert -rfc -file srca.cer 导出证书为字符串,然后将字符串转换为输入流,如果使用的是okhttp可以直接使用new Buffer().writeUtf8(s).inputStream()

    /**
     * 返回SSLSocketFactory
     *
     * @param certificates 证书的输入流
     * @return SSLSocketFactory
     */
    public static SSLSocketFactory getSSLSocketFactory(InputStream... certificates) {
        return getSSLSocketFactory(null,certificates);
    }

    /**
     * 双向认证
     * @param keyManagers KeyManager[]
     * @param certificates 证书的输入流
     * @return SSLSocketFactory
     */
    public static SSLSocketFactory getSSLSocketFactory(KeyManager[] keyManagers, InputStream... certificates) {
        try {
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(null);
            int index = 0;
            for (InputStream certificate : certificates) {
                String certificateAlias = Integer.toString(index++);
                keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
                try {
                    if (certificate != null)
                        certificate.close();
                } catch (IOException e) {
                }
            }
            SSLContext sslContext = SSLContext.getInstance("TLS");
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(keyStore);
            sslContext.init(keyManagers, trustManagerFactory.getTrustManagers(), new SecureRandom());
            SSLSocketFactory socketFactory = sslContext.getSocketFactory();
            return socketFactory;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获得双向认证所需的参数
     * @param bks bks证书的输入流
     * @param keystorePass 秘钥
     * @return KeyManager[]对象
     */
    public static KeyManager[] getKeyManagers(InputStream bks, String keystorePass) {
        KeyStore clientKeyStore = null;
        try {
            clientKeyStore = KeyStore.getInstance("BKS");
            clientKeyStore.load(bks, keystorePass.toCharArray());
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(clientKeyStore, keystorePass.toCharArray());
            KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
            return keyManagers;
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (UnrecoverableKeyException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

如果你不想使用文件,则你可以导出证书的内容,使用命令进行导出

keytool -printcert -rfc -file bmob.cer

然后将其赋值给一个字符串

private static final String CERT="-----BEGIN CERTIFICATE-----\n" +
            "MIIGLjCCBRagAwIBAgIDFCb6MA0GCSqGSIb3DQEBCwUAMIGMMQswCQYDVQQGEwJJTDEWMBQGA1UE\n" +
            "ChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2ln\n" +
            "bmluZzE4MDYGA1UEAxMvU3RhcnRDb20gQ2xhc3MgMSBQcmltYXJ5IEludGVybWVkaWF0ZSBTZXJ2\n" +
            "ZXIgQ0EwHhcNMTQxMTA3MDExNzM0WhcNMTUxMTA4MDIxMDQ2WjBJMQswCQYDVQQGEwJDTjEUMBIG\n" +
            "A1UEAxMLYXBpLmJtb2IuY24xJDAiBgkqhkiG9w0BCQEWFWhlc2hhb3l1ZUBmb3htYWlsLmNvbTCC\n" +
            "ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANCEvBFYJmhW+8iixdK0zlzwprsuytUGW5BH\n" +
            "ye9EEkJzGzYfVnEO/v4wC3vEvlWqkwTxY/ydnneH+yo0msAN6IEt6IA+3eO55PAlooAF8b8I2e83\n" +
            "usRTK4YmooZc/2GYNk2WBXvVlMuWABMKJ/oQMXlM46gffd3Z+evbbptZ5vm+QEWjUlw8fsTALakq\n" +
            "JgKsrmGSNBVngx1qnm00DL/3yfR2DZHro4CDzRp4toQV3ofcnt6Nz43Z4YkAXZr5gqxge8BZ2n8P\n" +
            "raQo/5wSfWoPW79Z8lPvZSZv5UIGCUAXdt0qYb3awSDsPSnMrRl03V4XmOK3RDdYDPrWMvii+YrC\n" +
            "/vUCAwEAAaOCAtkwggLVMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgOoMBMGA1UdJQQMMAoGCCsGAQUF\n" +
            "BwMBMB0GA1UdDgQWBBR8ztcEh/lE/9fxcga6p7/b+x+pUTAfBgNVHSMEGDAWgBTrQjTQmLCrn/Qb\n" +
            "awj3zGQu7w4sRTAfBgNVHREEGDAWggthcGkuYm1vYi5jboIHYm1vYi5jbjCCAVYGA1UdIASCAU0w\n" +
            "ggFJMAgGBmeBDAECATCCATsGCysGAQQBgbU3AQIDMIIBKjAuBggrBgEFBQcCARYiaHR0cDovL3d3\n" +
            "dy5zdGFydHNzbC5jb20vcG9saWN5LnBkZjCB9wYIKwYBBQUHAgIwgeowJxYgU3RhcnRDb20gQ2Vy\n" +
            "dGlmaWNhdGlvbiBBdXRob3JpdHkwAwIBARqBvlRoaXMgY2VydGlmaWNhdGUgd2FzIGlzc3VlZCBh\n" +
            "Y2NvcmRpbmcgdG8gdGhlIENsYXNzIDEgVmFsaWRhdGlvbiByZXF1aXJlbWVudHMgb2YgdGhlIFN0\n" +
            "YXJ0Q29tIENBIHBvbGljeSwgcmVsaWFuY2Ugb25seSBmb3IgdGhlIGludGVuZGVkIHB1cnBvc2Ug\n" +
            "aW4gY29tcGxpYW5jZSBvZiB0aGUgcmVseWluZyBwYXJ0eSBvYmxpZ2F0aW9ucy4wNQYDVR0fBC4w\n" +
            "LDAqoCigJoYkaHR0cDovL2NybC5zdGFydHNzbC5jb20vY3J0MS1jcmwuY3JsMIGOBggrBgEFBQcB\n" +
            "AQSBgTB/MDkGCCsGAQUFBzABhi1odHRwOi8vb2NzcC5zdGFydHNzbC5jb20vc3ViL2NsYXNzMS9z\n" +
            "ZXJ2ZXIvY2EwQgYIKwYBBQUHMAKGNmh0dHA6Ly9haWEuc3RhcnRzc2wuY29tL2NlcnRzL3N1Yi5j\n" +
            "bGFzczEuc2VydmVyLmNhLmNydDAjBgNVHRIEHDAahhhodHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS8w\n" +
            "DQYJKoZIhvcNAQELBQADggEBAF/t9Bc14BV0OwXcFf4Bs8y+p1AdbMqualCvLzjS95Z9HbPGcbRl\n" +
            "W76XwaM7iFE1R4mR1lGBQsacbBHOCNeZURYWGAG5c/yqhqCmWCzVJxM88AhCzkEv98uKa3IqE1zY\n" +
            "lOpYn4cMVqpPgg47QXqUfQlRoh21UTTORgiHEUY+JYNIlIXLoHtHVR0886+pIAq5fFrCwMHF45Df\n" +
            "r8tuTASazhYJUlOiGQTVv5p8Kg1wJ0ftMs9xJpThcnpEWrngmnNH/8H05rvJ9dEHkpnAU4mL46Bb\n" +
            "rmQe3oNoGE5EISL9KGVUMeS9wcR2kx+VmGhnAh7kjn5KuEidgfajS3XlcJ5o9t0=\n" +
            "-----END CERTIFICATE-----";

然后使用OkIO里的Buffer类进行读取并设置

SSLSocketFactory sslSocketFactory = SSLUtil.getSSLSocketFactory(new Buffer().writeUtf8(CERT).inputStream());
client.setSslSocketFactory(sslSocketFactory);

当然之前我们已将将其放到assets目录下了,就不用这么麻烦的导出字符串,赋值等操作,我们之间使用该文件即可

try {
    SSLSocketFactory sslSocketFactory = SSLUtil.getSSLSocketFactory(getAssets().open(CERT_FILENAME));
    client.setSslSocketFactory(sslSocketFactory);

} catch (IOException e) {
    e.printStackTrace();
}

这时候你请求一下,就会发现可以成功请求了,并且服务器返回了信息

请求的问题解决了,那么还遗留了一个重要的问题,就是如果使用Restful API,我们的APPLICATION_ID和API_KEY就直接暴露在客户端了。有没有一种方法可以提高一定的安全性呢,方法是有的,只不过只是相对来说安全一点,但是如果人家想搞你,那也是没有办法的,方法就是使用jni获得这两个值,由jni层返回这两个字符串。

Android Studio下ndk的开发环境搭建见Android Studio使用新的Gradle构建工具配置NDK环境,这里不再累赘。

声明java层方法

public class KeyUtil {
    static {
        System.loadLibrary("key");
    }
    public static native String getApplicationId();
    public static native String getAPIKey();
}

使用alt+enter生成jni方法,并在里面返回这两个值


JNIEXPORT jstring JNICALL
Java_cn_edu_zafu_bmobdemo_util_KeyUtil_getApplicationId(JNIEnv *env, jclass instance) {
    char returnValue[]="8dcb9fe*************ab19e7dfd9d";
    return (*env)->NewStringUTF(env, returnValue);
}

JNIEXPORT jstring JNICALL
Java_cn_edu_zafu_bmobdemo_util_KeyUtil_getAPIKey(JNIEnv *env, jclass instance) {
    char returnValue[]="aebe3b71c9b*****************e560b1";
    return (*env)->NewStringUTF(env, returnValue);
}" data-snippet-id="ext.5eb349e2ad3e9fd820357c64c10973e8" data-snippet-saved="false" data-csrftoken="JNlvlUwD-f2fRHXXRHfvw_8E266uWldmU0fQ" data-codota-status="done">#include <jni.h>

JNIEXPORT jstring JNICALL
Java_cn_edu_zafu_bmobdemo_util_KeyUtil_getApplicationId(JNIEnv *env, jclass instance) {
    char returnValue[]="8dcb9fe*************ab19e7dfd9d";
    return (*env)->NewStringUTF(env, returnValue);
}

JNIEXPORT jstring JNICALL
Java_cn_edu_zafu_bmobdemo_util_KeyUtil_getAPIKey(JNIEnv *env, jclass instance) {
    char returnValue[]="aebe3b71c9b*****************e560b1";
    return (*env)->NewStringUTF(env, returnValue);
}

在java层要获得这两个值只要使用对应的静态方法即可。

KeyUtil.getApplicationId();
KeyUtil.getAPIKey();

最终,我们的代码也就成了这样子


public class MainActivity extends AppCompatActivity {
    private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
    private static final String URL ="https://api.bmob.cn/1/classes/Person";
    private static final String CERT_FILENAME ="bmob.cer";
    private static final Gson gson=new Gson();
    private static final OkHttpClient client=new OkHttpClient();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    SSLSocketFactory sslSocketFactory = SSLUtil.getSSLSocketFactory(getAssets().open(CERT_FILENAME));
                    client.setSslSocketFactory(sslSocketFactory);

                } catch (IOException e) {
                    e.printStackTrace();
                }

                Person person=new Person("张三","杭州",20);
                RequestBody body = RequestBody.create(JSON, gson.toJson(person));
                Request insert=new Request.Builder()
                        .url(URL)
                        .addHeader("Content-Type","application/json")
                        .addHeader("X-Bmob-Application-Id", KeyUtil.getApplicationId())
                        .addHeader("X-Bmob-REST-API-Key",KeyUtil.getAPIKey())
                        .post(body)
                        .build();

                client.newCall(insert).enqueue(new Callback() {
                    @Override
                    public void onFailure(Request request, IOException e) {

                    }

                    @Override
                    public void onResponse(Response response) throws IOException {
                        Log.e("TAG",response.body().string());
                    }
                });

            }
        });
    }

}

这种方法只能提高一定的安全性但是不能完全避免,人家只要反汇编你的so就能看到这两个值,你要再加强安全性就只能在jni层进行加密解密等操作了,总之就是不要直接返回字符串。

之后你在Bmob后台就能看到数据

Bmob为懒人提供了很好的后端解决方案,我们几乎不用写一句代码,就可以搭建一个强大的后台服务。

最后放上源码。

http://download.csdn.net/detail/sbsujjbcy/9135981

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-12 20:44:34

Android使用Bmob移动后端云Restful API需要注意的问题的相关文章

Bmob移动后端云服务平台--Android从零开始--(一)何为Bmob

Bmob移动后端云服务平台--Android从零开始--(一)何为Bmob 在正式的项目开发中,单客户端不能满足我们的需求,需要实现客户端与服务端的连接.而在编写Android服务端代码时,常常有很多问题困扰这我们.问题就来了,怎么能通过过简单的方式去实现我们复杂的流程呢?Bmob移动后端服务平台给我们创造了一个很好的后端平台.下面就让我们一起来了解和学习Bmob. 官网地址:http://www.bmob.cn/ Android SDK:http://www.bmob.cn/site/sdk

Bmob移动后端云服务平台--Android从零开始--(三)Bmob数据操作((批量)增删改)

Bmob移动后端云服务平台--Android从零开始--(三)Bmob数据操作((批量)增删改) 上篇文章简单的利用Bmob实现了用户注册与登录效果(http://blog.csdn.net/a123demi/article/details/42059621). 在项目中,对数据的操作脱离不了增删改查.Bmob其实也一样,他对数据操作的增删改查做了很好的封装,以及其批量的处理.本文将重点讲解利用Bmob实现Android端与Bmob服务端的数据操作,实现增加.修改.删除以及他们的批量处理. 源码

Bmob移动后端云服务平台--Android从零开始--(二)android快速入门

Bmob移动后端云服务平台--Android从零开始--(二)android快速入门 上一篇博文我们简单介绍何为Bmob移动后端服务平台,以及其相关功能和优势.本文将利用Bmob快速实现简单例子,进一步了解它的强大之处. 一.准备工作 1.注册Bmob账号 在网址栏输入www.bmob.cn或者在百度输入Bmob进行搜索,打开Bmob官网后,点击右上角的"注册",在跳转页面填入你的姓名.邮箱.设置密码,确认后到你的邮箱激活Bmob账户,你就可以用Bmob轻松开发应用了. 2.网站后台创

Bmob—移动后端云服务平台

对于个人或者小团队来说,开发一个有网络功能的游戏是一件不容易的事情,必须掌握一门诸如Java/.net/php这类的服务器开发语言. Bmob云服务方便了开发者.Bmob可以给应用软件快速添加一个安全灵活的后台管理系统,方便浏览终端保存的各种信息,让开发者们可以不需要关注服务器后端的事情,只需要使用Bmob的Android/iOS/Cocos2d-x/Unity 等SDK就可以实现. 下面我们通过一个简单的实例来了解一下bmob的使用. 1.在Bmob官网上注册一个账号.(Bmob官网:http

android拓展之使用bmob后端云来实现短信验证码

bmob后端云,这个就不用我说了吧,它在我们安卓开发的过程当中非常重要.具体有哪些服务可以bmob的官网来查询 关于怎么实现短信验证码的方式,其实在bmob的后端云里面已经详细的介绍了的.我在这里最主要的是分享一下经验 1.要想实现短信验证码,必须在bmob的官网上下载短信验证码的sdk,然后导入工程 2.使用SDK,这个sdk可以单独使用,直接初始化就可以使用.那什么是单独使用呢?就是直接初始化这个SDK就可以使用,而不用初始化Bmob A.怎么初始化bmob? 通过调用Bmob.initia

android开发之后端云bmob的使用

由于开发的应用需要搭建服务器和数据库,所以了解了一下网上的后端云服务,初步了解之后选择了国内的bmob,下面就来简单介绍一下它的使用: 1.注册Bmob帐号 在网址栏输入www.bmob.cn或者在百度输入Bmob进行搜索,打开Bmob官网后,点击右上角的“注册”,在跳转页面填入你的姓名.邮箱.设置密码,确认后到你的邮箱激活Bmob账户,你就可以用Bmob轻松开发应用了. 2.网站后台创建应用 登录账号进入bmob后台后,点击后台界面左上角“创建应用”,在弹出框输入你应用的名称,然后确认,你就拥

Android实战——第三方服务之Bmob后端云的推送服务的集成和使用(三)

第三方服务之Bmob后端云的推送服务的集成和使用(三) 事先说明:这里的一切操作都是在集成了BmobSDK之后实现的,如果对Bmob还不了解的话,请关注我第一篇Bmob文章 步骤一:推送服务的集成 在app的build.gradle中添加compile依赖文件: 在manifests文件中配置权限: 在manifests文件中添加需要的配置: 创建一个Receiver接受推送消息: 在Activity的onCreate方法中初始化BmobPush: 在Bmob后台管理中设置: 步骤二:推送服务的

基于后端云的Android注册登录开发

APP开发离不开注册登录功能,但是注册登录功能开发需要后台数据库的支持,对于一些初学者或者对后台数据 不熟悉的同学来说可能会有些困难.本文介绍一下后端云: 1. Bmob是国内起步较早的云后端服务平台,提供了云数据库.消息推送.即时通讯.安全验证.移动支付等丰富的 功能服务,且这些服务有个人免费版. Bmob官网:https://www.bmob.cn/ 如何使用Bmob进行开发Bmob的文档写的很清楚,这里就不当搬运工啦. 2.另外,现在介绍一下另一个Android开发者服务平台MOB,MOB

移动后端云平台Bmob介绍

对于移动端的独立开发者来说,最痛苦的事情莫过于搭建后台服务器.没有基础的还得从头学起,有技术的又要搭建维护后台,非常麻烦.而移动后端云平台的出现,简直是每个独立开发者的福音,它可以免费提供论文服务器,有数据文件服务,还有社交功能等.国内主流的有友盟.极光推送,Bmob,AVOS Cloud等,下面介绍老少咸宜的Bmob 如何链接到Bmob的服务器开发文档都有,开发文档有没具体介绍的是文件下载功能.文件下载,首先是通过数据查询获得 BmobFile对象 ,然后通过该对象的getFileUrl()方