微信的二维码登录是个很酷的功能:客户端登录后,通过扫描web端的二维码,实现统一用在web端的登录
下面要讲的就是如何这一个功能:
实现思路:
web:访问web页面时,如果未登陆,会根据随机字符串生成二维码,显示到页面上,同时将该字符串保存起来,然后页面会通过ajax轮询方式访问web端,查看这个二维码是否可以登录,如果可以登录,则将用户名/id写入session,然后将页面跳转到登录状态
客户端:扫描页面的二维码后,通过http方式访问web端,将二维码源字符串,以及客户端登录用户的用户名/id发送过去,告诉web端这个用户可以登录
思路很简单,实现起来也不麻烦,下面会列一些核心代码,详细的,请看这个demo,代码可以到这里下载
web端:
二维码生成,使用qrcode库:
/* * $source 二维码源字符串 * $temp 图片缓存目录 * $errorCorrectionLevel, $matrixPointSize 错误等级、图片大小 */ function getQRCodeImg($source, $temp, $errorCorrectionLevel, $matrixPointSize){ $date = date('Ymd'); $PNG_WEB_DIR = DIRECTORY_SEPARATOR.$temp.DIRECTORY_SEPARATOR.$date.DIRECTORY_SEPARATOR; $PNG_TEMP_DIR = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'webroot'.DIRECTORY_SEPARATOR.$temp.DIRECTORY_SEPARATOR.$date.DIRECTORY_SEPARATOR; include_once '../lib/qrcode/qrlib.php'; if (!file_exists($PNG_TEMP_DIR)) mkdir($PNG_TEMP_DIR); if (!in_array($errorCorrectionLevel, array('L','M','Q','H'))) $errorCorrectionLevel = 'M'; $matrixPointSize = min(max((int)$matrixPointSize, 1), 10); $filename = md5($source.'|'.$errorCorrectionLevel.'|'.$matrixPointSize).'.png'; QRcode::png($source, $PNG_TEMP_DIR.$filename, $errorCorrectionLevel, $matrixPointSize, 2); return $PNG_WEB_DIR.$filename; } |
页面的ajax相当简单:
//ajax轮询时间间隔,通过服务器配置得到 var ASK_TIME = <?php echo conf('ajax.time');?>; var i = 0; $(function(){ setInterval(function(){ $.get('page_ask.php?source=<?php echo $source;?>', function(d){ //登录成功,跳转页面 if(d && d.login){ window.location = ''; } //测试信息的显示 $('div.test').html(i++ + '---' + JSON.stringify(d)); }, 'json'); }, ASK_TIME * 1000); }); |
page_ask.php的核心代码,服务端存储使用mongodb
//ajax轮询检查是否登录 登录成功返回user,否则为false function checkSource($source){ $collection = getDBCollection(conf('mongo.collection')); $sou = $collection->findone(array('_id' => $source)); if($sou && $sou['login'] === 1){ return $sou['user']; } return false; } |
客户端通过访问“/client_login.php?source=xxx&user=张三”实现登录,当然,实际的系统应该是传userId
//取记录 返回 1 登录成功 10 source有误 11 此source已使用过 21 无此user(暂无此项) function checkSource4Login($source, $user){ $collection = getDBCollection(conf('mongo.collection')); $sou = $collection->findone(array('_id' => $source)); $r = 10; if($sou && isset($sou['login'])){ if($sou['login'] === 0){ $r = 1;//校验正确 }else{ $r = 11; } } //保存登录状态 if($r == 1){ //保存登录状态 $collection->update( array('_id' => $source), array('$set' => array('login' => 1, 'user' => $user)), array('safe'=>true) ); } return $r; } |
下面是客户端的几个片段,这里使用的是ios
二维码扫描使用的库是ZBar,注意,需要添加一坨frameworks
-(IBAction)startScan:(id)sender { ZBarReaderViewController *reader = [ZBarReaderViewController new]; reader.readerDelegate = self; ZBarImageScanner *scanner = reader.scanner; [scanner setSymbology:ZBAR_I25 config:ZBAR_CFG_ENABLE to:0]; [self presentModalViewController:reader animated:YES]; [reader release]; } //将上面这个方法注册给“开始扫描”按钮: [QRBut addTarget:self action:@selector(startScan:) forControlEvents:UIControlEventTouchUpInside]; //扫描成功后,会自动调用此方法 - (void) imagePickerController: (UIImagePickerController*) picker didFinishPickingMediaWithInfo: (NSDictionary*) info { UIImage *image = [info objectForKey: UIImagePickerControllerOriginalImage]; id<NSFastEnumeration> results = [info objectForKey: ZBarReaderControllerResults]; ZBarSymbol *symbol = nil; for(symbol in results){ break; } if(!symbol || !image){ return; } NSLog(@"symbol.data = %@", symbol.data); [self sendRQcode:symbol.data]; [picker dismissModalViewControllerAnimated: YES]; } //imagePickerController |
//取得二维码后,通过http方式发送,然后解析返回的json数据
- (void) sendRQcode:(NSString*)code { NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; NSDate* dat = [NSDate dateWithTimeIntervalSinceNow:0]; NSTimeInterval a=[dat timeIntervalSince1970]; NSString *timeString = [NSString stringWithFormat:@"%f", a]; NSHTTPURLResponse* urlResponse = nil; NSError *error = [[NSError alloc] init]; NSLog(@"-------loginClick------user:%@", timeString); NSString *url = [[[[[@"http://xxxxxx/client_login.php?user=" stringByAppendingString:user] stringByAppendingString:@"&source="] stringByAppendingString:code] stringByAppendingString:@"&time="] stringByAppendingString:timeString]; [request setURL:[NSURL URLWithString:url]]; [request setHTTPMethod:@"GET"]; [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; //发送请求 NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&error]; NSLog(@"-------sendRQcode--status:------%d", [urlResponse statusCode]); NSString *login = @"0"; if(returnData != NULL && [urlResponse statusCode] == 200) { //解析json NSDictionary *data = [returnData objectFromJSONData]; NSLog(@"-------sendRQcode----returnData:------%@", data); login = [data objectForKey:@"login"]; } NSLog(@"-------sendRQcode----login:------%@", login); msg = @""; if([login isEqual: @"1"]){ msg = @"登录成功!"; }else if([login isEqual:@"0"]){ msg = @"网络连接错误,请检查网络设置后重试!"; }else{ msg = [[@"未知错误(login:" stringByAppendingString:login] stringByAppendingString:@"),请检查网络设置后重试!"]; } NSLog(@"-------sendRQcode----msg:------%@", msg); [self alertWithMessage:msg]; }//sendRQcode |
———–
代码就展示这么多,具体的,请访问github:https://github.com/jklgithub/qrcode_login
本文为本人原创,转载请注明地址:http://www.jiangkl.com/2012/10/%E4%BA%8C%E7%BB%B4%E7%A0%81%E7%99%BB%E5%BD%95_qrcode_zbar/
演示地址:dev.jiangkl.com (mongo不太稳定,测试不通的话可以给我留言)