補充:
從后台看到,好像很多非技術人員從搜索引擎收到本博客,好像目的是因為不知道網頁版的微信登錄地址。這里提供一下。
https://wx.qq.com/ 點擊鏈接,用手機微信掃描一下就可以登錄了。
正文:
看那個微信電腦端的掃描登錄看起來叼叼噠,找了一篇文章了解一下具體的實現思路和過程,看了牛人的分析后,發現實現起來也不是很難,這里我就簡單的實現了一下。可能功能和流暢度沒有微信做的好,具體是我對前端不是很了解。
好了,不多說,首先要有一個二維碼的生成軟件才行,這里找了一個叫QrenCode的軟件,好處是可以在命令行中進行生成。(下載地址: http://pkgs.org/download/qrencode 找自己合適的下載 應該還要一個依賴庫libqrencode)
1 #QrenCode 下面是centos6.4的安裝命令 2 wget http://dl.fedoraproject.org/pub/epel/6/i386/qrencode-3.4.2-1.el6.i686.rpm 3 wget http://ftp.altlinux.org/pub/distributions/ALTLinux/Sisyphus/i586/RPMS.classic/libqrencode-3.4.3-alt1.i586.rpm 4 rpm -ivh libqrencode-3.4.3-alt1.i586.rpm 5 rpm -ivh qrencode-3.4.2-1.el6.i686.rpm
命令行生成二維碼的方法
生成二維碼格式 qrencode -o [filename.png] ‘[text/url/information to encode]‘ 參數 -o 表示生成到指定文件 -s num 表示生成的二維碼的大小,每個點使用num個像素代替 -v num 表示生成的版本
創建一個login.c文件用於創建一個用於顯示二維碼的cgi (這里用的服務器是我博客中提到的web服務器,雖然還有bug,但是還是夠用的。其他的服務器應該也是可以的。用自己的服務器會不會被罵((逃 )
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 #include <time.h> 5 #include <unistd.h> 6 #include <sys/wait.h> 7 8 int rand_str(char *str,int num) 9 { 10 int i,j; 11 for(i=0;i<num;i++) 12 { 13 j=rand()%3; 14 if(j==0) 15 { 16 str[i]='a'+rand()%26; 17 } 18 else if(j==1) 19 { 20 str[i]='A'+rand()%26; 21 } 22 else if(j==2) 23 { 24 str[i]='0'+rand()%10; 25 } 26 } 27 str[i]=0; 28 return 0; 29 } 30 31 int main(int argc,char **args) 32 { 33 int i; 34 char code[64]; 35 char str[64]; 36 char qc[128]; 37 char filename[64]; 38 char cmd[128]; 39 int status; 40 pid_t pid; 41 srand(time(NULL)); 42 rand_str(code,20); 43 44 45 strcpy(qc,"http://192.168.198.157:8080/login/"); 46 strcat(qc,code); 47 48 strcpy(filename,code); 49 strcat(filename,".png"); 50 51 sprintf(cmd,"qrencode -o www/qc/%s -s 10 '%s'",filename,qc); 52 53 //生成二維碼 54 system(cmd); 55 56 printf("<html><head><title>掃描登錄</title></head><body>"); 57 printf("<img src=\"%s\" />",filename); 58 printf("</body></html>"); 59 60 return 0; 61 }
運行結果,基本每次都是可以隨機的。

這里還有一點要說的,就是隨機算法的問題(好像是什么十大算法之類的),我弄不了,使用的是庫函數。我這個程序的隨機種子是1秒一次,也就是1秒才會變換一次,還有一個問題就是這個srand函數的隨機種子數好像不多,應該是6W多吧,這個如果用戶一多就麻煩了。我這里想到的另一個辦法是根據那個毫秒數來弄(例如用加密算法來對這個毫秒進行加密得到一個字符串,再加個什么的都可以,例如用戶名什么的,最主要的是保證唯一,這個如果實際產品中實現應該不是很難)。
接下來要做的是根據這個url地址再生成一個cgi程序,用於手機APP的調用。我這里想到的辦法是每次刷新這個login頁面時,就復制一個cgi程序,給APP調用。APP的調用機制是根據這個用二維碼生成的url地址發送一個確認包。(確認包使用get方式可以,用post方式也可以,另外用一台服務器也是可以的,這個要看用戶量等具體情況。總之就是這樣了。)
我這里為了演示的方便就采用get方式(原因嘛?我沒有APP這種客戶端,就簡單的在手機上對這個url后面加上個參數了。)
本次實驗用到的3個程序代碼 (代碼中的目錄問題是因為我的服務器設計有缺陷,導致有些目錄要使用相對於程序的絕對路徑,有的可以使用相對路徑,總之我試驗的時候有點坑,不過不影響代碼的可讀性)
login.c
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 #include <time.h> 5 #include <unistd.h> 6 #include <sys/wait.h> 7 8 int rand_str(char *str,int num) 9 { 10 int i,j; 11 for(i=0;i<num;i++) 12 { 13 j=rand()%3; 14 if(j==0) 15 { 16 str[i]='a'+rand()%26; 17 } 18 else if(j==1) 19 { 20 str[i]='A'+rand()%26; 21 } 22 else if(j==2) 23 { 24 str[i]='0'+rand()%10; 25 } 26 } 27 str[i]=0; 28 return 0; 29 } 30 31 int main(int argc,char **args) 32 { 33 int i; 34 char code[64]; 35 char str[64]; 36 char qc[128]; 37 char filename[64]; 38 char cmd[128]; 39 int status; 40 pid_t pid; 41 srand(time(NULL)); 42 rand_str(code,20); 43 44 45 strcpy(qc,"http://192.168.198.157:8080/qc/"); 46 strcat(qc,code); 47 48 strcpy(filename,code); 49 strcat(filename,".png"); 50 51 //生成二維碼 52 sprintf(cmd,"qrencode -o www/qc/%s -s 10 '%s'",filename,qc); 53 system(cmd); 54 55 56 printf("<html><head><title>掃描登錄</title></head><body>"); 57 printf("<img src=\"%s\" />",filename); 58 printf("<br>如果APP上顯示登錄成功那么就點擊該按鈕進行跳轉<br>"); 59 printf("<form method=\"get\" action=\"welcome\">");//這里的action居然不能帶參數,哎前端不會啊 60 printf("<input type=\"hidden\" name=\"code\" value=\"%s\">",code); 61 printf("<input type=\"submit\" value=\"提交\"></form>"); 62 printf("</body></html>"); 63 64 sprintf(cmd,"ln -s callback www/qc/%s",code); 65 system(cmd); 66 67 return 0; 68 }
callback.c
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 int split(char **arr,char *str,const char*del) 6 { 7 char *s=NULL; 8 int i=0; 9 s=strtok(str,del); 10 while(s!=NULL) 11 { 12 *arr++=s; 13 s=strtok(NULL,del); 14 i++; 15 } 16 return i; 17 } 18 19 void split_key(char *ch,char *key,char *value) 20 { 21 int len; 22 int i; 23 int j; 24 len=strlen(ch); 25 j=0; 26 for(i=0;i<len;i++) 27 { 28 if(ch[i]=='=') 29 { 30 i++; 31 break; 32 } 33 key[j]=ch[i]; 34 j++; 35 } 36 key[j]=0; 37 j=0; 38 for(;i<len;i++) 39 { 40 value[j]=ch[i]; 41 j++; 42 } 43 value[j]=0; 44 return ; 45 } 46 47 int main(int argc,char **args) 48 { 49 char *data; 50 char *myargs[32]; 51 int cnt=0; 52 int i; 53 char key[32],value[32]; 54 char username[32],code[32]; 55 char cmd[128]; 56 FILE * fp=NULL; 57 memset(myargs,0,sizeof(myargs)); 58 memset(username,0,sizeof(username)); 59 cnt=split(myargs,args[1],"&"); 60 61 for(i=0;i<cnt;i++) 62 { 63 split_key(myargs[i],key,value); 64 if(strcmp(key,"username")==0) 65 strcpy(username,value); 66 if(strcmp(key,"code")==0) 67 strcpy(code,value); 68 } 69 70 //這里可以寫上完整的網頁和處理過程 71 72 if(username[0]!=0) 73 { 74 printf("<p>通過移動端進行用戶登錄成功,當前登陸的用戶是:</p>"); 75 printf("<font color=\"red\">%s</font> 現在可以在瀏覽器中進行操作了",username); 76 } 77 else 78 { 79 printf("參數錯誤\n"); 80 } 81 82 sprintf(cmd,"www/qc/%s.html",code); 83 fp=fopen(cmd,"w"); 84 fprintf(fp,"您好,用戶:%s 通過APP移動端登錄成功. 采用的uuid是:%s\n",username,code); 85 fclose(fp); 86 87 return 0; 88 }
welcome.c
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h> 5 6 int split(char **arr,char *str,const char*del) 7 { 8 char *s=NULL; 9 int i=0; 10 s=strtok(str,del); 11 while(s!=NULL) 12 { 13 *arr++=s; 14 s=strtok(NULL,del); 15 i++; 16 } 17 return i; 18 } 19 20 void split_key(char *ch,char *key,char *value) 21 { 22 int len; 23 int i; 24 int j; 25 len=strlen(ch); 26 j=0; 27 for(i=0;i<len;i++) 28 { 29 if(ch[i]=='=') 30 { 31 i++; 32 break; 33 } 34 key[j]=ch[i]; 35 j++; 36 } 37 key[j]=0; 38 j=0; 39 for(;i<len;i++) 40 { 41 value[j]=ch[i]; 42 j++; 43 } 44 value[j]=0; 45 return ; 46 } 47 48 int main(int argc,char **args) 49 { 50 char *data; 51 char *myargs[32]; 52 int cnt=0; 53 int i; 54 char key[32],value[32]; 55 char code[32]; 56 char cmd[128]; 57 memset(myargs,0,sizeof(myargs)); 58 cnt=split(myargs,args[1],"&"); 59 60 for(i=0;i<cnt;i++) 61 { 62 split_key(myargs[i],key,value); 63 if(strcmp(key,"code")==0) 64 strcpy(code,value); 65 } 66 67 sprintf(cmd,"www/qc/%s.html",code); 68 //這里可以寫上完整的網頁 69 if(access(cmd,F_OK)==0) 70 { 71 printf("終於登錄了,可以各種操作了"); 72 } 73 else 74 { 75 printf("請確認是否通過APP掃描登陸過"); 76 } 77 78 return 0; 79 }
代碼就放在那里,具體的操作過程就用截圖方式進行講解
(1) 打開服務器,輸入網址: http://192.168.198.157:8080/qc/login

(2)如果沒有通過移動端的的驗證,此時點擊提交是不起作用的。

(3)我們使用手機客戶端進行驗證(由於我沒有設計可用的APP,這里使用GET方式,自己構造一個。注意,如果已經有了客戶端那么,可以通過客戶端APP自行構造一個請求,可以是post,也可以是另外一個action然后進行驗證什么的。總之怎么安全,怎么方便怎么來。)下面這個是我手機通過掃二維碼,然后手動構造一個get請求得到的。(請求如下: http://192.168.198.157:8080/qc/GL199v8zsHV2bu7R7Qad?username=admin&code=GL199v8zsHV2bu7R7Qad)

(4)好了,我們可以在那個login界面上點擊登錄就可以登錄進去了。(這里為什么不像微信那樣,APP客戶端一掃描確認,瀏覽器就直接跳轉,而是要手動點擊提交按鈕呢?那是因為我對前端的技術不是很了解,不知道怎么構建一個長連接,和跳轉什么的。所以就弄成簡單的,手動點擊了,不過這些都不是重點)

(5)下面這些是服務器的一些信息


還有一個問題就是如果訪問的次數多了,那些中間文件就太多了,這時可以通過一個腳本,按時間進行清理。
處理的流程圖

終於完成了。心情有點小激動了。
參考資料: http://www.linuxeden.com/html/softuse/20110328/108018.html
