補充:
從背景看到,好像很多非技術人員從搜尋引擎收到本部落格,好像目的是因為不知道網頁版的微信登入位址。這裡提供一下。
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 }
運作結果,基本每次都是可以随機的。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiInBnauETMzIjMzAzM0EDM2ETMz8CX4ADNxAjMvw1N2ATN4QzLcd2bsJ2Lc12bj5ycn9Gbi52YuAzcldWYtl2Lc9CX6MHc0RHaiojIsJye.jpg)
這裡還有一點要說的,就是随機算法的問題(好像是什麼十大算法之類的),我弄不了,使用的是庫函數。我這個程式的随機種子是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
http://www.zhihu.com/question/20368066 (技術原理來源)
本文位址: http://www.cnblogs.com/wunaozai/p/3947635.html
作者:無腦仔的小明 出處:http://www.cnblogs.com/wunaozai/ 本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。 如果文中有什麼錯誤,歡迎指出。以免更多的人被誤導。有需要溝通的,可以站内私信,文章留言,或者關注“無腦仔的小明”公衆号私信我。一定盡力回答。 |