在之前开发游戏官方渠道sdk的时候,通常会有快速登录的需求,这时候就需要获取设备的唯一标识 来当做用户的账号。
在一些大公司,比如网易、腾讯、游久等,他们都会有自己的一套获取设备号的方法。
之前试过一些,比如mac地址,uuid ,udid等,这些方式也都被苹果禁用了。
当时,uuid可以获取到,但是如果用户在设置中
设置-隐私-广告-跟踪广告,这个如果是打开状态的话,会造成获取不到设备号(每次设备号都会重新生成)。所以,游戏玩家进入游戏,下线再次登录之后,会发现之前的游戏角色不见了。(或者变成别人的账号,发生串号问题)
那么,我们就要获取一个永不会变的“标识符”,存在设备的钥匙串中,即使删除应用程序(游戏),这个“标识符”依然存在。
如下:项目中实际的解决方案。
+ (NSString*)UDID
{
NSString *udid = [SvUDIDTools getUDIDFromKeyChain];
if(!udid) {
NSString *sysVersion = [UIDevice currentDevice].systemVersion;
CGFloat version = [sysVersion floatValue];
if(version >=7.0) {
udid = [SvUDIDTools _UDID_iOS7];
}
elseif(version >=2.0) {
udid = [SvUDIDTools _UDID_iOS6];
}
[SvUDIDTools settUDIDToKeyChain:udid];
}
returnudid;
}
/*
* iOS 6.0
* use wifi's mac address
*/
+ (NSString*)_UDID_iOS6
{
return[SvUDIDTools getMacAddress];
}
/*
* iOS 7.0
* Starting from iOS 7, the system always returns the value 02:00:00:00:00:00
* when you ask for the MAC address on any device.
* use identifierForVendor + keyChain
* make sure UDID consistency atfer app delete and reinstall
*/
+ (NSString*)_UDID_iOS7
{
return[[UIDevice currentDevice].identifierForVendor UUIDString];
}
#pragma mark -
#pragma mark Helper Method for Get Mac Address
+ (NSString *)getMacAddress
{
intmgmtInfoBase[6];
char*msgBuffer =NULL;
size_tlength;
unsignedcharmacAddress[6];
structif_msghdr*interfaceMsgStruct;
structsockaddr_dl*socketStruct;
NSString*errorFlag =nil;
// Setup the management Information Base (mib)
mgmtInfoBase[0] = CTL_NET;// Request network subsystem
mgmtInfoBase[1] = AF_ROUTE;// Routing table info
mgmtInfoBase[2] =0;
mgmtInfoBase[3] = AF_LINK;// Request link layer information
mgmtInfoBase[4] = NET_RT_IFLIST;// Request all configured interfaces
// With all configured interfaces requested, get handle index
if((mgmtInfoBase[5] = if_nametoindex("en0")) ==0)
errorFlag =@"if_nametoindex failure";
else
{
// Get the size of the data available (store in len)
if(sysctl(mgmtInfoBase,6,NULL, &length,NULL,0) <0)
errorFlag =@"sysctl mgmtInfoBase failure";
else
{
// Alloc memory based on above call
if((msgBuffer = malloc(length)) ==NULL)
errorFlag =@"buffer allocation failure";
else
{
// Get system information, store in buffer
if(sysctl(mgmtInfoBase,6, msgBuffer, &length,NULL,0) <0)
errorFlag =@"sysctl msgBuffer failure";
}
}
}
// Befor going any further...
if(errorFlag !=NULL)
{
NSLog(@"Error: %@", errorFlag);
if(msgBuffer) {
free(msgBuffer);
}
returnerrorFlag;
}
// Map msgbuffer to interface message structure
interfaceMsgStruct = (structif_msghdr *) msgBuffer;
// Map to link-level socket structure
socketStruct = (structsockaddr_dl *) (interfaceMsgStruct +1);
// Copy link layer address data in socket structure to an array
memcpy(&macAddress, socketStruct->sdl_data + socketStruct->sdl_nlen,6);
// Read from char array into a string object, into traditional Mac address format
NSString *macAddressString = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X",
macAddress[0], macAddress[1], macAddress[2],
macAddress[3], macAddress[4], macAddress[5]];
NSLog(@"Mac Address: %@", macAddressString);
// Release the buffer memory
free(msgBuffer);
returnmacAddressString;
}
#pragma mark -
#pragma mark Helper Method for make identityForVendor consistency
+ (NSString*)getUDIDFromKeyChain
{
NSMutableDictionary *dictForQuery = [[NSMutableDictionary alloc] init];
[dictForQuery setValue:(id)kSecClassGenericPassword forKey:(id)kSecClass];
// set Attr Description for query
[dictForQuery setValue:[NSString stringWithUTF8String:kKeychainUDIDItemIdentifier]
forKey:kSecAttrDescription];
// set Attr Identity for query
NSData *keychainItemID = [NSData dataWithBytes:kKeychainUDIDItemIdentifier
length:strlen(kKeychainUDIDItemIdentifier)];
[dictForQuery setObject:keychainItemID forKey:(id)kSecAttrGeneric];
// The keychain access group attribute determines if this item can be shared
// amongst multiple apps whose code signing entitlements contain the same keychain access group.
NSString *accessGroup = [NSString stringWithUTF8String:kKeyChainUDIDAccessGroup];
if(accessGroup !=nil)
{
#if TARGET_IPHONE_SIMULATOR
// Ignore the access group if running on the iPhone simulator.
//
// Apps that are built for the simulator aren't signed, so there's no keychain access group
// for the simulator to check. This means that all apps can see all keychain items when run
// on the simulator.
//
// If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
// simulator will return -25243 (errSecNoAccessForItem).
#else
[dictForQuery setObject:accessGroup forKey:(id)kSecAttrAccessGroup];
#endif
}
[dictForQuery setValue:(id)kCFBooleanTrue forKey:(id)kSecMatchCaseInsensitive];
[dictForQuery setValue:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
[dictForQuery setValue:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
OSStatus queryErr= noErr;
NSData*udidValue =nil;
NSString *udid=nil;
queryErr = SecItemCopyMatching((CFDictionaryRef)dictForQuery, (CFTypeRef*)&udidValue);
NSMutableDictionary *dict =nil;
[dictForQuery setValue:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];
queryErr = SecItemCopyMatching((CFDictionaryRef)dictForQuery, (CFTypeRef*)&dict);
if(queryErr == errSecItemNotFound) {
NSLog(@"KeyChain Item: %@ not found!!!", [NSString stringWithUTF8String:kKeychainUDIDItemIdentifier]);
}
elseif(queryErr != errSecSuccess) {
NSLog(@"KeyChain Item query Error!!! Error code:%ld", queryErr);
}
if(queryErr == errSecSuccess) {
NSLog(@"KeyChain Item: %@", udidValue);
if(udidValue) {
udid = [NSString stringWithUTF8String:udidValue.bytes];
[udidValue release];
}
[dict release];
}
[dictForQuery release];
returnudid;
}
+ (BOOL)settUDIDToKeyChain:(NSString*)udid
{
NSMutableDictionary *dictForAdd = [[NSMutableDictionary alloc] init];
[dictForAdd setValue:(id)kSecClassGenericPassword forKey:(id)kSecClass];
[dictForAdd setValue:[NSString stringWithUTF8String:kKeychainUDIDItemIdentifier] forKey:kSecAttrDescription];
[dictForAdd setValue:@"UUID"forKey:(id)kSecAttrGeneric];
// Default attributes for keychain item.
[dictForAdd setObject:@""forKey:(id)kSecAttrAccount];
[dictForAdd setObject:@""forKey:(id)kSecAttrLabel];
// The keychain access group attribute determines if this item can be shared
// amongst multiple apps whose code signing entitlements contain the same keychain access group.
NSString *accessGroup = [NSString stringWithUTF8String:kKeyChainUDIDAccessGroup];
if(accessGroup !=nil)
{
#if TARGET_IPHONE_SIMULATOR
// Ignore the access group if running on the iPhone simulator.
//
// Apps that are built for the simulator aren't signed, so there's no keychain access group
// for the simulator to check. This means that all apps can see all keychain items when run
// on the simulator.
//
// If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
// simulator will return -25243 (errSecNoAccessForItem).
#else
[dictForAdd setObject:accessGroup forKey:(id)kSecAttrAccessGroup];
#endif
}
constchar*udidStr = [udid UTF8String];
NSData *keyChainItemValue = [NSData dataWithBytes:udidStr length:strlen(udidStr)];
[dictForAdd setValue:keyChainItemValue forKey:(id)kSecValueData];
OSStatus writeErr = noErr;
if([SvUDIDTools getUDIDFromKeyChain]) {// there is item in keychain
[SvUDIDTools updateUDIDInKeyChain:udid];
[dictForAdd release];
returnYES;
}
else{// add item to keychain
writeErr = SecItemAdd((CFDictionaryRef)dictForAdd,NULL);
if(writeErr != errSecSuccess) {
NSLog(@"Add KeyChain Item Error!!! Error Code:%ld", writeErr);
[dictForAdd release];
returnNO;
}
else{
NSLog(@"Add KeyChain Item Success!!!");
[dictForAdd release];
returnYES;
}
}
[dictForAdd release];
returnNO;
}
+ (BOOL)removeUDIDFromKeyChain
{
NSMutableDictionary *dictToDelete = [[NSMutableDictionary alloc] init];
[dictToDelete setValue:(id)kSecClassGenericPassword forKey:(id)kSecClass];
NSData *keyChainItemID = [NSData dataWithBytes:kKeychainUDIDItemIdentifier length:strlen(kKeychainUDIDItemIdentifier)];
[dictToDelete setValue:keyChainItemID forKey:(id)kSecAttrGeneric];
OSStatus deleteErr = noErr;
deleteErr = SecItemDelete((CFDictionaryRef)dictToDelete);
if(deleteErr != errSecSuccess) {
NSLog(@"delete UUID from KeyChain Error!!! Error code:%ld", deleteErr);
[dictToDelete release];
returnNO;
}
else{
NSLog(@"delete success!!!");
}
[dictToDelete release];
returnYES;
}
+ (BOOL)updateUDIDInKeyChain:(NSString*)newUDID
{
NSMutableDictionary *dictForQuery = [[NSMutableDictionary alloc] init];
[dictForQuery setValue:(id)kSecClassGenericPassword forKey:(id)kSecClass];
NSData *keychainItemID = [NSData dataWithBytes:kKeychainUDIDItemIdentifier
length:strlen(kKeychainUDIDItemIdentifier)];
[dictForQuery setValue:keychainItemID forKey:(id)kSecAttrGeneric];
[dictForQuery setValue:(id)kCFBooleanTrue forKey:(id)kSecMatchCaseInsensitive];
[dictForQuery setValue:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
[dictForQuery setValue:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];
NSDictionary *queryResult =nil;
SecItemCopyMatching((CFDictionaryRef)dictForQuery, (CFTypeRef*)&queryResult);
if(queryResult) {
NSMutableDictionary *dictForUpdate = [[NSMutableDictionary alloc] init];
[dictForUpdate setValue:[NSString stringWithUTF8String:kKeychainUDIDItemIdentifier] forKey:kSecAttrDescription];
[dictForUpdate setValue:keychainItemID forKey:(id)kSecAttrGeneric];
constchar*udidStr = [newUDID UTF8String];
NSData *keyChainItemValue = [NSData dataWithBytes:udidStr length:strlen(udidStr)];
[dictForUpdate setValue:keyChainItemValue forKey:(id)kSecValueData];
OSStatus updateErr = noErr;
// First we need the attributes from the Keychain.
NSMutableDictionary *updateItem = [NSMutableDictionary dictionaryWithDictionary:queryResult];
[queryResult release];
// Second we need to add the appropriate search key/values.
// set kSecClass is Very important
[updateItem setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
updateErr = SecItemUpdate((CFDictionaryRef)updateItem, (CFDictionaryRef)dictForUpdate);
if(updateErr != errSecSuccess) {
NSLog(@"Update KeyChain Item Error!!! Error Code:%ld", updateErr);
[dictForQuery release];
[dictForUpdate release];
returnNO;
}
else{
NSLog(@"Update KeyChain Item Success!!!");
[dictForQuery release];
[dictForUpdate release];
returnYES;
}
}
[dictForQuery release];
returnNO;
}