Precautions for using the Bmob mobile backend cloud Restful API in Android
If you want to create a client for fun, but do not want to build a backend server, it is clear that Bmob mobile backend cloud is your best choice. For the official address, see bmob. He provides Android sdks and Restful APIs. However, it is recommended that Restful APIs be used directly on the client, after all, some key information will be exposed, but this article is about using its restful api in android. The reason is very simple. I want to control it myself at the network layer and do not want to use the android sdk provided by it, this solution is also provided for security.
Create an application
Write code
We use OkHttp and Gson to add dependencies.
compile 'com.squareup.okhttp:okhttp:2.5.0' compile 'com.google.code.gson:gson:2.3.1'
Add Network Access Permissions
data-snippet-id=ext.a07a4cf67b90ad77df941de12b11b97b data-snippet-saved=false data-csrftoken=wlzdJGob-PUBlORu9Wxf4eDhkE0NYjNzHse4 data-codota-status=done>
Write a network request and insert a data entry
Entity class
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 + '}'; }}
Request, this part of the code is on the java platform, you need to enable a thread on 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 ************ abstrac2de560b1; 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 (Zhang San, Hangzhou, 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(insert0000.exe cute (); System. out. println (execute. body (). string ();} catch (IOException e) {e. printStackTrace ();}
Note that the parameters in the Request Header must be set.
At this time, if you make a request, you will find an exception
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
It turns out to be an https request. We need to get a certificate.
Of course, you have two options at this time. One is to trust all certificates.
Public class MyX509TrustManager implements X509TrustManager {public void checkClientTrusted (X509Certificate [] chain, String authType) throws CertificateException {} public void checkServerTrusted) throws CertificateException {} public X509Certificate [] getAcceptedIssuers () {return null ;}} try {// create an SSLContext object, use the specified trust manager to initialize TrustManager [] tm = {new MyX509TrustManager ()}; SSLContext sslContext = SSLContext. getInstance (SSL, SunJSSE); sslContext. init (null, tm, new java. security. secureRandom (); // obtain the SSLSocketFactory object SSLSocketFactory ssf = SSLContext from the sslContext object. getSocketFactory (); client. setSslSocketFactory (ssf);} catch (NoSuchAlgorithmException e) {e. printStackTrace ();} catch (NoSuchProviderException e) {e. printStackTrace ();} catch (KeyManagementException e) {e. printStackTrace ();}
However, this method has security risks. We should use certificates.
Use Chrome to openHttps://api.bmob.cn/Click the lock graph on the left of the link, switch to the connection item, and click the certificate information, as shown in figure
Then export the certificate, and then continue to the next step.
Assume that the exported Certificate Name isBmob. cer, Put it inAssetsDirectory.
Then write a tool class for https verification.
/*** User: lizhangqu (513163535@qq.com) * Date: 2015-09-23 * Time: */public class SSLUtil {// use the command keytool-printcert-rfc-file srca. the cer exports the certificate as a string and converts the string to the input stream. If okhttp is used, you can directly use new Buffer (). writeUtf8 (s ). inputStream ()/*** returns SSLSocketFactory ** @ param certificates certificate input stream * @ return SSLSocketFactory */public static SSLSocketFactory getSSLSocketFactory (InputStream... certificates) {return getSS LSocketFactory (null, certificates );} /*** mutual authentication ** @ param keyManagers KeyManager [] * @ param certificates certificate input stream * @ 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 ;} /*** parameters required for two-way authentication * @ param bks certificate input stream * @ param keystorePass key * @ return KeyManager [] object */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 ;}}
If you do not want to use a file, you can export the certificate content and use commands to export
keytool -printcert -rfc -file bmob.cer
Then assign the value to a string.
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-----;
Then use the Buffer class in OkIO to read and set
SSLSocketFactory sslSocketFactory = SSLUtil.getSSLSocketFactory(new Buffer().writeUtf8(CERT).inputStream());client.setSslSocketFactory(sslSocketFactory);
Of course, we have put it in the assets Directory before, so we don't have to worry about exporting strings, assigning values, and so on. We can use this file between them.
try { SSLSocketFactory sslSocketFactory = SSLUtil.getSSLSocketFactory(getAssets().open(CERT_FILENAME)); client.setSslSocketFactory(sslSocketFactory);} catch (IOException e) { e.printStackTrace();}
At this time, you will find that the request is successful and the server returns the information
After the request problem is solved, an important problem persists, that is, if Restful API is used, our APPLICATION_ID and API_KEY are directly exposed on the client. Is there a way to improve security? There are some methods, but they are relatively safer. But if people want to engage you, there is no way, the method is to use jni to obtain these two values, and the jni layer returns these two strings.
For how to build the development environment of ndk under Android Studio, see Android Studio using the new Gradle build tool to configure the NDK environment, which is no longer cumbersome here.
Declare java Layer Method
public class KeyUtil { static { System.loadLibrary(key); } public static native String getApplicationId(); public static native String getAPIKey();}
Use alt + enter to generate the jni method and return the two values in it
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);}
To obtain these two values at the java layer, you only need to use the corresponding static method.
KeyUtil.getApplicationId();KeyUtil.getAPIKey();
In the end, our code becomes like this.
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 Person = new Person (Zhang San, Hangzhou, 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 ());}});}});}}
This method can only improve security, but cannot be completely avoided. People only need to disassemble your so to see these two values, to enhance security, you can only perform encryption and decryption on the jni layer. In short, do not directly return strings.
Then you can see the data in the Bmob background.
Bmob provides good backend solutions for lazy people. We can build a powerful backend service without writing any code.