最近,业务提出了一个需求,在从主界面跳转到2级界面的时候,如果解析文件有错误,则直接跳转回主界面。
这是一个连ios开发菜鸟一看都可以很快得出答案的问题,很简单啊,用navigation controller 的 popViewControllerAnimated 的方式,直接就可以跳转到之前界面了。
可是,看似简单的需求,却让我这个有几年工作经验的人郁闷了好久。
在进入2级页面的时候,起线程,然后线程返回错误,这个时候需要跳转,调用回到主线程跳转的方法
[selfperformSelectorOnMainThread:@selector(back)withObject:nil
waitUntilDone:YES];
-(void)back{
[self.navigationControllerpopViewControllerAnimated:YES];
}
界面直接跳转出去了。而且二级页面的析构函数也被调用,没有内存问题,故事到这里貌似很完美。等等。
当第二次再打开界面的时候,同样的步骤,却出现了不同的结局。界面代码执行了,界面没有跳转,反而顺利的调用了了析构函数,把界面释放掉了。结果就是显示了一个被释放掉的2级界面。 o m g , 刚开始我一直以为是线程调度的问题,可是研究了好久,发现代码没啥问题哦,于是各种找,把大几千行的俩界面看了个遍,仿佛都没啥问题。这是怎么回事呢?为什么第一次跳转正常,第二次就失败而且导致崩溃呢??
也许很多大神现在该喷我了。呵呵,确实,我忽略了一个关键的问题。
就是下面这个每天都伴随我们的,可是我们却经常视而不见的奇葩函数,-(void) viewDidAppear:(BOOL)animated;
这个函数不用多介绍了,每次进入界面,都会调这个方法。第一次我进入了2级界面之后,在我解析完成之前系统就调用了这个方法,于是我解析错误就直接正常退出了。但是天杀的系统,我再次进入2级界面之后,解析完成的时候,竟然还没有调用-(void) viewDidAppear.导致我直接跳转界面会失败。这个小问题,花费了我个把小时,所以不吐不快。于是,加上了判断,界面随便进入一点问题都没有。
-(void) viewDidAppear:(BOOL)animated{
_viewDidAppear =YES;
if(_parseBookError){
[selfperformSelectorOnMainThread:@selector(back)withObject:nil
waitUntilDone:YES];
}
}
- (void)parseError:(NSString*)msg{
_parseBookError = YES;
if(_viewDidAppear)
[self performSelectorOnMainThread:@selector(back) withObject:nil
waitUntilDone:YES];
}
看来经验主义会害死人啊,我没有想到界面都显示了,-(void) viewDidAppear 还没有正常被调用。以后要注意了。如果能帮到各位,希望各位给我留个好啊。
如果本地的蓝牙设备可以被附近的其它蓝牙设备发现,可以使用下面的方法,代码中有注释。
当然需要你的蓝牙设备设置一下,可以被附近的蓝牙设备检测到(一般为2分钟)
在设置-蓝牙中-选中可检测性复选框。我的数据时android4.1.1,手机厂家不同或版本不同,可能有所不同。
eg:
JAVA:代码:
package com.example.enabling_discoverability_bluetooth; import android.os.Bundle; import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.content.Intent; import android.view.Menu; import android.widget.Toast; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); BluetoothAdapter mBluetoothAdapter = BluetoothAdapter .getDefaultAdapter(); if (mBluetoothAdapter == null) { Toast.makeText(this, "本机没有找到蓝牙硬件或驱动!", Toast.LENGTH_SHORT).show(); finish(); } // 如果允许本地蓝牙被附近的其它蓝牙设备发现,如果没有打开,就先打开蓝牙设备 // 设置让自己的手机被其他蓝牙设备发现 // 如果需要用户确认操作,不需要获取底层蓝牙服务实例,可以通过一个Intent来传递ACTION_REQUEST_DISCOVERABLE参数, // 这里通过startActivityForResult来强制获取一个结果,重写onActivityResult()方法获取执行结果, // 返回结果有RESULT_OK和RESULT_CANCELLED分别代表开启和取消(失败) Intent mIntent = new Intent( BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); startActivityForResult(mIntent, 1); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // TODO Auto-generated method stub super.onActivityResult(requestCode, resultCode, data); if (requestCode == 1) { if (resultCode == RESULT_OK) { Toast.makeText(this, "允许本地蓝牙被附近的其它蓝牙设备发现", Toast.LENGTH_SHORT) .show(); } else if (resultCode == RESULT_CANCELED) { Toast.makeText(this, "不允许蓝牙被附近的其它蓝牙设备发现", Toast.LENGTH_SHORT) .show(); finish(); } } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } }
配置文件:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.enabling_discoverability_bluetooth" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17" /> <uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.example.enabling_discoverability_bluetooth.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
运行结果:
设计HTTP安全的时候,恶意代理是需要考虑到的很重要的一环,尤其在这个“Wifi横行”的年代。一个酒店、商厦中不怀好意的免费Wifi节点,可能就会让用户在使用客户端的过程中,泄漏密码、资金等重要信息。
本文从常见恶意代理的几种攻击方式出发,谈一谈如何在安全设计上避免被恶心代理攻击。
一、报文纂改
这个是攻击力比较小的方式,只要稍有一些安全意识,就容易防范。典型的攻击方式是通过恶意代理截取到一段明文请求,分析请求格式,然后对其中的一些参数进行修改,再向模拟用户向服务器请求。通过这种方式,可以很容易地破坏用户在服务器上的私有数据。比如,用户本意是发送一个删除某条信息的命令,恶意代理把用户发送的命令修改为清除所有信息。
对这种报文纂改的攻击,我们可以通过对请求报文进行摘要即可防范。
二、重放攻击
这个主要是针对服务器的攻击。服务器中总有那么一些API是性能消耗比较大的,或是为了安全,或是业务比较复杂。恶意代理很可能会截取到用户的一个请求之后,向服务器不停地发送该请求,进行“拒绝服务”攻击,如果服务器对请求无法签别,就会影响对正常用户的服务。对于这种重放攻击,我们可以通过在请求中添加随机数验证来设置请求时效性的方式来解决。有几种具体的手段可以借鉴:
1、服务器与客户端在第一次请求时进行一次时间同步,客户请求时随机数取请求的当前时间,服务器根据客户端的时间是否与服务器的时间相差太多来判断是否重放攻击。这种方式很容易实现,但由于客户端与服务器的网络交互存在时间差,时效性会稍差。
2、每次请求服务器在响应时带回下一次请求的随机数,服务器把随机数和正常请求数据一起进行摘要。服务器对每个请求进行验证,如果随机数不正常,则属于重放攻击,判定该请求无效。但这种方式对付比较简单的恶意代理还可以,更强大的恶意代理可以拦截到这个随机数。这种方式最大的问题是无法进行并发请求的验证。
3、客户端和服务器利用共享密钥来为每次请求生成随机数。比如安全硬件等,服务器还可以为每个客户端生成一个对应密钥,客户端在发送请求时利用密钥生成随机数,并在请求中包含自己的AppKey。服务器接收到请求时,根据该客户端的AppKey查询到该客户端的密钥,再利用密钥生成对应的随机数,如果该随机数与客户端发送过来的相同,则请求正常,否则,可判断该请求为重放攻击。这种方式的缺点是一旦客户端的AppSecret被破解,则安全荡然无存。
三、密码破解
这种攻击主要以获取用户的密码等身份认证信息为主。可能有以下几种方式:
1、对HTTPS连接,通过恶意代理对安全证书做替换。如果设计上太多依赖HTTPS安全性,利用HTTP发送的信息不加密的话,很容易被恶意代理截获并分析出用户的原始密码。
2、对于简单的密码摘要信息认证方式,恶意代理可以在拦截到摘要后利用词典进行暴力破解。对于请求中有随机数的摘要,恶意代理可以模拟服务器向客户端响应一个随机数,然后对拦截到的客户端请求进行暴力破解。
恶意代理在无线互联网上的破坏性要远大于传统互联网,而安全问题从来也不是一蹴而就的,我们需要在设计时尽量在安全、便捷、性能之间做更慎重的权衡,从多方面一起下手来保护我们的用户。