I recently changed jobs, and a set of interview questions in the new company was about the mobile terminal interface encryption process. At that time, I drew a rough UML class diagram based on the interview questions, but there were still many things that were not considered carefully. After getting in touch with the code and carefully studying the data request logic, I felt that it was necessary to summarize it to deepen my understanding. [[213149]] - For data security, most companies that do not use https will choose tokens to improve the security of data requests. The methods of generating tokens are also different, as long as the uniqueness is guaranteed. The best is the background management token, which is generated by the background and returned to the front end for storage.
Send Parameters All interfaces need to carry the current version number and platform Different app_id and private_key are assigned to iOS and Android. In addition to the above data, timestamp, app_id, private_key, rand, and sign are also sent for each communication. See the table below: For example: iOS - app_id=170ib799dd511e7b66000163e033322
- private_key = 170ib799dd511e7b66000163e033322
Business data is phoneNo=123456&password=123456 The data sent without sign is: - 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), which is to perform MD5 operation on the string - 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
The calculated result sign is finally a 32-bit data and letter combination 8357heukf384r83gh3fi3dwks42i993 (example) Finally, data + sign is sent, such as: - app_id=170ib799dd511e7b66000163e033322&phoneNo=123456& password =123456&plat=iOS&rand=12dwfhdy487rSelsd×tamp=2017-12-9-13:39:39:01&ver=1.0&sign= 8357heukf384r83gh3fi3dwks42i993
This is the signature management class.h code - #import <Foundation/Foundation.h>
-
- @interface TPBaseRequest : NSObject
-
- @property (nonatomic, strong) NSString *plat;
- @property (nonatomic, strong) NSString *ver;
-
- - (NSDictionary *)baseParameters;
- - (NSDictionary *)finalParametersFrom:(NSDictionary *)dic;
-
- @ end
-
-
- Author: Qingqing is the boss, so listen to her
- Link: http://www.jianshu.com/p/fe3c2931ed53
- Source: Jianshu
- The copyright belongs to the author. For commercial reproduction, please contact the author for authorization. For non-commercial reproduction, please indicate the source.
This is the signature management class.m code - #import "TPBaseRequest.h"
- #import <CommonCrypto/CommonDigest.h>
-
- @interface TPBaseRequest()
-
- @property (nonatomic, strong)NSString *rand;
-
- @property (nonatomic, copy) NSString *timeStr;
-
- @ end
-
- @implementation TPBaseRequest
-
- //The final output is data(base + dic)+sign
- - (NSDictionary *)finalParametersFrom:(NSDictionary *)dic
- {
- self.rand = [self gainRand];
- NSMutableDictionary *finalDic = [NSMutableDictionary dictionaryWithDictionary:[self baseParameters]];
- [finalDic removeObjectForKey:@ "private_key" ];
- [finalDic addEntriesFromDictionary:dic];
- [finalDic setObject:[self gainSign:dic] forKey:@ "sign" ];
- [finalDic setObject:_timeStr forKey:@ "timestamp" ];
- return finalDic;
- }
- //Basic parameters
- - (NSDictionary *)baseParameters
- {
- //Basic parameter assignment
- NSMutableDictionary *para = [NSMutableDictionary dictionary];
- [para setObject:kApp_id forKey:@ "app_id" ];
- [para setObject:kPrivate_key forKey:@ "private_key" ];
- [para setObject:@ "ios" forKey:@ "plat" ];
- [para setObject:[[[NSBundle mainBundle] infoDictionary] objectForKey:@ "CFBundleShortVersionString" ] forKey:@ "ver" ];
- [para setObject:self.rand forKey:@ "rand" ];
- [para setObject:[self gainTimestamp] forKey:@ "timestamp" ];
-
- if([MDAccountStore isLogined]){
- if(![MDAccountManager sharedInstance].currentAccount){
- [MDAccountManager sharedInstance].currentAccount = [[MDAccountStore sharedInstance] readTheAccount];
- }
- }
- // Check if there is a user logged in. If yes, carry userId
- if ([MDAccountManager sharedInstance].currentAccount.userId) {
- long userId = [MDAccountManager sharedInstance].currentAccount.userId;
- [para setObject:[NSNumber numberWithLong:userId] forKey:@ "userId" ];
- }
- // Check if there is a token in local storage
- if ([MDAccountManager sharedInstance].currentAccount.token) {
- [para setObject:[MDAccountManager sharedInstance].currentAccount.token forKey:@ "token" ];
- }
-
- return [NSDictionary dictionaryWithDictionary:para];
- }
- //Get a random number
- - (NSString *)gainRand
- {
- NSString *str = [NSString string];
- //16 bits
- for ( int i = 0; i < 16; i++)
- {
- // Random letters and numbers
- switch(arc4random() % 3) {
- case 0:
- str = [str stringByAppendingString:[NSString stringWithFormat:@ "%d" , arc4random() % 10]];
- break;
- case 1:
- str = [str stringByAppendingString:[NSString stringWithFormat:@ "%c" , (arc4random() % 26) + 97]];
- break;
- case 2:
- str = [str stringByAppendingString:[NSString stringWithFormat:@ "%c" , (arc4random() % 26) + 65]];
- break;
- }
- }
- return str;
- }
- //Get the time string
- - (NSString *)gainTimestamp
- {
- NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
- dateFormatter.dateFormat = @ "yyyy-MM-dd-HH:mm:ss" ;
- NSString *timeStr = [dateFormatter stringFromDate:[NSDate date ]];
- // NSString *stamp = [NSString stringWithFormat:@ "%ld" , (long)[[NSDate date ] timeIntervalSince1970]];
- return timeStr;
- }
- //Get the sign sign=md5(data+private_key)
- - (NSString *)gainSign:(NSDictionary *)dic
- {
- NSString *sign = [self getMd5_32Bit_String:[self gainData:dic]];
- return sign;
- }
- //Get data+private_key (private_key is included in base)
- - (NSString *)gainData:(NSDictionary *)dic
- {
- //data = base + dic
- NSMutableDictionary *basePara = [NSMutableDictionary dictionaryWithDictionary:[self baseParameters]];
- _timeStr = [self gainTimestamp];
- [basePara addEntriesFromDictionary:dic];
-
- NSArray *arr = [basePara allKeys];
- arr = [arr sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2){
- NSComparisonResult result = [obj1 compare:obj2];
- return result == NSOrderedDescending;
- }];
-
- NSString *dataStr = [NSString string];
- for ( int i = 0; i < arr. count ; i++) {
- dataStr = [dataStr stringByAppendingString:arr[i]];
- dataStr = [dataStr stringByAppendingString:@ "=" ];
- dataStr = [dataStr stringByAppendingString:[NSString stringWithFormat:@ "%@" ,[basePara objectOrNilForKey:arr[i]]]];
- dataStr = [dataStr stringByAppendingString:@ "&" ];
- }
- dataStr = [dataStr substringToIndex:dataStr.length - 1];
- return dataStr;
- }
- //md5 encryption
- - (NSString *)getMd5_32Bit_String:(NSString *)srcString
- {
- const char *cStr = [srcString UTF8String];
- unsigned char digest[CC_MD5_DIGEST_LENGTH];
- CC_MD5( cStr, strlen(cStr), digest );
- NSMutableString *result = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
- for ( int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
- [result appendFormat:@ "%02x" , digest[i]];
-
- return result;
- }
- @ end
-
-
- Author: Qingqing is the boss, so listen to her
- Link: http://www.jianshu.com/p/fe3c2931ed53
- Source: Jianshu
- The copyright belongs to the author. For commercial reproduction, please contact the author for authorization. For non-commercial reproduction, please indicate the source.
After receiving the data, the background will first perform a signature verification operation to verify whether the signature is correct and whether the signature is invalid. - Single sign-on: If the account is logged in by another client, the token sent will change, the previous token will expire, and a login box will pop up when the client sends a data request.
- Expiration time: The time for backend token verification. If the time exceeds the preset time, -100 will be returned.
The json data returned by single sign-on and expiration time is as follows: - {
- "err" :{
- "code" :-100,
- "msg" : Login has expired, please log in again.
- "eventId" : "xxx-xxx-xxx-xxx-xxx"
- }
- }
- Here is a suggestion. We can set a notification listener in the root view. When we receive code = -100 when processing data, it is a login notification, and the login page is displayed modally.
- Data Request Method
- + (void)postRequestWihtUrl:(NSString *)url params:(NSDictionary *)params success:(void (^)(id))successInfo failure:(TPErrorInfo)errorInfo {
- AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
- manager.requestSerializer = [AFHTTPRequestSerializer serializer];
- manager.responseSerializer.acceptableContentTypes = [NSSet setWithArray:@[@ "text/html" , @ "application/json" , @ "application/x-www-form-urlencoded" ]];
-
- /**
- Data Request
- Get basic send parameters
- [[[TPBaseRequest alloc] init] finalParametersFrom:params]
- */
- [manager POST:url parameters:[[[TPBaseRequest alloc] init] finalParametersFrom:params] progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
- TPErrorHandle *err = [[TPErrorHandle alloc] initWithDic:[responseObject objectForKey:@ "err" ]];
- if (err.isError) {
- errorInfo(err);
- return ;
- }
- successInfo(responseObject);
- } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
- NSLog(@ "Request failed information: %@" , error);
- NSDictionary *errDic=error.userInfo;
- NSHTTPURLResponse *response=[errDic objectForKey:@ "com.alamofire.serialization.response.error.response" ];
- NSInteger errCode=response.statusCode;
- MDErrorInfo *err = [[MDErrorInfo alloc] initWithDefault];
- err.code = errCode;
- if (error.code == -1009) {
- err.msg = @ "Network timeout" ;
- }
- else if (err.code == 404){
- err.msg = @ "The network page does not exist" ;
- }
- //The method I use to issue a notification to handle errors is in the TPErrorHandle file
- errorInfo([[TPErrorHandle alloc] initWithMDErrorInfo:err]);
- }];
- }
-
-
- Author: Qingqing is the boss, so listen to her
- Link: http://www.jianshu.com/p/fe3c2931ed53
- Source: Jianshu
- The copyright belongs to the author. For commercial reproduction, please contact the author for authorization. For non-commercial reproduction, please indicate the source.
- There is an eventId field here. I mentioned before that app_id should be changed every time a version is updated.
One week after the APP is updated, we will delete the app_id used by the previous version in the background. If the user continues to use the old version to request data, the following error will appear: - {
- "err" :{
- "code" :-99,
- "msg" : "There is a new version, please update in time" ,
- "eventId" : "Returned update link"
- }
- }
The update notification listener added to the root view will pop up the update page, which greatly improves the update rate of the new version. - When this set of processes is used, the mobile terminal hardly needs to control whether to log in. All judgments on whether to log in are thrown to the background to judge. This approach will waste some server resources, but as long as the front and back ends encapsulate a complete set of processes, the development speed will be greatly improved.
- That's about it for interface encryption. No encryption is unbreakable. All we have to do is to increase the difficulty of cracking. This encryption method will definitely have disadvantages. At this stage, my technology may be too poor, so I can only summarize it like this. Please forgive me if it is not well written.
- ***I want to say something about taking over other people's code. I recently changed jobs and one of the company's own products needs me to take over the development and iteration. This code has been handled by 5 people from 2015 to now. There is no documentation except the code. Everyone has a different style of writing code. The process of getting familiar with other people's code is painful, but you can also make rapid progress in the process of getting familiar with other people's code. No matter whether other people's code is good or bad, you can always find highlights in everyone's code and find skills that you have not used.
|