如果你自己想做一个客户端玩玩,但是又不想去搭建后台服务器,显然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'

增加网络访问权限

    <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/,然后点击链接左边的锁的图形,切到连接项,点击证书信息,如下图

Android使用Bmob移动后端云Restful API需要注意的问题_第1张图片

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

Android使用Bmob移动后端云Restful API需要注意的问题_第2张图片

这里假设导出的证书名字为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

Android使用Bmob移动后端云Restful API需要注意的问题_第3张图片

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

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方法,并在里面返回这两个值

#include <jni.h>JNIEXPORT jstring JNICALLJava_cn_edu_zafu_bmobdemo_util_KeyUtil_getApplicationId(JNIEnv *env, jclass instance) {    char returnValue[]="8dcb9fe*************ab19e7dfd9d";    return (*env)->NewStringUTF(env, returnValue);}JNIEXPORT jstring JNICALLJava_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后台就能看到数据

Android使用Bmob移动后端云Restful API需要注意的问题_第4张图片

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

最后放上源码。
http://download.csdn.net/detail/sbsujjbcy/9135981

更多相关文章

  1. DIY osc android 客户端 之 方法论
  2. Android字符串进阶之三:字体属性及测量(FontMetrics)
  3. Android webview和js互相调用实现方法
  4. 深入解析android log的分析方法(1)
  5. Android增量升级的方法和原理
  6. android ListView常见问题解决方法(滚动背景变黑,去除滑动时阴影,拖

随机推荐

  1. Android 4游戏编程入门经典
  2. Android: Android Bluetooth
  3. Android根据文件路径使用File类获取文件
  4. Android Fresco图片处理库用法API英文原
  5. Android外设存储设备的访问及测试
  6. Android巧用DecorView实现对话框功能
  7. 【Android】如何让跑马灯跑起来-控件请求
  8. 【边做项目边学Android】小白会遇到的问
  9. Android是什么?
  10. 安卓xml文件中设置动画匀速旋转无效?