Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android使用Bmob移動後端雲Restful API需要注意的問題

Android使用Bmob移動後端雲Restful API需要注意的問題

編輯:關於Android編程

如果你自己想做一個客戶端玩玩,但是又不想去搭建後台服務器,顯然Bmob移動後端雲是你的最佳選擇。官方地址見bmob,他提供了Android的sdk,同樣也提供了Restful Api,但是個人建議Restful Api還是不適合直接在客戶端使用,畢竟會暴露一下一些key的信息,但是本篇文章就是在android中使用它的restful api,原因嘛很簡單,我想網絡層自己控制,不想用它提供的android sdk,對於安全方面,同樣給出了這種情況的解決方法。

新建應用

 

編寫代碼

我們使用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>    

編寫一個網絡的請求,插入一條數據

實體類

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-----
 +
            MIIGLjCCBRagAwIBAgIDFCb6MA0GCSqGSIb3DQEBCwUAMIGMMQswCQYDVQQGEwJJTDEWMBQGA1UE
 +
            ChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2ln
 +
            bmluZzE4MDYGA1UEAxMvU3RhcnRDb20gQ2xhc3MgMSBQcmltYXJ5IEludGVybWVkaWF0ZSBTZXJ2
 +
            ZXIgQ0EwHhcNMTQxMTA3MDExNzM0WhcNMTUxMTA4MDIxMDQ2WjBJMQswCQYDVQQGEwJDTjEUMBIG
 +
            A1UEAxMLYXBpLmJtb2IuY24xJDAiBgkqhkiG9w0BCQEWFWhlc2hhb3l1ZUBmb3htYWlsLmNvbTCC
 +
            ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANCEvBFYJmhW+8iixdK0zlzwprsuytUGW5BH
 +
            ye9EEkJzGzYfVnEO/v4wC3vEvlWqkwTxY/ydnneH+yo0msAN6IEt6IA+3eO55PAlooAF8b8I2e83
 +
            usRTK4YmooZc/2GYNk2WBXvVlMuWABMKJ/oQMXlM46gffd3Z+evbbptZ5vm+QEWjUlw8fsTALakq
 +
            JgKsrmGSNBVngx1qnm00DL/3yfR2DZHro4CDzRp4toQV3ofcnt6Nz43Z4YkAXZr5gqxge8BZ2n8P
 +
            raQo/5wSfWoPW79Z8lPvZSZv5UIGCUAXdt0qYb3awSDsPSnMrRl03V4XmOK3RDdYDPrWMvii+YrC
 +
            /vUCAwEAAaOCAtkwggLVMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgOoMBMGA1UdJQQMMAoGCCsGAQUF
 +
            BwMBMB0GA1UdDgQWBBR8ztcEh/lE/9fxcga6p7/b+x+pUTAfBgNVHSMEGDAWgBTrQjTQmLCrn/Qb
 +
            awj3zGQu7w4sRTAfBgNVHREEGDAWggthcGkuYm1vYi5jboIHYm1vYi5jbjCCAVYGA1UdIASCAU0w
 +
            ggFJMAgGBmeBDAECATCCATsGCysGAQQBgbU3AQIDMIIBKjAuBggrBgEFBQcCARYiaHR0cDovL3d3
 +
            dy5zdGFydHNzbC5jb20vcG9saWN5LnBkZjCB9wYIKwYBBQUHAgIwgeowJxYgU3RhcnRDb20gQ2Vy
 +
            dGlmaWNhdGlvbiBBdXRob3JpdHkwAwIBARqBvlRoaXMgY2VydGlmaWNhdGUgd2FzIGlzc3VlZCBh
 +
            Y2NvcmRpbmcgdG8gdGhlIENsYXNzIDEgVmFsaWRhdGlvbiByZXF1aXJlbWVudHMgb2YgdGhlIFN0
 +
            YXJ0Q29tIENBIHBvbGljeSwgcmVsaWFuY2Ugb25seSBmb3IgdGhlIGludGVuZGVkIHB1cnBvc2Ug
 +
            aW4gY29tcGxpYW5jZSBvZiB0aGUgcmVseWluZyBwYXJ0eSBvYmxpZ2F0aW9ucy4wNQYDVR0fBC4w
 +
            LDAqoCigJoYkaHR0cDovL2NybC5zdGFydHNzbC5jb20vY3J0MS1jcmwuY3JsMIGOBggrBgEFBQcB
 +
            AQSBgTB/MDkGCCsGAQUFBzABhi1odHRwOi8vb2NzcC5zdGFydHNzbC5jb20vc3ViL2NsYXNzMS9z
 +
            ZXJ2ZXIvY2EwQgYIKwYBBQUHMAKGNmh0dHA6Ly9haWEuc3RhcnRzc2wuY29tL2NlcnRzL3N1Yi5j
 +
            bGFzczEuc2VydmVyLmNhLmNydDAjBgNVHRIEHDAahhhodHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS8w
 +
            DQYJKoZIhvcNAQELBQADggEBAF/t9Bc14BV0OwXcFf4Bs8y+p1AdbMqualCvLzjS95Z9HbPGcbRl
 +
            W76XwaM7iFE1R4mR1lGBQsacbBHOCNeZURYWGAG5c/yqhqCmWCzVJxM88AhCzkEv98uKa3IqE1zY
 +
            lOpYn4cMVqpPgg47QXqUfQlRoh21UTTORgiHEUY+JYNIlIXLoHtHVR0886+pIAq5fFrCwMHF45Df
 +
            r8tuTASazhYJUlOiGQTVv5p8Kg1wJ0ftMs9xJpThcnpEWrngmnNH/8H05rvJ9dEHkpnAU4mL46Bb
 +
            rmQe3oNoGE5EISL9KGVUMeS9wcR2kx+VmGhnAh7kjn5KuEidgfajS3XlcJ5o9t0=
 +
            -----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 

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為懶人提供了很好的後端解決方案,我們幾乎不用寫一句代碼,就可以搭建一個強大的後台服務。

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved