1. 常用的UUID
UDID是一个40位十六进制序列(越狱的设备通过某些工具可以改变设备的 UDID),移动网络可以利用 UDID 来识别移动设备。
许多开发者把 UDID 跟用户的个人信息关联起来,网络窥探者会从多个应用收集这些数据,然后顺藤摸瓜得到这个人的许多隐私数据,同时大部分应用确实在频繁传输 UDID 和私人信息。 为了避免集体诉讼,苹果最终决定在 iOS 5 的时候,将这一惯例废除。
获取UUID的方法:
1
2
3
4
|
/** 卸载应用重新安装后会不一致*/ + (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文件导入头文件,以及定义一些宏
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#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" |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
+ (NSString *)getMAC:( BOOL )preferIPv4 { return [[XWGetMAC alloc] getIPAddress:preferIPv4]; } /* * 获取设备当前网络IP地址 */ - (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) /* || (interface->ifa_flags & IFF_LOOPBACK) */ ) { 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; } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
/* * 获取设备物理地址 */ - (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的两个方法
1
2
3
4
5
6
7
8
9
10
11
12
|
/** 卸载应用重新安装后会不一致*/ + (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 文件即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
|
#import <Foundation/Foundation.h> #import <Security/Security.h> /** Error codes that can be returned in NSError objects. */ typedef enum { /** No error. */ SSKeychainErrorNone = noErr, /** Some of the arguments were invalid. */ SSKeychainErrorBadArguments = -1001, /** There was no password. */ SSKeychainErrorNoPassword = -1002, /** One or more parameters passed internally were not valid. */ SSKeychainErrorInvalidParameter = errSecParam, /** Failed to allocate memory. */ SSKeychainErrorFailedToAllocated = errSecAllocate, /** No trust results are available. */ SSKeychainErrorNotAvailable = errSecNotAvailable, /** Authorization/Authentication failed. */ SSKeychainErrorAuthorizationFailed = errSecAuthFailed, /** The item already exists. */ SSKeychainErrorDuplicatedItem = errSecDuplicateItem, /** The item cannot be found.*/ SSKeychainErrorNotFound = errSecItemNotFound, /** Interaction with the Security Server is not allowed. */ SSKeychainErrorInteractionNotAllowed = errSecInteractionNotAllowed, /** Unable to decode the provided data. */ SSKeychainErrorFailedToDecode = errSecDecode } SSKeychainErrorCode; extern NSString * const kSSKeychainErrorDomain; /** Account name. */ extern NSString * const kSSKeychainAccountKey; /** Time the item was created. The value will be a string. */ extern NSString * const kSSKeychainCreatedAtKey; /** Item class. */ extern NSString * const kSSKeychainClassKey; /** Item description. */ extern NSString * const kSSKeychainDescriptionKey; /** Item label. */ extern NSString * const kSSKeychainLabelKey; /** Time the item was last modified. The value will be a string. */ extern NSString * const kSSKeychainLastModifiedKey; /** Where the item was created. */ extern NSString * const kSSKeychainWhereKey; /** Simple wrapper for accessing accounts, getting passwords, setting passwords, and deleting passwords using the system Keychain on Mac OS X and iOS. This was originally inspired by EMKeychain and SDKeychain (both of which are now gone). Thanks to the authors. SSKeychain has since switched to a simpler implementation that was abstracted from [SSToolkit](http://sstoolk.it). */ @interface SSKeychain : NSObject ///----------------------- /// @name Getting Accounts ///----------------------- /** Returns an array containing the Keychain's accounts, or `nil` if the Keychain has no accounts. See the `NSString` constants declared in SSKeychain.h for a list of keys that can be used when accessing the dictionaries returned by this method. @return An array of dictionaries containing the Keychain's accounts, or `nil` if the Keychain doesn't have any accounts. The order of the objects in the array isn't defined. @see allAccounts: */ + (NSArray *)allAccounts; /** Returns an array containing the Keychain's accounts, or `nil` if the Keychain doesn't have any accounts. See the `NSString` constants declared in SSKeychain.h for a list of keys that can be used when accessing the dictionaries returned by this method. @param error If accessing the accounts fails, upon return contains an error that describes the problem. @return An array of dictionaries containing the Keychain's accounts, or `nil` if the Keychain doesn't have any accounts. The order of the objects in the array isn't defined. @see allAccounts */ + (NSArray *)allAccounts:(NSError **)error; /** Returns an array containing the Keychain's accounts for a given service, or `nil` if the Keychain doesn't have any accounts for the given service. See the `NSString` constants declared in SSKeychain.h for a list of keys that can be used when accessing the dictionaries returned by this method. @param serviceName The service for which to return the corresponding accounts. @return An array of dictionaries containing the Keychain's accountsfor a given `serviceName`, or `nil` if the Keychain doesn't have any accounts for the given `serviceName`. The order of the objects in the array isn't defined. @see accountsForService:error: */ + (NSArray *)accountsForService:(NSString *)serviceName; /** Returns an array containing the Keychain's accounts for a given service, or `nil` if the Keychain doesn't have any accounts for the given service. @param serviceName The service for which to return the corresponding accounts. @param error If accessing the accounts fails, upon return contains an error that describes the problem. @return An array of dictionaries containing the Keychain's accountsfor a given `serviceName`, or `nil` if the Keychain doesn't have any accounts for the given `serviceName`. The order of the objects in the array isn't defined. @see accountsForService: */ + (NSArray *)accountsForService:(NSString *)serviceName error:(NSError **)error; ///------------------------ /// @name Getting Passwords ///------------------------ /** Returns a string containing the password for a given account and service, or `nil` if the Keychain doesn't have a password for the given parameters. @param serviceName The service for which to return the corresponding password. @param account The account for which to return the corresponding password. @return Returns a string containing the password for a given account and service, or `nil` if the Keychain doesn't have a password for the given parameters. @see passwordForService:account:error: */ + (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account; /** Returns a string containing the password for a given account and service, or `nil` if the Keychain doesn't have a password for the given parameters. @param serviceName The service for which to return the corresponding password. @param account The account for which to return the corresponding password. @param error If accessing the password fails, upon return contains an error that describes the problem. @return Returns a string containing the password for a given account and service, or `nil` if the Keychain doesn't have a password for the given parameters. @see passwordForService:account: */ + (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error; /** Returns the password data for a given account and service, or `nil` if the Keychain doesn't have data for the given parameters. @param serviceName The service for which to return the corresponding password. @param account The account for which to return the corresponding password. @param error If accessing the password fails, upon return contains an error that describes the problem. @return Returns a the password data for the given account and service, or `nil` if the Keychain doesn't have data for the given parameters. @see passwordDataForService:account:error: */ + (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account; /** Returns the password data for a given account and service, or `nil` if the Keychain doesn't have data for the given parameters. @param serviceName The service for which to return the corresponding password. @param account The account for which to return the corresponding password. @param error If accessing the password fails, upon return contains an error that describes the problem. @return Returns a the password data for the given account and service, or `nil` if the Keychain doesn't have a password for the given parameters. @see passwordDataForService:account: */ + (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error; ///------------------------- /// @name Deleting Passwords ///------------------------- /** Deletes a password from the Keychain. @param serviceName The service for which to delete the corresponding password. @param account The account for which to delete the corresponding password. @return Returns `YES` on success, or `NO` on failure. @see deletePasswordForService:account:error: */ + ( BOOL )deletePasswordForService:(NSString *)serviceName account:(NSString *)account; /** Deletes a password from the Keychain. @param serviceName The service for which to delete the corresponding password. @param account The account for which to delete the corresponding password. @param error If deleting the password fails, upon return contains an error that describes the problem. @return Returns `YES` on success, or `NO` on failure. @see deletePasswordForService:account: */ + ( BOOL )deletePasswordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error; ///------------------------ /// @name Setting Passwords ///------------------------ /** Sets a password in the Keychain. @param password The password to store in the Keychain. @param serviceName The service for which to set the corresponding password. @param account The account for which to set the corresponding password. @return Returns `YES` on success, or `NO` on failure. @see setPassword:forService:account:error: */ + ( BOOL )setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account; /** Sets a password in the Keychain. @param password The password to store in the Keychain. @param serviceName The service for which to set the corresponding password. @param account The account for which to set the corresponding password. @param error If setting the password fails, upon return contains an error that describes the problem. @return Returns `YES` on success, or `NO` on failure. @see setPassword:forService:account: */ + ( BOOL )setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error; /** Sets arbirary data in the Keychain. @param password The data to store in the Keychain. @param serviceName The service for which to set the corresponding password. @param account The account for which to set the corresponding password. @param error If setting the password fails, upon return contains an error that describes the problem. @return Returns `YES` on success, or `NO` on failure. @see setPasswordData:forService:account:error: */ + ( BOOL )setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account; /** Sets arbirary data in the Keychain. @param password The data to store in the Keychain. @param serviceName The service for which to set the corresponding password. @param account The account for which to set the corresponding password. @param error If setting the password fails, upon return contains an error that describes the problem. @return Returns `YES` on success, or `NO` on failure. @see setPasswordData:forService:account: */ + ( BOOL )setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error; ///-------------------- /// @name Configuration ///-------------------- #if __IPHONE_4_0 && TARGET_OS_IPHONE /** Returns the accessibility type for all future passwords saved to the Keychain. @return Returns the accessibility type. The return value will be `NULL` or one of the "Keychain Item Accessibility Constants" used for determining when a keychain item should be readable. @see accessibilityType */ + (CFTypeRef)accessibilityType; /** Sets the accessibility type for all future passwords saved to the Keychain. @param accessibilityType One of the "Keychain Item Accessibility Constants" used for determining when a keychain item should be readable. If the value is `NULL` (the default), the Keychain default will be used. @see accessibilityType */ + ( void )setAccessibilityType:(CFTypeRef)accessibilityType; #endif @end |
3.4 创建新类,引用 SSKeychain 封装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#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获取设备唯一标识的资料请关注服务器之家其它相关文章!
原文链接:https://juejin.cn/post/6953882718941741087