一、HTTP常用方法
- 在客戶端和服務(wù)器之間進行請求-響應(yīng)時,兩種最常被用到的方法是:GET 和 POST瘾杭。
- 瀏覽器演示GET/POST請求的區(qū)別粥烁。
- 控制臺查看服務(wù)器訪問日志,觀察 GET 和 POST 請求的區(qū)別芥永。
1埋涧、GET 請求
-
GET - 從指定的URL獲取資源數(shù)據(jù)。
- GET 請求可被緩存耳标,可以保留在瀏覽器歷史記錄中呼猪,可被收藏為書簽。
- GET 請求從數(shù)學(xué)角度來講谚赎,GET的結(jié)果是 冪等 的
- GET 請求有長度限制,在HTTP協(xié)議定義中视粮,沒有對GET請求的數(shù)據(jù)大小限制,不過因為瀏覽器不同钓觉,一般限制在2~8K。
- GET 請求的所有參數(shù)包裝在URL中础锐,并且服務(wù)器的訪問日志會記錄皆警,不要傳遞敏感信息。
-
名詞解析
- 冪等
- 在數(shù)學(xué)中意推,一個數(shù)多次進行該運算所得的結(jié)果和進行一次該運算所得的結(jié)果是一樣的靡羡,那么我們就稱該運算是冪等的略步。比如絕對值運算就是一個例子绽诚,在實數(shù)集中,有abs(a)=abs(abs(a))蜂桶。
- GET的結(jié)果是 冪等 的,是說對同一個URL請求多次獲得的結(jié)果都是一樣的疆股。
- 冪等
-
GET請求參數(shù)格式
- 在資源路徑末尾添加 ? 表示追加參數(shù)
- 每一個變量及值按照 變量名=變量值 方式設(shè)定,不能包含空格或中文
- 多個參數(shù)使用 & 連接
- URL 字符中如果包含中文,需要添加百分號轉(zhuǎn)義卷雕。
2、POST 請求
- POST - 向指定的資源提交要被處理的數(shù)據(jù)
- POST 請求不會被緩存,不會保留在瀏覽器歷史記錄中,不能被收藏為書簽
- POST 向服務(wù)器發(fā)送數(shù)據(jù),也可以獲得服務(wù)器處理之后的結(jié)果,效率不如GET
- POST 提交數(shù)據(jù)比較大,大小由服務(wù)器的設(shè)定值限制,PHP通常限定2M。
- POST 提交的參數(shù)包裝成二進制的數(shù)據(jù)體,格式與 GET 基本一致,只是不包含 ?
- URL中 只有資源路徑,但不包含參數(shù),服務(wù)器日志不會記錄參數(shù),相對更安全魁蒜。
- 所有設(shè)計用戶隱私的數(shù)據(jù)(密碼,銀行卡號)一定記住使用POST方式傳遞。
3吩翻、GET 緩存
1兜看、提問:為什么 GET 請求可以被緩存,而 POST 請求不被緩存狭瞎?
答:GET 的結(jié)果是冪等的细移,同一個 URL 每次的請求結(jié)果都是一樣的,基于這個特點熊锭,GET 請求就可以被緩存起來避免每次對同一個 URL 請求都要訪問服務(wù)器碗殷,可以節(jié)省用戶流量和提供響應(yīng)速度。而 POST請求的結(jié)果一般都是不一樣的,比如用戶登錄仿吞,每個用戶登錄獲得的信息都是不一樣的,基于這個特點蒿褂,POST 請求的結(jié)果不會被緩存。-
2、GET 緩存實現(xiàn)
-
Request緩存請求頭
- If-None-Match : 與響應(yīng)頭的Etag相對應(yīng)干厚,可以判斷本地緩存數(shù)據(jù)是否發(fā)生變化。
-
如何實現(xiàn) get緩存?
- 請求是可變的蒙谓,緩存策略要每次都從服務(wù)器加載
- 每次得到響應(yīng)后毒租,需要記錄 etag
- 下次發(fā)送請求的同時灾梦,將 etag 一起發(fā)送給服務(wù)器鲫忍,由服務(wù)器比較內(nèi)容有沒有變
代碼實現(xiàn)如下
-
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSURL *url = [NSURL URLWithString:@"[http://localhost/itcast/images/head1.png](http://localhost/itcast/images/head1.png)"];
// NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:15.0];
// 傳遞 etag
if (self.etag.length > 0) {
[request setValue:self.etag forHTTPHeaderField:@"If-None-Match"];
}
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSLog(@"%@,%zd",response,data.length);
// 類型轉(zhuǎn)換(在oc中,如果將父類轉(zhuǎn)換成子類需要強制轉(zhuǎn)換)
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
// 獲取并記錄 etag,區(qū)分大小寫
self.etag = httpResponse.allHeaderFields[@"Etag"];
// 判斷響應(yīng)狀態(tài)碼是否是 304 Not Modified
if (httpResponse.statusCode == 304) {
NSLog(@"加載本地數(shù)據(jù)");
// 如果是,使用本地緩存
// 根據(jù)請求獲得被緩存的響應(yīng)
NSCachedURLResponse *cacheResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
// 拿到緩存的數(shù)據(jù)
data = cacheResponse.data;
}
NSLog(@"etag = %@",self.etag);
self.iconView.image = [UIImage imageWithData:data];
}];
}
- 3肯腕、代碼小結(jié)
- 請求的緩存策略使用NSURLRequestReloadIgnoringCacheData贡茅,忽略本地緩存。
- 服務(wù)器響應(yīng)結(jié)束后,要記錄 Etag,服務(wù)器內(nèi)容和本地緩存對比是否變化的重要依據(jù)搅轿。
- 在發(fā)送請求時,設(shè)置 If-None-Match儿惫,并且傳人Etag澡罚。
- 連接結(jié)束后,要判斷響應(yīng)頭的狀態(tài)碼肾请,如果是304留搔,說明本地緩存內(nèi)容沒有變化。
4铛铁、NSURLCache--設(shè)置緩存
- 在iOS中,可以使用NSURLCache類緩存數(shù)據(jù)
// 設(shè)置網(wǎng)絡(luò)緩存
// 4M 的內(nèi)存緩存
// 20M 的磁盤緩存
// diskPath:緩存路徑 nil:表示使用系統(tǒng)默認(rèn)的緩存路徑:沙盒/Library/Caches
NSURLCache *cache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024 diskCapacity:20 * 1024 * 1024 diskPath:@"pkxing"];
// 設(shè)置全局緩存
[NSURLCache setSharedURLCache:cache];
- iOS 5之前:只支持內(nèi)存緩存隔显。
- 從iOS 5開始:同時支持內(nèi)存緩存和硬盤緩存
# AFNetworking的作者Mattt說:無數(shù)開發(fā)者嘗試自己做一個簡陋而脆弱的系統(tǒng)來實現(xiàn)網(wǎng)絡(luò)緩存功能,殊不知 NSURLCache 只要兩行代碼就能搞定且好上100倍饵逐。
二括眠、用戶登錄
- 安全原則
- 不能在網(wǎng)絡(luò)上傳傳輸用戶隱私數(shù)據(jù)的明文
- 不能在本地存儲用戶隱私數(shù)據(jù)的明文
1、GET登錄
- (void)getLogin{
// 創(chuàng)建url
/**
url擴展:
1倍权、login.php 負(fù)責(zé)登錄的腳本,提示:上課使用的是 php,而工作中不一定,可能是.jsp,asp,aspx...取決于后臺,后臺提供什么,客戶端就用什么掷豺。
2、‘?’ 表示接參數(shù)
3薄声、參數(shù)格式:變量名=值
4当船、‘&’ 多個參數(shù)拼接
*/
NSString urlStr = [NSString stringWithFormat:@"http://localhost/login.php?username=%@&password=%@",self.userName,self.password];
// 如果url 字符串中,包含中文或空格等特殊字符默辨,需要添加百分號轉(zhuǎn)義
urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:urlStr];
// 創(chuàng)建請求
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 請求的默認(rèn)方法就是 GET
// GET 效率高,使用頻率高的訪問 建議是用GET! POST一般用來發(fā)送訂單德频,個人隱私數(shù)據(jù),上傳文件....
NSLog(@"%@",request.HTTPMethod);
// 發(fā)送請求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
NSLog(@"%@,%@",response,dict);
}];
}
-
1.1、URL編碼
-
為什么需要編碼?
- url支持26個英文字母缩幸、數(shù)字和少數(shù)幾個特殊字符,因此,對于url中包含非標(biāo)準(zhǔn)url的字符時,就需要對其進行編碼壹置。
Url編碼通常也被稱為百分號編碼(Url Encoding,also known as percent- encoding),是因為它的編碼方式非常簡單,使用%百分號加上兩位的字 符——0123456789ABCDEF——代表一個字節(jié)的十六進制形式竞思。
Url編碼默認(rèn)使用的字符集是ASCII
-
1.2、如何編碼
NSString urlStr = [NSString stringWithFormat:@"http://localhost/login.php?username=% @&password=%@",self.userName,self.password];
// 如果url 字符串中,包含中文或空格等特殊字符,需要添加百分號轉(zhuǎn)義
urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
- 1.3钞护、GET請求緩存的位置
-
驗證 GET 請求返回的 json 數(shù)據(jù)被緩存起來了盖喷。
- 把請求對象的緩存策略改為:NSURLRequestReturnCacheDataDontLoad。只從緩存加載患亿。再次運行程序看有沒結(jié)果返回传蹈。
- 停止 apache 服務(wù)器:再次運行程序看有沒結(jié)果返回。
- 停止命令:sudo apachectl -k stop;
- 重啟命令:sudo apachectl -k restart;
- 開啟命令:sudo apachectl -k start;
-
GET請求返回的json數(shù)據(jù)緩存在Caches/bundleId/Cache.db的sqlite數(shù)據(jù)庫中步藕。
- 通過終端查看:進入到 Caches.db 所在的文件夾
- 執(zhí)行命令 sqlite3 Cache.db; 打開數(shù)據(jù)庫
- 執(zhí)行命令 .tables; 可查看數(shù)據(jù)庫的表
- 執(zhí)行命令 select * from 表名;可查看對應(yīng)的表數(shù)據(jù),即緩存內(nèi)容
-
友情提示
- 現(xiàn)在只需要知道緩存的位置就行了惦界,后續(xù)的課程會介紹數(shù)據(jù)庫相關(guān)的知識。
-
2咙冗、POST登錄
- 代碼實現(xiàn)
- (void)postLogin{
// 創(chuàng)建url,不需要添加百分號轉(zhuǎn)義(內(nèi)部已經(jīng)實現(xiàn))
NSURL *url = [NSURL URLWithString:@"http://localhost/login.php"];
// 創(chuàng)建請求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 設(shè)置請求方法為 POST
request.HTTPMethod = @"POST";
// 設(shè)置請求體二進制數(shù)據(jù)
NSString *bodyStr = [NSString stringWithFormat:@"username=%@&password=%@",self.userName,self.password];
// request.HTTPBody = [bodyStr dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPBodyStream = [[NSInputStream alloc] initWithData:[bodyStr dataUsingEncoding:NSUTF8StringEncoding]];
// 發(fā)送請求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
NSLog(@"%@,%@",response,dict);
}];
}
-
如何查看請求體的格式沾歪?
- 可以借助火狐瀏覽器和firebug插件。獲得使用瀏覽器登錄時要發(fā)送請求體的格式雾消。
- 火狐瀏覽器幾乎是每一個做網(wǎng)絡(luò)開發(fā)的程序員必備的一款神器灾搏。它有兩個顯著的特點:
- 性能很差,速度很慢立润。
- 插件非常多狂窑。各種調(diào)試插件。
-
設(shè)置請求體注意點
- HTTPBody 和 HTTPBodyStream 只需要設(shè)置其中一個就行了,如果兩個都設(shè)置了,前面設(shè)置的就無效了桑腮。
3泉哈、GET和POST 請求對比
-
URL 對比
-
GET
- login.php 負(fù)責(zé)登錄的腳本。
- 提示:上課使用的是 php,而工作中不一定,可能是.jsp,asp,aspx...取決于后臺,后臺提供什么,客戶端就用什么破讨。
- ‘?’ 表示接參數(shù)丛晦。參數(shù)格式:變量名=值。
- ‘&’表示 多個參數(shù)拼接提陶。
- 包含中文或空格等特殊字符烫沙,需要添加百分號轉(zhuǎn)義。
- login.php 負(fù)責(zé)登錄的腳本。
-
POST
- 只有一個 URL隙笆,不包含參數(shù)锌蓄。
-
-
Request 對比
- GET:默認(rèn)方法就是 GET。
- POST
- 將字符串轉(zhuǎn)換成二進制數(shù)據(jù),設(shè)置HTTPBody或HTTPBodyStream
- 指定 request.HTTPMethod = @"POST"
-
Connnection 對比
- 就是將請求發(fā)送給服務(wù)器撑柔,獲得二進制數(shù)據(jù)的響應(yīng)煤率、 GET和POST沒有區(qū)別。
三乏冀、模擬登錄
1、搭建界面
-
友情提示和細(xì)節(jié)處理
- 在移動應(yīng)用中洋只,絕大多數(shù)的登錄界面不需要記住登錄密碼控件辆沦。
- 在使用自動布局時昼捍,如果一組相關(guān)的控件包在一個大的控件中,只需要對外面大的控件添加布局約束就可以了肢扯。
- 根據(jù)傳統(tǒng)網(wǎng)頁應(yīng)用程序的特點妒茬,用戶按回車鍵時,下一個文本輸入框應(yīng)該獲得焦點蔚晨。
-
實現(xiàn)登錄邏輯
- 連線獲得用戶名和密碼文本輸入框乍钻。
- 監(jiān)聽登錄按鈕的點擊。
- 使用 post 請求登錄
2铭腕、保存登錄信息
- 登錄成功后保存用戶名和密碼银择。
- 程序啟動的時候加載用戶名和密碼。
#define HMUsernameKey @"HMUsernameKey"
#define HMPasswordKey @"HMPasswordKey"
- 保存用戶登錄信息
- (void)saveUserInfo{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:self.nameField.text forKey:HMUsernameKey];
[defaults setObject:self.passField.text forKey:HMPasswordKey];
}
- 加載用戶登錄信息
- (void)loadUserInfo{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
self.nameField.text = [defaults valueForKey:HMUsernameKey];
self.passField.text = [defaults valueForKey:HMPasswordKey];
}
- 友情提示
在 iOS8.0 之后累舷,就不需要同步了浩考。
四、Base64
1被盈、簡介
參考網(wǎng)站:http://zh.wikipedia.org/wiki/Base64
- 是網(wǎng)絡(luò)上使用最廣泛的編碼系統(tǒng)析孽,能夠?qū)⑷魏味M制數(shù)據(jù),轉(zhuǎn)換成只有65個字符組成的文本文件只怎。
- 由 az袜瞬,AZ,0~9身堡,+邓尤,/,= 等65個字符組成盾沫。
- Base64 編碼后的結(jié)果能夠反算裁赠,不夠安全。
- Base64 是所有現(xiàn)代加密算法的基礎(chǔ)算法赴精。
2佩捞、原理
參考文章:http://www.cnblogs.com/hongru/archive/2012/01/14/2321397.html
- base64的編碼都是按字符串長度,以每3個8bit的字符為一組蕾哟,
- 然后針對每組一忱,首先獲取每個字符的ASCII編碼,
- 然后將ASCII編碼轉(zhuǎn)換成8bit的二進制谭确,得到一組3*8=24bit的字節(jié)
- 然后再將這24bit劃分為4個6bit的字節(jié)帘营,并在每個6bit的字節(jié)前面都填兩個高位0,得到4個8bit的字節(jié)
- 然后將這4個8bit的字節(jié)轉(zhuǎn)換成10進制,對照Base64編碼表,得到對應(yīng)編碼后的字符逐哈。
注:如果被編碼字符長度不是3的倍數(shù)的時候芬迄,則都用0代替,對應(yīng)的輸出字符為=
ABC
01000001 01000010 01000011
010000 010100 001001 000011
16 20 9 3
QUJD
AB
01000001 01000010
010000 010100 001000
16 20 8
QUI=
A
010000 010000
16 16
QQ==
Base64對照表
3昂秃、終端命令
# 將 abc.png 進行 base64編碼,生成 xxx.txt 文件
$ base64 abc.png -o xxx.txt // -o 表示輸出
# 將 xxx.txt 解碼生成1.png
$ base64 -D xxx.txt -o 1.png // -D 表示 decoder 解碼
# 將字符串 ABC 進行 base64 編碼
$ echo -n ABC | base64
# 將字符串 QUJD 解碼
$ echo -n QUJD | base64 -D
4禀梳、Base64代碼實現(xiàn)
- 1杜窄、修改代碼
- 保存密碼不能使用明文,使用base64進行加密算途。
- 提交數(shù)據(jù)到服務(wù)器不能使用明文塞耕,使用base64進行加密。
#pragma mark - Base64
// 編碼:A => QQ==
-(NSString *)base64Encode:(NSString *)string{
// 1.將字符串轉(zhuǎn)換成二進制數(shù)據(jù)
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
// 2.利用 ios7.0的方法嘴瓤,直接 base64 編碼
return [data base64EncodedStringWithOptions:0];
}
// 解碼:QQ== => A
- (NSString *)base64Decode:(NSString *)string {
// 1.將 base64編碼后的字符串扫外,解碼成二進制數(shù)據(jù)
// 這里不能使用注釋掉的方法轉(zhuǎn)換成二進制,因為 string 就是已經(jīng)編碼過的字符串
// NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:0];
// 2.返回解碼的字符串
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
五、MD5加密
1廓脆、MD5是什么
Message Digest Algorithm MD5(中文名為消息摘要算法第五版)是計算機安全領(lǐng)域廣泛使用的一種散列函數(shù)(也叫Hash函數(shù))筛谚,用以提供消息的完整性保護。其核心思想是從給定的數(shù)據(jù)中提取特征碼狞贱,不容產(chǎn)生重復(fù)刻获。加密后的字符串通常被稱為指紋或消息摘要。 32位
知識擴展:特征碼有什么作用瞎嬉?
舉例:百度網(wǎng)盤
上傳文件到百度網(wǎng)盤的時候蝎毡,有時候會發(fā)現(xiàn)上傳速度非常快氧枣,幾秒鐘幾個 G 的文件就上傳上去了沐兵,其內(nèi)部實現(xiàn)的原理是根據(jù)給每個文件一個特征碼來實現(xiàn)的,百度服務(wù)器會為每一個上傳的文件生成一個特征碼便监,后續(xù)用戶上傳文件時會檢測網(wǎng)盤里面是否已經(jīng)有相同特征碼的文件扎谎,如果有,就直接拿過來用了烧董,相當(dāng)于引用計數(shù)加一毁靶,如果有人刪除了,則引用計數(shù)減一逊移,知道所有人都刪除了预吆,文件才會真正從百度服務(wù)器刪掉。
2胳泉、MD5算法特點
- 壓縮性:任意長度的數(shù)據(jù)拐叉,算出的MD5值長度都是固定的。相同的字符串扇商,每次MD5后的結(jié)果是固定的凤瘦。都是32個字符
- 容易計算:從原數(shù)據(jù)計算出MD5值很容易。
- 抗修改性:對原數(shù)據(jù)進行任何改動案铺,哪怕只修改1個字節(jié)蔬芥,所得到的MD5值都有很大區(qū)別。
- 弱抗碰撞:已知原數(shù)據(jù)和其MD5值,想找到一個具有相同MD5值的數(shù)據(jù)(即偽造數(shù)據(jù))是非常困難的坝茎。
- 強抗碰撞:想找到兩個不同的數(shù)據(jù)涤姊,使它們具有相同的MD5值,是非常困難的嗤放。
- 不可逆性:不能逆運算。不可破解壁酬。
3次酌、MD5的作用
-
一致性驗證
- 我們都知道,地球上任何人都有自己獨一無二的指紋,這常常成為司法機關(guān)鑒別罪犯身份最值得信賴的方法舆乔。與之類似岳服,通過MD5就可以為任何文件(不管其大小、格式希俩、數(shù)量)產(chǎn)生一個獨一無二的"數(shù)字指紋"吊宋,如果任何人對文件做了任何改動,其MD5值也就是對應(yīng)的"數(shù)字指紋"都會發(fā)生變化颜武。
- 具體來說文件的MD5值就像是這個文件的“數(shù)字指紋”璃搜。每個文件的MD5值是不同的,如果任何人對文件做了任何改動鳞上,其MD5值也就是對應(yīng)的“數(shù)字指紋”就會發(fā)生變化这吻。比如下載服務(wù)器針對一個文件預(yù)先提供一個MD5值,用戶下載完該文件后篙议,用這個算法重新計算下載文件的MD5值唾糯,通過比較這兩個值是否相同,就能判斷下載的文件是否出錯鬼贱,或者說下載的文件是否被篡改了移怯。
- 利用MD5算法來進行文件校驗的方案被大量應(yīng)用到軟件下載站、論壇數(shù)據(jù)庫这难、系統(tǒng)文件安全等方面舟误。
-
數(shù)字簽名
- MD5的典型應(yīng)用是對一段Message(字節(jié)串)產(chǎn)生fingerprint(指紋)以防止被“篡改”。
舉個例子:
你將一段話寫在一個叫readme.txt文件中雁佳,并對這個readme.txt產(chǎn)生一個MD5的值并記錄在案脐帝,然后你可以傳播這個文件給別人,別人如果修改了文件中的任何內(nèi)容糖权,你對這個文件重新計算MD5時就會發(fā)現(xiàn)(兩個MD5值不相同)堵腹。如果再有一個第三方的認(rèn)證機構(gòu),用MD5還可以防止文件作者的“抵賴”,這就是所謂的數(shù)字簽名應(yīng)用星澳。
- MD5的典型應(yīng)用是對一段Message(字節(jié)串)產(chǎn)生fingerprint(指紋)以防止被“篡改”。
-
安全訪問認(rèn)證
- 典型案例:加密用戶登錄密碼疚顷。
當(dāng)用戶登錄的時候,系統(tǒng)把用戶輸入的密碼進行MD5 Hash運算,然后再去和保存在文件系統(tǒng)中的MD5值進行比較腿堤,進而確定輸入的密碼是否正確阀坏。通過這樣的步驟,系統(tǒng)在并不知道用戶密碼的明碼的情況下就可以確定用戶登錄系統(tǒng)的合法性笆檀。這可以避免用戶的密碼被具有系統(tǒng)管理員權(quán)限的用戶知道忌堂。
- 典型案例:加密用戶登錄密碼疚顷。
4、MD5加密實現(xiàn)
- 代碼實現(xiàn)
NSString *password = self.passField.text.md5String; // 執(zhí)行一次 MD5
NSString *password = self.passField.text.md5String.md5String; // 執(zhí)行兩次 MD5
對密碼進行 MD5 加密 - 不安全
-
如何使MD5加密更安全?
- 加鹽酗洒、現(xiàn)在用的比較少士修,前兩年用得比較多。
// 準(zhǔn)備鹽
static NSString *salt = @"fadsfdbvcxweioa4321asfFAFA321DSFASDF%$%$^$^$$#@23123124{}{4";
NSString *password = [self.passField.text stringByAppendingString:salt].md5String;
溫馨提示:‘鹽’在現(xiàn)實生活中是佐料,就是給密碼加點料樱衷,salt要夠咸(復(fù)雜點的字符串)棋嘲。
-
用HMac:HMAC運算利用哈希算法,以一個密鑰和一個消息為輸入矩桂,生成一個消息摘要作為輸出沸移。
NSString *password = [self.passField.text hmacMD5StringWithKey:@"itheima"];
上面代碼 md5 的過程:使用密鑰 itheima 對密碼加密,加密后做md5侄榴,得到32位字符串雹锣,再次使用 itheima 加密,再md5牲蜀。- HMAC現(xiàn)在使用的比較廣泛笆制,安全級別更高, 破解難度高。
- 但還是有風(fēng)險:每次結(jié)果一致涣达,有可能被暴力破解在辆。
要想做到的安全級別更更高,現(xiàn)在密碼學(xué)要求:同樣的算法度苔,同樣的密碼明文匆篓,每次的結(jié)果不一樣。
5寇窑、生成帶時間戳的密碼
使用時間戳,目前使用非常廣泛
- 使用客戶端時間生成帶時間戳的密碼
- (NSString *)timePassword{
// 1.設(shè)置密鑰 key
NSString *key = @"itheima".md5String;
// 2.使用密鑰key對密碼進行HMac
NSString *pwd = [self.passField.text hmacMD5StringWithKey:key];
NSLog(@"key = %@",key);
// 3.獲得當(dāng)前的系統(tǒng)時間
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
// 指定時區(qū)鸦概,真機通常需要指定時區(qū)
fmt.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh"];
// 設(shè)置時間格式
fmt.dateFormat = @"yyyy-MM-dd HH:mm";
// 格式化當(dāng)前時間
NSString *dateStr = [fmt stringFromDate:[NSDate date]];
// 4.用密碼 + 時間 生成 密碼
pwd = [pwd stringByAppendingString:dateStr];
// 5.返回 hmac 結(jié)果
return [pwd hmacMD5StringWithKey:key];
}
NSString *password = [self timePassword];
- 使用服務(wù)器時間,生成帶時間戳的密碼
- (NSString *)timePassword{
// 1.設(shè)置密鑰 key
NSString *key = @"itheima".md5String;
// 2.對密鑰key對密碼進行HMac
NSString *pwd = [self.passField.text hmacMD5StringWithKey:key];
// 3.獲得當(dāng)前服務(wù)器的系統(tǒng)時間
NSURL *url = [NSURL URLWithString:@"http://localhost/hmackey.php"];
// 使用同步獲取時間(注意:這里要使用同步,確定先獲得服務(wù)器的時間,后面的代碼才能執(zhí)行)
NSData *timeData = [NSData dataWithContentsOfURL:url];
// 反序列化取出時間
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:timeData options:0 error:NULL];
NSString *dateStr = dict[@"key"];
// 4.用密碼 + 時間 生成 密碼
pwd = [pwd stringByAppendingString:dateStr];
// 5.返回 hmac 結(jié)果
return [pwd hmacMD5StringWithKey:key];
}
- 問題解答:
- 為什么要獲得服務(wù)器時間來對密碼進行hmac?
有些人是走在時間的前面的,他手機上的時間設(shè)置會比真實的時間的快5分鐘。如果是這樣的,就會導(dǎo)致客戶端獲得的系統(tǒng)時間和服務(wù)器獲得的系統(tǒng)時 間相差幾分鐘甩骏。那就會導(dǎo)致 hmac 的結(jié)果不一致,無法登錄窗市。
- 為什么要獲得服務(wù)器時間來對密碼進行hmac?
6、其他
破解網(wǎng)站:http://www.cmd5.com
六饮笛、鑰匙串訪問
- 使用MD5加密本地密碼有個問題
MD5是不可逆的,本地使用MD5加密密碼保存到偏好設(shè)置的時候,無法再讀取的時候載解析成 原來的文字咨察。而使用 Base64加密又太過簡單了,容易破解。這時候就要使用鑰匙串了福青。
1摄狱、鑰匙串簡介
- 鑰匙串訪問,使用 AES 256加密算法,能夠保證用戶密碼的安全脓诡。
- 鑰匙串訪問SDK,是蘋果ios7.0.3 版本以后發(fā)布的
- 鑰匙串訪問的接口是純C語言的,但是,網(wǎng)絡(luò)上有個哥們把它封裝成 OC的,使用相當(dāng)簡單!
- ? 鑰匙串訪問的第三方框架,是對C框架的封裝,可以不用看源代碼
- 框架地址:https://github.com/samsoffes/sskeychain
2、代碼實現(xiàn)
- 保存用戶登錄信息
- (void)saveUserInfo{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setValue:self.nameField.text forKey:HMUsernameKey];
// 保存到密碼到鑰匙串
/**
參數(shù):
1.密碼‘明文’媒役,加密工作蘋果做了祝谚,使用的是 AES 256 算法
2.服務(wù)名,可以隨便寫酣衷,建議使用bundleId
3.帳號交惯,用戶名。因為鑰匙串訪問中鸥诽,可以保存很多帳號商玫,很多app的密碼
*/
NSString *bundleId = [NSBundle mainBundle].bundleIdentifier;
[SSKeychain setPassword:self.passField.text forService:bundleId account:self.nameField.text];
}
- 加載用戶登錄信息
- (void)loadUserInfo{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
self.nameField.text = [defaults valueForKey:HMUsernameKey];
// 從鑰匙串中獲得密碼
NSString *bundleId = [NSBundle mainBundle].bundleIdentifier;
self.passField.text = [SSKeychain passwordForService:bundleId account:self.nameField.text];
}
- 鑰匙串訪問的密碼保存在哪里?
只有蘋果知道,是為了進一步保障用戶的密碼安全。
七牡借、重構(gòu)登錄代碼
// 登錄成功通知
#define HMLoginSuccessNotification @"HMLoginSuccessNotification"
@interface HMNetworkTools : NSObject
/**
* 全局訪問點,用來獲取單列對象
*/
+(instancetype)sharedTools;
/**
* 登錄
*/
- (void)loginFailed:(void (^)())failed;
/**
* 用戶名
*/
@property(nonatomic,copy) NSString *username;
/**
* 密碼
*/
@property(nonatomic,copy) NSString *pwd;
@end
@implementation HMNetworkTools
/**
* 全局訪問點袭异,用來獲取單列對象
*/
+(instancetype)sharedTools{
static id instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
- (instancetype)init {
if (self = [super init]) {
[self loadUserInfo];
}
return self;
}
/**
* 登錄
*/
- (void)loginFailed:(void (^)())failed{
NSAssert(failed != nil, @"必須傳入回調(diào)");
// 判斷用戶名或密碼是否有值
if(!(self.username.length > 0 && self.pwd.length > 0)){
failed();
return;
}
NSString *password = [self timePassword];
NSLog(@"發(fā)送的密碼=%@",password);
// 創(chuàng)建url
NSURL *url = [NSURL URLWithString:@"http://localhost/loginhmac.php"];
// 創(chuàng)建請求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url ];
// 設(shè)置請求方法為 POST
request.HTTPMethod = @"POST";
// 設(shè)置請求體二進制數(shù)據(jù)
NSString *bodyStr = [NSString stringWithFormat:@"username=%@&password=%@",_username,password];
request.HTTPBodyStream = [[NSInputStream alloc] initWithData:[bodyStr dataUsingEncoding:NSUTF8StringEncoding]];
// 發(fā)送請求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
if([dict[@"userId"] intValue] > 0) {
// 登錄成功钠龙,保存用戶信息
[self saveUserInfo];
// 發(fā)送通知
[[NSNotificationCenter defaultCenter] postNotificationName:HMLoginSuccessNotification object:@"Main"];
} else {
failed();
}
}];
}
#pragma mark - 私有方法
/**
* 生成帶時間戳的密碼
*/
- (NSString *)timePassword{
// 1.設(shè)置密鑰 key
NSString *key = @"itheima".md5String;
// 2.對密鑰key對密碼進行HMac
NSString *pwd = [self.pwd hmacMD5StringWithKey:key];
// 3.獲得當(dāng)前服務(wù)器的系統(tǒng)時間
NSURL *url = [NSURL URLWithString:@"http://localhost/hmackey.php"];
// 使用同步獲取時間
NSData *timeData = [NSData dataWithContentsOfURL:url];
// 反序列化取出時間
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:timeData options:0 error:NULL];
NSString *dateStr = dict[@"key"];
// 4.用密碼 + 時間 生成 密碼
pwd = [pwd stringByAppendingString:dateStr];
// 5.返回 hmac 結(jié)果
return [pwd hmacMD5StringWithKey:key];
}
#pragma mark - 保存和加載用戶信息
#define HMUsernameKey @"HMUsernameKey"
#define HMPasswordKey @"HMPasswordKey"
/**
* 保存用戶登錄信息
*/
- (void)saveUserInfo{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:self.username forKey:HMUsernameKey];
NSString *bundleId = [NSBundle mainBundle].bundleIdentifier;
[SSKeychain setPassword:self.pwd forService:bundleId account:self.username];
}
/**
* 加載用戶登錄信息
*/
- (void)loadUserInfo{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
self.username = [defaults valueForKey:HMUsernameKey];
// 從鑰匙串中獲得密碼
NSString *bundleId = [NSBundle mainBundle].bundleIdentifier;
self.pwd = [SSKeychain passwordForService:bundleId account:self.username];
}