iOS密碼管理Keychain的使用

keychain(鑰匙串)存儲(chǔ)在iOS系統(tǒng)中,并且恢復(fù)iPhone會(huì)使keychain的內(nèi)容也恢復(fù).但是刪除App是不會(huì)影響keychain.
不同App之間Keychain是不能相互訪問的,但是可以通過配置keychain-sharing來解決。

![keychain(鑰匙串)存儲(chǔ)在iOS系統(tǒng)中,并且恢復(fù)iPhone會(huì)使keychain的內(nèi)容也恢復(fù).但是刪除App是不會(huì)影響keychain.
不同App之間Keychain是不能相互訪問的招盲,但是可以通過配置keychain-sharing來解決制市。

image

如圖,在兩個(gè)項(xiàng)目中配置keychain-sharing,這樣就可以訪問互相的keychain,以此達(dá)到共享數(shù)據(jù)

如何使用

keychain是一個(gè)結(jié)構(gòu),有很多key-value

image
image
image

此外還有kSecReturnData(返回結(jié)果) kSecMatchLimit(查詢策略)等 這兩項(xiàng)是查詢時(shí)必選的

1.首選要構(gòu)建一個(gè)存取條件,這些條件指定了要存取的這條內(nèi)容的存取類型,唯一性,存取策略和內(nèi)容

一共需要五個(gè)key

    NSString *classKey = (__bridge NSString *)kSecClass;
    //指定服務(wù)類型是普通密碼
    NSString *classValue = (__bridge NSString *)kSecClassGenericPassword;
    NSString *accessibleKey = (__bridge NSString *)kSecAttrAccessible;
    //指定安全類型是任何時(shí)候都可以訪問
    NSString *accessibleValue = (__bridge NSString *)kSecAttrAccessibleAlways;
    NSString *accountKey = (__bridge NSString *)kSecAttrAccount;
    //指定服務(wù)的賬戶名 可以與服務(wù)名相同 賬戶名可以對(duì)應(yīng)多個(gè)服務(wù)名
    NSString *accountValue = accountId;
    NSString *serviceKey = (__bridge NSString *)kSecAttrService;
    //指定服務(wù)的名字 可以與服務(wù)賬戶名相同
    NSString *serviceValue = serviceId;
    //指定存儲(chǔ)的內(nèi)容
    NSString *valueDataKey = (__bridge NSString *)kSecValueData;
    NSString *value = @"要存的內(nèi)容";
    NSDictionary *keychainItems = @{classKey   : classValue,
                                                            accessibleKey : accessibleValue,
                                                            accountKey    : accountValue,
                                                            serviceKey    : serviceValue,
                                                            valueDataKey  : value };

需要注意的是,賬戶名需要預(yù)防重名,因?yàn)閗eychain是系統(tǒng)級(jí)的,配置了keychain-sharing的其他App也可以訪問,一般使用類似bundleId的寫法
服務(wù)名稱就好像給宏取名字,表示存儲(chǔ)的這個(gè)東西是做什么的.
通過這兩個(gè)key就可以指定唯一性
kSecClassGenericPassword表示服務(wù)策略 key是kSecClass
kSecAttrAccessibleAlways是存取策略 key是kSecAttrAccessible
value是存儲(chǔ)的內(nèi)容 key是kSecValueData

2.增刪改查

- (void)viewDidLoad {
    [super viewDidLoad];

    BOOL succeed = [self keychainSaveData:@"123456789" withAccountIdentifier:@"trigger" andServiceIdentifier:@"password"];
    BOOL succeed1 = [self keychainSaveData:@"987654321" withAccountIdentifier:@"trigger" andServiceIdentifier:@"account"];
    NSLog(@"%d",succeed);
    NSLog(@"%d",succeed1);
    NSLog(@"%@",(NSString *)[self keychainGetDataWithAccountIdentifier:@"trigger" andServiceIdentifier:@"account"]);
    NSLog(@"%@",(NSString *)[self keychainGetDataWithAccountIdentifier:@"trigger" andServiceIdentifier:@"password"]);
    [self keychainUpdataData:@"000000000" withAccountIdentifier:@"trigger" andServiceIdentifier:@"password"];
    NSLog(@"%@",(NSString *)[self keychainGetDataWithAccountIdentifier:@"trigger" andServiceIdentifier:@"password"]);
    [self keychainDeleteWithAccountIdentifier:@"trigger" andServiceIdentifier:@"account"];
     NSLog(@"%@",(NSString *)[self keychainGetDataWithAccountIdentifier:@"trigger" andServiceIdentifier:@"account"]);
}

- (NSMutableDictionary *)keychainDicWithAccountId:(NSString *)accountId andServiceId:(NSString *)serviceId{
//構(gòu)建一個(gè)存取條件,實(shí)質(zhì)是一個(gè)字典
    NSString *classKey = (__bridge NSString *)kSecClass;
    //指定服務(wù)類型是普通密碼
    NSString *classValue = (__bridge NSString *)kSecClassGenericPassword;
    NSString *accessibleKey = (__bridge NSString *)kSecAttrAccessible;
    //指定安全類型是任何時(shí)候都可以訪問
    NSString *accessibleValue = (__bridge NSString *)kSecAttrAccessibleAlways;
    NSString *accountKey = (__bridge NSString *)kSecAttrAccount;
    //指定服務(wù)的賬戶名 可以與服務(wù)名相同 賬戶名可以對(duì)應(yīng)多個(gè)服務(wù)名
    NSString *accountValue = accountId;
    NSString *serviceKey = (__bridge NSString *)kSecAttrService;
    //指定服務(wù)的名字 可以與服務(wù)賬戶名相同
    NSString *serviceValue = serviceId;
    NSDictionary *keychainItems = @{classKey      : classValue,
                                    accessibleKey : accessibleValue,
                                    accountKey    : accountValue,
                                    serviceKey    : serviceValue};
    return keychainItems.mutableCopy;
}

- (BOOL)keychainSaveData:(id)aData withAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
    // 獲取存儲(chǔ)的數(shù)據(jù)的條件
    NSMutableDictionary * saveQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
    // 刪除舊的數(shù)據(jù)
    SecItemDelete((CFDictionaryRef)saveQueryDic);
    // 設(shè)置新的數(shù)據(jù)
    [saveQueryDic setObject:[NSKeyedArchiver archivedDataWithRootObject:aData] forKey:(id)kSecValueData];
    // 添加數(shù)據(jù)
    OSStatus saveState = SecItemAdd((CFDictionaryRef)saveQueryDic, nil);
    // 釋放對(duì)象
    saveQueryDic = nil ;
    // 判斷是否存儲(chǔ)成功
    if (saveState == errSecSuccess) {
        return YES;
    }
    return NO;
}

- (id)keychainGetDataWithAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
    id idObject = nil ;
    // 通過標(biāo)記獲取數(shù)據(jù)查詢條件
    NSMutableDictionary * readQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
    // 查詢結(jié)果返回到 kSecValueData (此項(xiàng)必選)
    [readQueryDic setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
    // 只返回搜索到的第一條數(shù)據(jù) (此項(xiàng)必選)
    [readQueryDic setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
    // 創(chuàng)建一個(gè)對(duì)象接受結(jié)果
    CFDataRef keyChainData = nil ;
    // 通過條件查詢數(shù)據(jù)
    if (SecItemCopyMatching((CFDictionaryRef)readQueryDic , (CFTypeRef *)&keyChainData) == noErr){
        @try {
            //轉(zhuǎn)換類型
            idObject = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)(keyChainData)];
        } @catch (NSException * exception){
            NSLog(@"Unarchive of search data where %@ failed of %@ ",serviceId,exception);
        }
    }
    if (keyChainData) {
        CFRelease(keyChainData);
    }
    readQueryDic = nil;
    // 返回?cái)?shù)據(jù)
    return idObject ;
}

- (BOOL)keychainUpdataData:(id)data withAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
    // 通過標(biāo)記獲取數(shù)據(jù)更新的條件
    NSMutableDictionary * updataQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
    // 創(chuàng)建更新數(shù)據(jù)字典
    NSMutableDictionary * newDic = @{}.mutableCopy;
    // 存儲(chǔ)數(shù)據(jù)
    [newDic setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
    // 獲取存儲(chǔ)的狀態(tài)
    OSStatus  updataStatus = SecItemUpdate((CFDictionaryRef)updataQueryDic, (CFDictionaryRef)newDic);
    updataQueryDic = nil;
    newDic = nil;
    // 判斷是否更新成功
    if (updataStatus == errSecSuccess) {
        return  YES ;
    }
    return NO;
}

- (void)keychainDeleteWithAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
    // 獲取刪除數(shù)據(jù)的查詢條件
    NSMutableDictionary * deleteQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
    // 刪除指定條件的數(shù)據(jù)
    SecItemDelete((CFDictionaryRef)deleteQueryDic);
    deleteQueryDic = nil ;
}

作者:Trigger_o
鏈接:http://www.reibang.com/p/6c2265a82f72
來源:簡(jiǎn)書
著作權(quán)歸作者所有无切。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)湖笨,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處黄刚。
](//upload-images.jianshu.io/upload_images/3238808-51b18f2d21779dd5.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200)

如圖,在兩個(gè)項(xiàng)目中配置keychain-sharing,這樣就可以訪問互相的keychain,以此達(dá)到共享數(shù)據(jù)

如何使用

keychain是一個(gè)結(jié)構(gòu),有很多key-value

image
image
image

此外還有kSecReturnData(返回結(jié)果) kSecMatchLimit(查詢策略)等 這兩項(xiàng)是查詢時(shí)必選的

1.首選要構(gòu)建一個(gè)存取條件,這些條件指定了要存取的這條內(nèi)容的存取類型,唯一性,存取策略和內(nèi)容

一共需要五個(gè)key

    NSString *classKey = (__bridge NSString *)kSecClass;
    //指定服務(wù)類型是普通密碼
    NSString *classValue = (__bridge NSString *)kSecClassGenericPassword;
    NSString *accessibleKey = (__bridge NSString *)kSecAttrAccessible;
    //指定安全類型是任何時(shí)候都可以訪問
    NSString *accessibleValue = (__bridge NSString *)kSecAttrAccessibleAlways;
    NSString *accountKey = (__bridge NSString *)kSecAttrAccount;
    //指定服務(wù)的賬戶名 可以與服務(wù)名相同 賬戶名可以對(duì)應(yīng)多個(gè)服務(wù)名
    NSString *accountValue = accountId;
    NSString *serviceKey = (__bridge NSString *)kSecAttrService;
    //指定服務(wù)的名字 可以與服務(wù)賬戶名相同
    NSString *serviceValue = serviceId;
    //指定存儲(chǔ)的內(nèi)容
    NSString *valueDataKey = (__bridge NSString *)kSecValueData;
    NSString *value = @"要存的內(nèi)容";
    NSDictionary *keychainItems = @{classKey   : classValue,
                                                            accessibleKey : accessibleValue,
                                                            accountKey    : accountValue,
                                                            serviceKey    : serviceValue,
                                                            valueDataKey  : value };

需要注意的是,賬戶名需要預(yù)防重名,因?yàn)閗eychain是系統(tǒng)級(jí)的,配置了keychain-sharing的其他App也可以訪問,一般使用類似bundleId的寫法
服務(wù)名稱就好像給宏取名字,表示存儲(chǔ)的這個(gè)東西是做什么的.
通過這兩個(gè)key就可以指定唯一性
kSecClassGenericPassword表示服務(wù)策略 key是kSecClass
kSecAttrAccessibleAlways是存取策略 key是kSecAttrAccessible
value是存儲(chǔ)的內(nèi)容 key是kSecValueData

2.增刪改查

- (void)viewDidLoad {
    [super viewDidLoad];

    BOOL succeed = [self keychainSaveData:@"123456789" withAccountIdentifier:@"trigger" andServiceIdentifier:@"password"];
    BOOL succeed1 = [self keychainSaveData:@"987654321" withAccountIdentifier:@"trigger" andServiceIdentifier:@"account"];
    NSLog(@"%d",succeed);
    NSLog(@"%d",succeed1);
    NSLog(@"%@",(NSString *)[self keychainGetDataWithAccountIdentifier:@"trigger" andServiceIdentifier:@"account"]);
    NSLog(@"%@",(NSString *)[self keychainGetDataWithAccountIdentifier:@"trigger" andServiceIdentifier:@"password"]);
    [self keychainUpdataData:@"000000000" withAccountIdentifier:@"trigger" andServiceIdentifier:@"password"];
    NSLog(@"%@",(NSString *)[self keychainGetDataWithAccountIdentifier:@"trigger" andServiceIdentifier:@"password"]);
    [self keychainDeleteWithAccountIdentifier:@"trigger" andServiceIdentifier:@"account"];
     NSLog(@"%@",(NSString *)[self keychainGetDataWithAccountIdentifier:@"trigger" andServiceIdentifier:@"account"]);
}

- (NSMutableDictionary *)keychainDicWithAccountId:(NSString *)accountId andServiceId:(NSString *)serviceId{
//構(gòu)建一個(gè)存取條件,實(shí)質(zhì)是一個(gè)字典
    NSString *classKey = (__bridge NSString *)kSecClass;
    //指定服務(wù)類型是普通密碼
    NSString *classValue = (__bridge NSString *)kSecClassGenericPassword;
    NSString *accessibleKey = (__bridge NSString *)kSecAttrAccessible;
    //指定安全類型是任何時(shí)候都可以訪問
    NSString *accessibleValue = (__bridge NSString *)kSecAttrAccessibleAlways;
    NSString *accountKey = (__bridge NSString *)kSecAttrAccount;
    //指定服務(wù)的賬戶名 可以與服務(wù)名相同 賬戶名可以對(duì)應(yīng)多個(gè)服務(wù)名
    NSString *accountValue = accountId;
    NSString *serviceKey = (__bridge NSString *)kSecAttrService;
    //指定服務(wù)的名字 可以與服務(wù)賬戶名相同
    NSString *serviceValue = serviceId;
    NSDictionary *keychainItems = @{classKey      : classValue,
                                    accessibleKey : accessibleValue,
                                    accountKey    : accountValue,
                                    serviceKey    : serviceValue};
    return keychainItems.mutableCopy;
}

- (BOOL)keychainSaveData:(id)aData withAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
    // 獲取存儲(chǔ)的數(shù)據(jù)的條件
    NSMutableDictionary * saveQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
    // 刪除舊的數(shù)據(jù)
    SecItemDelete((CFDictionaryRef)saveQueryDic);
    // 設(shè)置新的數(shù)據(jù)
    [saveQueryDic setObject:[NSKeyedArchiver archivedDataWithRootObject:aData] forKey:(id)kSecValueData];
    // 添加數(shù)據(jù)
    OSStatus saveState = SecItemAdd((CFDictionaryRef)saveQueryDic, nil);
    // 釋放對(duì)象
    saveQueryDic = nil ;
    // 判斷是否存儲(chǔ)成功
    if (saveState == errSecSuccess) {
        return YES;
    }
    return NO;
}

- (id)keychainGetDataWithAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
    id idObject = nil ;
    // 通過標(biāo)記獲取數(shù)據(jù)查詢條件
    NSMutableDictionary * readQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
    // 查詢結(jié)果返回到 kSecValueData (此項(xiàng)必選)
    [readQueryDic setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
    // 只返回搜索到的第一條數(shù)據(jù) (此項(xiàng)必選)
    [readQueryDic setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
    // 創(chuàng)建一個(gè)對(duì)象接受結(jié)果
    CFDataRef keyChainData = nil ;
    // 通過條件查詢數(shù)據(jù)
    if (SecItemCopyMatching((CFDictionaryRef)readQueryDic , (CFTypeRef *)&keyChainData) == noErr){
        @try {
            //轉(zhuǎn)換類型
            idObject = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)(keyChainData)];
        } @catch (NSException * exception){
            NSLog(@"Unarchive of search data where %@ failed of %@ ",serviceId,exception);
        }
    }
    if (keyChainData) {
        CFRelease(keyChainData);
    }
    readQueryDic = nil;
    // 返回?cái)?shù)據(jù)
    return idObject ;
}

- (BOOL)keychainUpdataData:(id)data withAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
    // 通過標(biāo)記獲取數(shù)據(jù)更新的條件
    NSMutableDictionary * updataQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
    // 創(chuàng)建更新數(shù)據(jù)字典
    NSMutableDictionary * newDic = @{}.mutableCopy;
    // 存儲(chǔ)數(shù)據(jù)
    [newDic setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
    // 獲取存儲(chǔ)的狀態(tài)
    OSStatus  updataStatus = SecItemUpdate((CFDictionaryRef)updataQueryDic, (CFDictionaryRef)newDic);
    updataQueryDic = nil;
    newDic = nil;
    // 判斷是否更新成功
    if (updataStatus == errSecSuccess) {
        return  YES ;
    }
    return NO;
}

- (void)keychainDeleteWithAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
    // 獲取刪除數(shù)據(jù)的查詢條件
    NSMutableDictionary * deleteQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
    // 刪除指定條件的數(shù)據(jù)
    SecItemDelete((CFDictionaryRef)deleteQueryDic);
    deleteQueryDic = nil ;
}

注:文章出處

作者:Trigger_o
鏈接:http://www.reibang.com/p/6c2265a82f7

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末黔漂,一起剝皮案震驚了整個(gè)濱河市诫尽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌炬守,老刑警劉巖牧嫉,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異减途,居然都是意外死亡酣藻,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門鳍置,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辽剧,“玉大人,你說我怎么就攤上這事税产∨陆危” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵辟拷,是天一觀的道長(zhǎng)撞羽。 經(jīng)常有香客問我,道長(zhǎng)衫冻,這世上最難降的妖魔是什么诀紊? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮隅俘,結(jié)果婚禮上邻奠,老公的妹妹穿的比我還像新娘笤喳。我一直安慰自己,他們只是感情好碌宴,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布杀狡。 她就那樣靜靜地躺著,像睡著了一般唧喉。 火紅的嫁衣襯著肌膚如雪捣卤。 梳的紋絲不亂的頭發(fā)上忍抽,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天八孝,我揣著相機(jī)與錄音,去河邊找鬼鸠项。 笑死干跛,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的祟绊。 我是一名探鬼主播楼入,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼牧抽!你這毒婦竟也來了嘉熊?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤扬舒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后讲坎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡衫画,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瓮栗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片削罩。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡费奸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出货邓,到底是詐尸還是另有隱情秆撮,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布换况,位于F島的核電站盗蟆,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏喳资。R本人自食惡果不足惜腾供,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望伴鳖。 院中可真熱鬧,春花似錦榜聂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽幢炸。三九已至拒贱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間柜思,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工号枕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留陨享,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓赞厕,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親皿桑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容