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

來源:互聯網
上載者:User

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

如果你自己想做一個用戶端玩玩,但是又不想去搭建後台伺服器,顯然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(513163535@qq.com) * 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 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);} data-snippet-id=ext.5eb349e2ad3e9fd820357c64c10973e8 data-snippet-saved=false data-csrftoken=JNlvlUwD-f2fRHXXRHfvw_8E266uWldmU0fQ data-codota-status=done>#include 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後台就能看到資料

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

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.