iOS获取设备唯一标识的实现步骤
1. 常用的UUID
UDID是一个40位十六进制序列(越狱的设备通过某些工具可以改变设备的 UDID),移动网络可以利用 UDID 来识别移动设备。
许多开发者把 UDID 跟用户的个人信息关联起来,网络窥探者会从多个应用收集这些数据,然后顺藤摸瓜得到这个人的许多隐私数据,同时大部分应用确实在频繁传输 UDID 和私人信息。 为了避免集体诉讼,苹果最终决定在 iOS 5 的时候,将这一惯例废除。
获取UUID的方法:
+ (NSString *)getUUID{
return [UIDevice currentDevice].identifierForVendor.UUIDString;;
}
2. MAC 地址
MAC地址,用来表示互联网上每一个站点的标示符,是一个六个字节(48位)的十六进制序列。前三个字节是由 IEEE 的注册管理机构 RA 负责给不同厂家分配的”编制上唯一的标示符,后三个字节由各厂家自行指派给生产的适配器接口。
MAC 地址在网络上用来区分设备的唯一性,接入网络的设备都有一个MAC地址,他们肯定都是唯一的。一部 iPhone 上可能有多个 MAC 地址,包括 WIFI 的、SIM 的等,但是 iTouch 和 iPad 上就有一个 WIFI 的,因此只需获取 WIFI 的 MAC 地址就好了。一般会采取 MD5(MAC 地址 + bundleID)获取唯一标识。
但是 MAC 地址和 UDID 一样,存在隐私问题, iOS 7 之后,所有设备请求 MAC 地址会返回一个固定值,这个方法也不攻自破了。
获取MAC在github找到一个挺好的方法:
2.1 首先导入下面几个库:
2.2 新建一个文件,继承NSObject,在.m文件导入头文件,以及定义一些宏
#import "XWGetMAC.h"
#import <ifaddrs.h>
#import <resolv.h>
#import <arpa/inet.h>
#import <net/if.h>
#import <netdb.h>
#import <netinet/ip.h>
#import <net/ethernet.h>
#import <net/if_dl.h>
#define MDNS_PORT 5353
#define QUERY_NAME "_apple-mobdev2._tcp.local"
#define DUMMY_MAC_ADDR @"02:00:00:00:00:00"
#define IOS_CELLULAR @"pdp_ip0"
#define IOS_WIFI @"en0"
#define IOS_VPN @"utun0"
#define IP_ADDR_IPv4 @"ipv4"
#define IP_ADDR_IPv6 @"ipv6"
+ (NSString *)getMAC:(BOOL)preferIPv4 {
return [[XWGetMAC alloc] getIPAddress:preferIPv4];
}
- (NSString *)getIPAddress:(BOOL)preferIPv4
{
NSArray *searchArray = preferIPv4 ?
@[ IOS_VPN @"/" IP_ADDR_IPv4, IOS_VPN @"/" IP_ADDR_IPv6, IOS_WIFI @"/" IP_ADDR_IPv4, IOS_WIFI @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6 ] :
@[ IOS_VPN @"/" IP_ADDR_IPv6, IOS_VPN @"/" IP_ADDR_IPv4, IOS_WIFI @"/" IP_ADDR_IPv6, IOS_WIFI @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4 ] ;
NSDictionary *addresses = [self getIPAddr];
__block NSString *address;
[searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL * _Nonnull stop) {
address = addresses[key];
//筛选出IP地址格式
if([self isValidatIP:address]) *stop = YES;
}];
return address ? address : @"0.0.0.0";
}
- (BOOL)isValidatIP:(NSString *)ipAddress {
if (ipAddress.length == 0) {
return NO;
}
NSString *urlRegEx = @"^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
"([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
"([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
"([01]?\\d\\d?|2[0-4]\\d|25[0-5])$";
NSError *error;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:urlRegEx options:0 error:&error];
if (regex != nil) {
NSTextCheckingResult *firstMatch=[regex firstMatchInString:ipAddress options:0 range:NSMakeRange(0, [ipAddress length])];
return firstMatch;
}
return NO;
}
- (NSDictionary *)getIPAddr
{
NSMutableDictionary *addresses = [NSMutableDictionary dictionaryWithCapacity:8];
// retrieve the current interfaces - returns 0 on success
struct ifaddrs *interfaces;
if(!getifaddrs(&interfaces)) {
// Loop through linked list of interfaces
struct ifaddrs *interface;
for(interface=interfaces; interface; interface=interface->ifa_next) {
if(!(interface->ifa_flags & IFF_UP) ) {
continue; // deeply nested code harder to read
}
const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr;
char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
if(addr && (addr->sin_family==AF_INET || addr->sin_family==AF_INET6)) {
NSString *name = [NSString stringWithUTF8String:interface->ifa_name];
NSString *type;
if(addr->sin_family == AF_INET) {
if(inet_ntop(AF_INET, &addr->sin_addr, addrBuf, INET_ADDRSTRLEN)) {
type = IP_ADDR_IPv4;
}
} else {
const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)interface->ifa_addr;
if(inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN)) {
type = IP_ADDR_IPv6;
}
}
if(type) {
NSString *key = [NSString stringWithFormat:@"%@/%@", name, type];
addresses[key] = [NSString stringWithUTF8String:addrBuf];
}
}
}
// Free memory
freeifaddrs(interfaces);
}
return [addresses count] ? addresses : nil;
}
- (nullable NSString *)getMacAddress {
res_9_init();
int len;
//get currnet ip address
NSString *ip = [self currentIPAddressOf:IOS_WIFI];
if(ip == nil) {
fprintf(stderr, "could not get current IP address of en0\n");
return DUMMY_MAC_ADDR;
}//end if
//set port and destination
_res.nsaddr_list[0].sin_family = AF_INET;
_res.nsaddr_list[0].sin_port = htons(MDNS_PORT);
_res.nsaddr_list[0].sin_addr.s_addr = [self IPv4Pton:ip];
_res.nscount = 1;
unsigned char response[NS_PACKETSZ];
//send mdns query
if((len = res_9_query(QUERY_NAME, ns_c_in, ns_t_ptr, response, sizeof(response))) < 0) {
fprintf(stderr, "res_search(): %s\n", hstrerror(h_errno));
return DUMMY_MAC_ADDR;
}//end if
//parse mdns message
ns_msg handle;
if(ns_initparse(response, len, &handle) < 0) {
fprintf(stderr, "ns_initparse(): %s\n", hstrerror(h_errno));
return DUMMY_MAC_ADDR;
}//end if
//get answer length
len = ns_msg_count(handle, ns_s_an);
if(len < 0) {
fprintf(stderr, "ns_msg_count return zero\n");
return DUMMY_MAC_ADDR;
}//end if
//try to get mac address from data
NSString *macAddress = nil;
for(int i = 0 ; i < len ; i++) {
ns_rr rr;
ns_parserr(&handle, ns_s_an, 0, &rr);
if(ns_rr_class(rr) == ns_c_in &&
ns_rr_type(rr) == ns_t_ptr &&
!strcmp(ns_rr_name(rr), QUERY_NAME)) {
char *ptr = (char *)(ns_rr_rdata(rr) + 1);
int l = (int)strcspn(ptr, "@");
char *tmp = calloc(l + 1, sizeof(char));
if(!tmp) {
perror("calloc()");
continue;
}//end if
memcpy(tmp, ptr, l);
macAddress = [NSString stringWithUTF8String:tmp];
free(tmp);
}//end if
}//end for each
macAddress = macAddress ? macAddress : DUMMY_MAC_ADDR;
return macAddress;
}//end getMacAddressFromMDNS
- (nonnull NSString *)currentIPAddressOf: (nonnull NSString *)device {
struct ifaddrs *addrs;
NSString *ipAddress = nil;
if(getifaddrs(&addrs) != 0) {
return nil;
}//end if
//get ipv4 address
for(struct ifaddrs *addr = addrs ; addr ; addr = addr->ifa_next) {
if(!strcmp(addr->ifa_name, [device UTF8String])) {
if(addr->ifa_addr) {
struct sockaddr_in *in_addr = (struct sockaddr_in *)addr->ifa_addr;
if(in_addr->sin_family == AF_INET) {
ipAddress = [self IPv4Ntop:in_addr->sin_addr.s_addr];
break;
}//end if
}//end if
}//end if
}//end for
freeifaddrs(addrs);
return ipAddress;
}//end currentIPAddressOf:
- (nullable NSString *)IPv4Ntop: (in_addr_t)addr {
char buffer[INET_ADDRSTRLEN] = {0};
return inet_ntop(AF_INET, &addr, buffer, sizeof(buffer)) ?
[NSString stringWithUTF8String:buffer] : nil;
}//end IPv4Ntop:
- (in_addr_t)IPv4Pton: (nonnull NSString *)IPAddr {
in_addr_t network = INADDR_NONE;
return inet_pton(AF_INET, [IPAddr UTF8String], &network) == 1 ?
network : INADDR_NONE;
}//end IPv4Pton:
如果出现 “_res_9_ninit", referenced from:”这种报错,是因为没有添加步骤1的几个库
3.UUID+自己存储
3.1 获取UUID的两个方法
+ (NSString *)getUUID{
CFUUIDRef uuid = CFUUIDCreate(NULL);
NSString *UUID = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, uuid);
CFRelease(uuid);
return UUID;
}
+ (NSString *)getUUID{
return [UIDevice currentDevice].identifierForVendor.UUIDString;;
}
很明显UUID已经不足以支持设备的唯一性了,目前很多App都有新用户的优惠,但是又要保证每台设备绑定一个账户,如果单纯使用UUID的话已经满足不了这个需求,所以,这里需要用keychain保存,这样即使卸载app在安装,获取到的UUID也是唯一性的。
3.2 首先在项目中添加 KeyChain Sharing
3.3 导入第三方库 Security.framework
3.4 核心代码(代码有点多)
在github搜索SSKeychain可以找到,只要 SSKeychain.h 和 SSKeychain.m 文件即可
#import <Foundation/Foundation.h>
#import <Security/Security.h>
typedef enum {
SSKeychainErrorNone = noErr,
SSKeychainErrorBadArguments = -1001,
SSKeychainErrorNoPassword = -1002,
SSKeychainErrorInvalidParameter = errSecParam,
SSKeychainErrorFailedToAllocated = errSecAllocate,
SSKeychainErrorNotAvailable = errSecNotAvailable,
SSKeychainErrorAuthorizationFailed = errSecAuthFailed,
SSKeychainErrorDuplicatedItem = errSecDuplicateItem,
SSKeychainErrorNotFound = errSecItemNotFound,
SSKeychainErrorInteractionNotAllowed = errSecInteractionNotAllowed,
SSKeychainErrorFailedToDecode = errSecDecode
} SSKeychainErrorCode;
extern NSString *const kSSKeychainErrorDomain;
extern NSString *const kSSKeychainAccountKey;
extern NSString *const kSSKeychainCreatedAtKey;
extern NSString *const kSSKeychainClassKey;
extern NSString *const kSSKeychainDescriptionKey;
extern NSString *const kSSKeychainLabelKey;
extern NSString *const kSSKeychainLastModifiedKey;
extern NSString *const kSSKeychainWhereKey;
@interface SSKeychain : NSObject
///-----------------------
/// @name Getting Accounts
///-----------------------
+ (NSArray *)allAccounts;
+ (NSArray *)allAccounts:(NSError **)error;
+ (NSArray *)accountsForService:(NSString *)serviceName;
+ (NSArray *)accountsForService:(NSString *)serviceName error:(NSError **)error;
///------------------------
/// @name Getting Passwords
///------------------------
+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account;
+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
+ (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account;
+ (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
///-------------------------
/// @name Deleting Passwords
///-------------------------
+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account;
+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
///------------------------
/// @name Setting Passwords
///------------------------
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account;
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
+ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account;
+ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
///--------------------
/// @name Configuration
///--------------------
#if __IPHONE_4_0 && TARGET_OS_IPHONE
+ (CFTypeRef)accessibilityType;
+ (void)setAccessibilityType:(CFTypeRef)accessibilityType;
#endif
@end
3.4 创建新类,引用 SSKeychain 封装
#import "GetKeychain.h"
#import "SSKeychain.h"
@implementation GetKeychain
+ (NSString *)getDeviceUUID {
NSString *currentDeviceUUIDStr = [SSKeychain passwordForService:@"项目boudle id" account:@"uuid"];
if (currentDeviceUUIDStr == nil || [currentDeviceUUIDStr isEqualToString:@""])
{
NSUUID *currentDeviceUUID = [UIDevice currentDevice].identifierForVendor;
currentDeviceUUIDStr = currentDeviceUUID.UUIDString;
currentDeviceUUIDStr = [currentDeviceUUIDStr stringByReplacingOccurrencesOfString:@"-" withString:@""];
currentDeviceUUIDStr = [currentDeviceUUIDStr lowercaseString];
[SSKeychain setPassword: currentDeviceUUIDStr forService:@"项目boudle id" account:@"uuid"];
}
return currentDeviceUUIDStr;
}
@end
以上就是iOS获取设备唯一标识的实现步骤的详细内容,更多关于iOS获取设备唯一标识的资料请关注编程网其它相关文章!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341