iOS移动端接口加密流程(包含单点登录和失效时间)

移动开发
最近换了工作,新公司的一套面试题是关于移动端接口加密流程,当时根据面试题画出了大概的 UML 类图,但是还有很多考虑不周的情况。接触到代码后仔细研究看了一下数据请求逻辑,感觉还是有必要总结一下加深理解。

最近换了工作,新公司的一套面试题是关于移动端接口加密流程,当时根据面试题画出了大概的 UML 类图,但是还有很多考虑不周的情况。接触到代码后仔细研究看了一下数据请求逻辑,感觉还是有必要总结一下加深理解。

[[213149]]

  • 为了数据安全,大多数没有使用 https 的公司都会选择 token 来提高数据请求的安全性,token 的生成方法也不尽相同,只要保证唯一性就行,***是后台管理 token,由后台生成并返回前端保存。

上送参数

所有接口都需要携带当前的的版本号和平台

iOS移动端接口加密流程(包含单点登录和失效时间)

iOS端和安卓端分配不同的 app_id、private_key。每次通信是除了上述要上送的数据外,还要上送 timestamp、app_id、private_key、rand、sign。见下表:

iOS移动端接口加密流程(包含单点登录和失效时间)

如:iOS 端

 

  1. app_id=170ib799dd511e7b66000163e033322  
  2. private_key = 170ib799dd511e7b66000163e033322 

业务数据为 phoneNo=123456&password=123456

不包含 sign 的上送数据(data) 为:

  1. app_id=170ib799dd511e7b66000163e033322&phoneNo=123456&password=123456&plat=iOS&rand=12dwfhdy487rSelsd×tamp=2017-12-9-13:39:39:01&ver=1.0 

sign =MD5(data+ private_key), 就是对一下字符串做MD5运算

  1. app_id=170ib799dd511e7b66000163e033322&phoneNo=123456&password=123456&private_key=170ib799dd511e7b66000163e033322&plat=iOS&rand=12dwfhdy487rSelsd×tamp=2017-12-9-13:39:39:01&ver=1.0 

计算出来的结果sign最终为32位数据和字母组合8357heukf384r83gh3fi3dwks42i993(示例)

最终上送data + sign 如:

  1. app_id=170ib799dd511e7b66000163e033322&phoneNo=123456&password=123456&plat=iOS&rand=12dwfhdy487rSelsd×tamp=2017-12-9-13:39:39:01&ver=1.0&sign= 8357heukf384r83gh3fi3dwks42i993 

这是签名管理类.h代码

 

  1. #import <Foundation/Foundation.h> 
  2.  
  3. @interface TPBaseRequest : NSObject 
  4.  
  5. @property (nonatomic, strong) NSString *plat; 
  6. @property (nonatomic, strong) NSString *ver; 
  7.  
  8. - (NSDictionary *)baseParameters; 
  9. - (NSDictionary *)finalParametersFrom:(NSDictionary *)dic; 
  10.  
  11. @end 
  12.  
  13.  
  14. 作者:青青是老大要听青青话 
  15. 链接:http://www.jianshu.com/p/fe3c2931ed53 
  16. 來源:简书 
  17. 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 

这是签名管理类.m代码

 

  1. #import "TPBaseRequest.h" 
  2. #import <CommonCrypto/CommonDigest.h> 
  3.  
  4. @interface TPBaseRequest() 
  5.  
  6. @property (nonatomic, strong)NSString *rand; 
  7.  
  8. @property (nonatomic, copy) NSString *timeStr; 
  9.  
  10. @end 
  11.  
  12. @implementation TPBaseRequest 
  13.  
  14. //最终上送为data(base + dic)+sign 
  15. - (NSDictionary *)finalParametersFrom:(NSDictionary *)dic 
  16.     self.rand = [self gainRand]; 
  17.     NSMutableDictionary *finalDic = [NSMutableDictionary dictionaryWithDictionary:[self baseParameters]]; 
  18.     [finalDic removeObjectForKey:@"private_key"]; 
  19.     [finalDic addEntriesFromDictionary:dic]; 
  20.     [finalDic setObject:[self gainSign:dic] forKey:@"sign"]; 
  21.     [finalDic setObject:_timeStr forKey:@"timestamp"]; 
  22.     return finalDic; 
  23. //基础参数 
  24. - (NSDictionary *)baseParameters 
  25.     //基础参数赋值 
  26.     NSMutableDictionary *para = [NSMutableDictionary dictionary]; 
  27.     [para setObject:kApp_id forKey:@"app_id"]; 
  28.     [para setObject:kPrivate_key forKey:@"private_key"]; 
  29.     [para setObject:@"ios" forKey:@"plat"]; 
  30.     [para setObject:[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"] forKey:@"ver"]; 
  31.     [para setObject:self.rand forKey:@"rand"]; 
  32.     [para setObject:[self gainTimestamp] forKey:@"timestamp"]; 
  33.      
  34.     if([MDAccountStore isLogined]){ 
  35.         if(![MDAccountManager sharedInstance].currentAccount){ 
  36.             [MDAccountManager sharedInstance].currentAccount = [[MDAccountStore sharedInstance] readTheAccount]; 
  37.         } 
  38.     } 
  39.     //判断是否有用户登录 如果有携带上 userId 
  40.     if ([MDAccountManager sharedInstance].currentAccount.userId) { 
  41.         long userId = [MDAccountManager sharedInstance].currentAccount.userId; 
  42.         [para setObject:[NSNumber numberWithLong:userId] forKey:@"userId"]; 
  43.     } 
  44.     //判断本地存储是否有 token 
  45.     if ([MDAccountManager sharedInstance].currentAccount.token) { 
  46.         [para setObject:[MDAccountManager sharedInstance].currentAccount.token forKey:@"token"]; 
  47.     } 
  48.      
  49.     return [NSDictionary dictionaryWithDictionary:para]; 
  50. //获取随机数 
  51. - (NSString *)gainRand 
  52.     NSString *str = [NSString string]; 
  53.     //16位 
  54.     for (int i = 0; i < 16; i++) 
  55.     { 
  56.         //随机出现字母、数字 
  57.         switch(arc4random() % 3) { 
  58.             case 0: 
  59.                 str = [str stringByAppendingString:[NSString stringWithFormat:@"%d", arc4random() % 10]]; 
  60.                 break; 
  61.             case 1: 
  62.                 str = [str stringByAppendingString:[NSString stringWithFormat:@"%c",  (arc4random() % 26) + 97]]; 
  63.                 break; 
  64.             case 2: 
  65.                 str = [str stringByAppendingString:[NSString stringWithFormat:@"%c", (arc4random() % 26) + 65]]; 
  66.                 break; 
  67.         } 
  68.     } 
  69.     return str; 
  70. //获取时间字符串 
  71. - (NSString *)gainTimestamp 
  72.     NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 
  73.     dateFormatter.dateFormat = @"yyyy-MM-dd-HH:mm:ss"
  74.     NSString *timeStr = [dateFormatter stringFromDate:[NSDate date]]; 
  75.     //    NSString *stamp = [NSString stringWithFormat:@"%ld", (long)[[NSDate date] timeIntervalSince1970]]; 
  76.     return timeStr; 
  77. //获取sign sign=md5(data+private_key) 
  78. - (NSString *)gainSign:(NSDictionary *)dic 
  79.     NSString *sign = [self getMd5_32Bit_String:[self gainData:dic]]; 
  80.     return sign; 
  81. //获取data+private_key (private_key包含在base中) 
  82. - (NSString *)gainData:(NSDictionary *)dic 
  83.     //data = base + dic 
  84.     NSMutableDictionary *basePara = [NSMutableDictionary dictionaryWithDictionary:[self baseParameters]]; 
  85.     _timeStr = [self gainTimestamp]; 
  86.     [basePara addEntriesFromDictionary:dic]; 
  87.      
  88.     NSArray *arr = [basePara allKeys]; 
  89.     arr = [arr sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2){ 
  90.         NSComparisonResult result = [obj1 compare:obj2]; 
  91.         return result == NSOrderedDescending; 
  92.     }]; 
  93.      
  94.     NSString *dataStr = [NSString string]; 
  95.     for (int i = 0; i < arr.count; i++) { 
  96.         dataStr = [dataStr stringByAppendingString:arr[i]]; 
  97.         dataStr = [dataStr stringByAppendingString:@"="]; 
  98.         dataStr = [dataStr stringByAppendingString:[NSString stringWithFormat:@"%@",[basePara objectOrNilForKey:arr[i]]]]; 
  99.         dataStr = [dataStr stringByAppendingString:@"&"]; 
  100.     } 
  101.     dataStr = [dataStr substringToIndex:dataStr.length - 1]; 
  102.     return dataStr; 
  103. //md5加密 
  104. - (NSString *)getMd5_32Bit_String:(NSString *)srcString 
  105.     const char *cStr = [srcString UTF8String]; 
  106.     unsigned char digest[CC_MD5_DIGEST_LENGTH]; 
  107.     CC_MD5( cStr, strlen(cStr), digest ); 
  108.     NSMutableString *result = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2]; 
  109.     for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) 
  110.         [result appendFormat:@"%02x", digest[i]]; 
  111.      
  112.     return result; 
  113. @end 
  114.  
  115.  
  116. 作者:青青是老大要听青青话 
  117. 链接:http://www.jianshu.com/p/fe3c2931ed53 
  118. 來源:简书 
  119. 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 

后台在接收到数据后首先会做验签操作,验证签名是否正确和签名是否失效。

  • 单点登录:如果账号被别的客户端登录,上送的 token 会发生改变,之前的 token 就会过期,会在本客户端发出数据请求时弹出登录框。
  • 失效时间: 后台验证token时间,超过预设时间会返回-100

单点登录和失效时间返回 json 数据如下:

 

  1.   "err":{ 
  2.       "code":-100, 
  3.       "msg": 登录已过期,请重新登录, 
  4.       "eventId":"xxx-xxx-xxx-xxx-xxx" 
  5.   } 
  • 这是有一个建议,我们可以在根视图设置一个通知监听,当我们在处理数据时收到 code= -100 是发出登录的通知,模态出登陆页面
  • 数据请求方法

 

  1. + (void)postRequestWihtUrl:(NSString *)url params:(NSDictionary *)params success:(void (^)(id))successInfo failure:(TPErrorInfo)errorInfo { 
  2.     AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; 
  3.     manager.requestSerializer = [AFHTTPRequestSerializer serializer]; 
  4.     manager.responseSerializer.acceptableContentTypes = [NSSet setWithArray:@[@"text/html", @"application/json", @"application/x-www-form-urlencoded"]]; 
  5.      
  6.     /** 
  7.      数据请求 
  8.      获取基础上送参数 
  9.      [[[TPBaseRequest alloc] init] finalParametersFrom:params] 
  10.      */ 
  11.     [manager POST:url parameters:[[[TPBaseRequest alloc] init] finalParametersFrom:params] progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) { 
  12.         TPErrorHandle *err = [[TPErrorHandle alloc] initWithDic:[responseObject objectForKey:@"err"]]; 
  13.         if (err.isError) { 
  14.             errorInfo(err); 
  15.             return
  16.         } 
  17.         successInfo(responseObject); 
  18.     } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { 
  19.         NSLog(@"请求失败信息:%@",error); 
  20.         NSDictionary *errDic=error.userInfo; 
  21.         NSHTTPURLResponse *response=[errDic objectForKey:@"com.alamofire.serialization.response.error.response"]; 
  22.         NSInteger errCode=response.statusCode; 
  23.         MDErrorInfo *err = [[MDErrorInfo alloc] initWithDefault]; 
  24.         err.code = errCode; 
  25.         if (error.code == -1009) { 
  26.             err.msg = @"网络超时"
  27.         } 
  28.         else if (err.code == 404){ 
  29.             err.msg = @"网络页面不存在"
  30.         } 
  31.    //我发出通知处理错误的方法在TPErrorHandle 文件里 
  32.         errorInfo([[TPErrorHandle alloc] initWithMDErrorInfo:err]); 
  33.     }]; 
  34.  
  35.  
  36. 作者:青青是老大要听青青话 
  37. 链接:http://www.jianshu.com/p/fe3c2931ed53 
  38. 來源:简书 
  39. 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 
  • 这里有一个eventId字段,我之前说了app_id尽量每个版本更新时候换一个,这里就用了

在 APP 更新一个星期后,我们会在后台把之前版本使用的 app_id 删除,用户如果在继续使用老版本进行数据请求是会出现以下报错:

 

  1.   "err":{ 
  2.       "code":-99, 
  3.       "msg""有新版本,请及时更新", 
  4.       "eventId":"返回的更新链接" 
  5.   } 

根视图上添加的更新通知监听就会pop 出更新页面,这样极大地提高了新版本的更新率。

  • 这一套流程使用时移动端几乎不需要控制是否需要去登陆,所有的判断是否登录都抛给后台去判断,这种做法会浪费一部分服务器资源,但是只要前后台封装好一整套流程,开发速度会有很大提高的。
  • 接口的加密大概就这么些东西了,没有任何加密是无法破解的,我们要做的只是去增加破解的难度。这种加密方式肯定会有弊端,现阶段的我可能技术太烂,只能这么去总结一下,如果写的不好请见谅。
  • ***想感慨一下关于接手别人代码的事,最近换了工作,公司自己的一个产品需要我接手开发迭代,这份代码从15年到现在经手过5个人,除了代码没有留下任何文档,每个人写代码的风格迥异。熟悉别人代码的过程是痛苦的,但是你在熟悉别人代码的过程中进步也是特别快的,不管别人代码写的好还是坏,每个人的代码中你总能找到亮点,找到你没有用过的技能。

 

责任编辑:未丽燕 来源: 简书
相关推荐

2020-12-28 05:52:27

SSO登录单点

2013-10-16 15:17:30

vCenter单点登录

2016-12-26 18:05:00

单点登录原理简单实现

2024-01-12 18:26:44

2024-03-07 09:20:16

2020-09-24 23:01:35

TensorFlow数据机器学习

2012-08-07 09:04:46

单点登录云安全云计算

2024-08-29 10:23:42

2024-06-21 09:28:43

2012-02-14 14:17:35

ibmdw

2021-06-24 08:52:19

单点登录代码前端

2021-07-02 10:45:53

SpringBootCAS登录

2021-01-18 06:21:18

登录SSO接口

2024-09-11 08:37:39

2021-07-13 14:05:37

单点登录页面

2014-03-06 10:50:59

iOS开发

2021-05-27 07:12:19

单点登录系统

2023-08-29 08:00:38

2011-12-06 09:58:20

2013-06-04 16:59:42

iOS开发iOS工具移动开发
点赞
收藏

51CTO技术栈公众号