近期在做sdk底层网络模块封装的时候碰到一个关于客户端身份认证的问题,拿出来分享一下,可以确定的是我的解决办法并不是最优方案。
1.什么是Token 机制?
token的意思是“令牌”,是服务端生成的一串字符串,作为客户端进行请求的一个标识。 当用户第一次登录后,服务器生成一个Token并将此Token返回给客户端,在以后的请求过程中,需要将该Token作为参数或者放在header里面发送给服务器作为身份验证的一个步骤,无需再次带上用户名和密码,但是服务端每隔一段时间(比如30分钟)就会重新生成Token,这时候客户端再用本地缓存的旧Token去请求就无法通过身份认证,一般状态码都会是401。
下面这段字符串就是一个完整的Token
Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6ImEzck1VZ01Gdjl0UGNsTGE2eUYzekFrZnF1RSIsImtpZCI6ImEzck1VZ01Gdjl0UGNsTGE2eUYzekFrZnF1RSJ9.eyJpc3MiOiJodHRwczovL29wZW4uaml1emhhbmcubmV0IiwiYXVkIjoiaHR0cHM6Ly9vcGVuLmppdXpoYW5nLm5ldC9yZXNvdXJjZXMiLCJleHAiOjE1MjM0MzMyMTcsIm5iZiI6MTUyMzQyOTYxNywiY2xpZW50X2lkIjoicm8uY2xpZW50Iiwic2NvcGUiOlsib2ZmbGluZV9hY2Nlc3MiLCJyZWFkIiwid3JpdGUiXSwic3ViIjoianpkZXZAanpkZXYiLCJhdXRoX3RpbWUiOiIxNTIzNDI5NTgwIiwiaWRwIjoiaWRzcnYiLCJhbXIiOlsicGFzc3dvcmQiXX0.fxTyygfF9rXTYm4tbkLucoF5tm0Zs7O2i_E-2NfOl1x_iy7J_sg-lYxk7UQDoCTHiOJKKAV3cQ3jAymHnzmXrgcdtJQFaYE1qGjVSf3qdELIum2kyoOAkfJ_FHOtpgNxhSNgIdWA42_lKl1QPCBoxQOQyQsqB2cKXVLkC2J-4w7Cysf8cjo4PfSgWYoB64-NrMx1WdD4R0Q0nZ_1-2Ky7sB7495NoOBf7IJ6J-IP12FCOjk6e404rH2ZKNlURjDs9dURuP1k_9x3eOsW0C3sdewMAxpmUzhw278FgZcG30v-UuHaWv0DnGP3Y9jg5ob2ktAqGg8gqF0m0-It6bSz7Q
2.解决办法
请求A在得知当前缓存的Token失效后就会重新获取服务端的新Token,然后再继续请求A之后的操作
以objective-c代码描述业务逻辑,会用到部分伪代码
// 401代表用户身份认证失败
if(statusCode == 401)
{
[HttpTool requestWihtUrlString:URL_GetToken params:params success:^(id response, NSUInteger totalCount) {
// 1.缓存Token
[[NSUserDefaults standardUserDefaults] setObject:[dataDic objectForKey:@"access_token"] forKey:@"access_token"];
// 2.重点也就在这里了,iOS中AFN帮你拼接的每一个请求实例NSURLSessionDataTask请求过一次便不再可用 所以我们只能从这个实体类中拿到我们的请求信息重新生成一个datatTask重新请求即可,以AFN发起请求为例,GET 请求最简单,只需要把请求的服务器路径取出来就可以了,而POST,PUT,DELETE等需要从请求题body中把二进制转化为我们想要的参数
NSMutableDictionary *paramDict = [NSMutableDictionary dictionary];
NSString *urlStr = task.originalRequest.URL.absoluteString;
NSString *httpMethod = task.originalRequest.HTTPMethod;
if (![httpMethod isEqualToString:@"GET"])
{
NSStringEncoding encoding = [HttpTool sharedManager].requestSerializer.stringEncoding;
NSData *httpBody = task.originalRequest.HTTPBody;
NSString *paramStr = [[[NSString alloc] initWithData:httpBody encoding:encoding] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSArray *keyValues = [paramStr componentsSeparatedByString:@"&"];
for (NSString *keyValue in keyValues)
{
NSArray *param = [keyValue componentsSeparatedByString:@"="];
NSString *key = param.firstObject;
NSString *value = param.lastObject;
[paramDict setObject:value forKey:key];
}
}
[HttpTool commonRequestWithUrlString:urlStr
method:httpMethod
params:paramDict
success:successBlock
failure:failureBlock];
}