对于一个中型或大型网站,有n个子项目在不同的服务器甚至不同的IDC部署和运行,SSO(单点登录)和无SESSION已经是必备的功能。在这种情况下用户登陆后的身份验证就会是一个问题。一种简单的解决办法就是登陆时将用户的身份写入cookie,为了安全再写入一个cookie的校验,防止cookie篡改。
1
<?php
2 $secretkey = '1234567890abcdefghi';
3 // …… 取出数据到 $data 的代码略去
4 if( md5( sha1( $password ) ) == $data [ 'passowrd' ] ) { // 登陆成功
5 $_COOKIE [ 'uid' ] = 1234;
6 $_COOKIE [ 'nickname' ] = 'soga';
7 $_COOKIE [ 'token' ] = md5( "uid:1234&nickname:soga&secretkey:1234567890abcdefghi" );
8 }
9 ?>
2 $secretkey = '1234567890abcdefghi';
3 // …… 取出数据到 $data 的代码略去
4 if( md5( sha1( $password ) ) == $data [ 'passowrd' ] ) { // 登陆成功
5 $_COOKIE [ 'uid' ] = 1234;
6 $_COOKIE [ 'nickname' ] = 'soga';
7 $_COOKIE [ 'token' ] = md5( "uid:1234&nickname:soga&secretkey:1234567890abcdefghi" );
8 }
9 ?>
验证登陆合法性:
01
<?php
02 $secretkey = '1234567890abcdefghi';
03
04 if( $_COOKIE [ 'token' ] == md5( "uid: { $_COOKIE [ 'uid' ] } &nickname: { $_COOKIE [ 'nickname' ] } &secretkey: { $secretkey } " ) ) {
05 $login = true;
06 }
07 else {
08 $login = false;
09 }
10 ?>
02 $secretkey = '1234567890abcdefghi';
03
04 if( $_COOKIE [ 'token' ] == md5( "uid: { $_COOKIE [ 'uid' ] } &nickname: { $_COOKIE [ 'nickname' ] } &secretkey: { $secretkey } " ) ) {
05 $login = true;
06 }
07 else {
08 $login = false;
09 }
10 ?>
这样实现有一些问题存在,密钥$secretkey会暴露在所有的程序内,存在安全隐患。所有产品的程序中都需要内置token校验的算法。
如果能在webserver层进行校验,直接告诉应用层校验结果,就可以避免上面的问题。找一种webserver上安全、稳定、高性能的实现,并且开发成本低的方案。我选择的是Nginx + ngx_lua。Nginx的稳定性、高性能搭配Lua的高性能、低成本开发,简直绝配。
Nginx+ngx_lua的编译安装就不在这里讲了。
Nginx 配置:
1
access_by_lua_file
'/dir/test.lua';
test.lua 代码:
01
local
secretkey
=
'
1234567890abcdefghi'
02 if ngx . var . cookie_uid == nil or ngx . var . cookie_nickname == nil or ngx . var . cookie_token == nil then
03 ngx . req . set_header( " Check-Login" , " NULL")
04 return
05 end
06
07 local ctoken = ngx . md5( ' uid:' .. ngx . var . cookie_uid .. ' &nickname:' .. ngx . var . cookie_nickname .. ' &secretkey:' .. secretkey)
08
09 if ctoken == ngx . var . cookie_token then
10 ngx . req . set_header( " Check-Login" , " Yes")
11 else
12 ngx . req . set_header( " Check-Login" , " No")
13 end
14
15 return
02 if ngx . var . cookie_uid == nil or ngx . var . cookie_nickname == nil or ngx . var . cookie_token == nil then
03 ngx . req . set_header( " Check-Login" , " NULL")
04 return
05 end
06
07 local ctoken = ngx . md5( ' uid:' .. ngx . var . cookie_uid .. ' &nickname:' .. ngx . var . cookie_nickname .. ' &secretkey:' .. secretkey)
08
09 if ctoken == ngx . var . cookie_token then
10 ngx . req . set_header( " Check-Login" , " Yes")
11 else
12 ngx . req . set_header( " Check-Login" , " No")
13 end
14
15 return
然后就可以测试一下了:
无cookie登陆测试
01 [root@localhost soft]#
curl -v "http://yuenshui.com:88/test.php"
02 * About to connect() to yuenshui.com port 88
03 * Trying 122.0.66.162… connected
04 * Connected to yuenshui.com (122.0.66.162) port 88
05 > GET /test.php HTTP/1.1
06 > User-Agent: curl/7.15.5 (x86 _64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5
07 > Host: yuenshui.com:88
08 > Accept: */*
09 >
10 < HTTP/1.1 200 OK
11 < Server: nginx
12 < Date: Sun, 24 Jun 2012 10:38:09 GMT
13 < Content-Type: application/octet-stream
14 < Transfer-Encoding: chunked
15 < Connection: keep-alive
16 nil
17 <pre> $ _SERVER:
18 Array
19 (
20 [ TMP ] = > / tmp
21 [ TMPDIR ] = > / tmp
22 [ TEMP ] = > / tmp
23 [ OSTYPE ] = >
24 [ MACHTYPE ] = >
25 [ MALLOC_CHECK_ ] = > 2
26 [ USER ] = > www
27 [ HOME ] = > / home / www
28 [ FCGI_ROLE ] = > RESPONDER
29 [ SERVER_SOFTWARE ] = > nginx
30 [ QUERY_STRING ] = >
31 [ REQUEST_METHOD ] = > GET
32 [ CONTENT_TYPE ] = >
33 [ CONTENT_LENGTH ] = >
34 [ SCRIPT_NAME ] = > / test.php
35 [ REQUEST_URI ] = > / test.php
36 [ DOCUMENT_URI ] = > / test.php
37 [ SERVER_PROTOCOL ] = > HTTP / 1 . 1
38 [ REMOTE_ADDR ] = > 114 . 93 . 76 . 130
39 [ REMOTE_PORT ] = > 1037
40 [ SERVER_ADDR ] = > 122 . 0 . 66 . 162
41 [ SERVER_PORT ] = > 88
42 [ SERVER_NAME ] = > yuenshui.com
43 [ REDIRECT_STATUS ] = > 200
44 [ HTTP_USER_AGENT ] = > curl / 7 . 15 . 5 ( x 86 _ 64 - redhat - linux - gnu) libcurl / 7 . 15 . 5 OpenSSL / 0 . 9 . 8 b zlib / 1 . 2 . 3 libidn / 0 . 6 . 5
45 [ HTTP_HOST ] = > yuenshui.com: 88
46 [ HTTP_ACCEPT ] = > */*
47 [HTTP_CHECK_LOGIN] => NULL
48 [ PHP_SELF ] = > / test.php
49 [ REQUEST_TIME ] = > 1340534289
50 [ argv ] = > Array
51 (
52 )
53
54 [ argc ] = > 0
55 )
56
57 $ _COOKIE:
58 Array
59 (
60 )
02 * About to connect() to yuenshui.com port 88
03 * Trying 122.0.66.162… connected
04 * Connected to yuenshui.com (122.0.66.162) port 88
05 > GET /test.php HTTP/1.1
06 > User-Agent: curl/7.15.5 (x86 _64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5
07 > Host: yuenshui.com:88
08 > Accept: */*
09 >
10 < HTTP/1.1 200 OK
11 < Server: nginx
12 < Date: Sun, 24 Jun 2012 10:38:09 GMT
13 < Content-Type: application/octet-stream
14 < Transfer-Encoding: chunked
15 < Connection: keep-alive
16 nil
17 <pre> $ _SERVER:
18 Array
19 (
20 [ TMP ] = > / tmp
21 [ TMPDIR ] = > / tmp
22 [ TEMP ] = > / tmp
23 [ OSTYPE ] = >
24 [ MACHTYPE ] = >
25 [ MALLOC_CHECK_ ] = > 2
26 [ USER ] = > www
27 [ HOME ] = > / home / www
28 [ FCGI_ROLE ] = > RESPONDER
29 [ SERVER_SOFTWARE ] = > nginx
30 [ QUERY_STRING ] = >
31 [ REQUEST_METHOD ] = > GET
32 [ CONTENT_TYPE ] = >
33 [ CONTENT_LENGTH ] = >
34 [ SCRIPT_NAME ] = > / test.php
35 [ REQUEST_URI ] = > / test.php
36 [ DOCUMENT_URI ] = > / test.php
37 [ SERVER_PROTOCOL ] = > HTTP / 1 . 1
38 [ REMOTE_ADDR ] = > 114 . 93 . 76 . 130
39 [ REMOTE_PORT ] = > 1037
40 [ SERVER_ADDR ] = > 122 . 0 . 66 . 162
41 [ SERVER_PORT ] = > 88
42 [ SERVER_NAME ] = > yuenshui.com
43 [ REDIRECT_STATUS ] = > 200
44 [ HTTP_USER_AGENT ] = > curl / 7 . 15 . 5 ( x 86 _ 64 - redhat - linux - gnu) libcurl / 7 . 15 . 5 OpenSSL / 0 . 9 . 8 b zlib / 1 . 2 . 3 libidn / 0 . 6 . 5
45 [ HTTP_HOST ] = > yuenshui.com: 88
46 [ HTTP_ACCEPT ] = > */*
47 [HTTP_CHECK_LOGIN] => NULL
48 [ PHP_SELF ] = > / test.php
49 [ REQUEST_TIME ] = > 1340534289
50 [ argv ] = > Array
51 (
52 )
53
54 [ argc ] = > 0
55 )
56
57 $ _COOKIE:
58 Array
59 (
60 )
token错误的测试:
01 [root@localhost soft]# curl -b "uid=12345;nickname=soga;token=aa6f21ec0fcf008aa5250904985a817b" "http://yuenshui.com:88/test.php"
02 <pre> $ _SERVER:
03 Array
04 (
05 [ TMP ] = > / tmp
06 [ TMPDIR ] = > / tmp
07 [ TEMP ] = > / tmp
08 [ OSTYPE ] = >
09 [ MACHTYPE ] = >
10 [ MALLOC_CHECK_ ] = > 2
11 [ USER ] = > www
12 [ HOME ] = > / home / www
13 [ FCGI_ROLE ] = > RESPONDER
14 [ SERVER_SOFTWARE ] = > nginx
15 [ QUERY_STRING ] = >
16 [ REQUEST_METHOD ] = > GET
17 [ CONTENT_TYPE ] = >
18 [ CONTENT_LENGTH ] = >
19 [ SCRIPT_NAME ] = > / test.php
20 [ REQUEST_URI ] = > / test.php
21 [ DOCUMENT_URI ] = > / test.php
22 [ SERVER_PROTOCOL ] = > HTTP / 1 . 1
23 [ REMOTE_ADDR ] = > 114 . 93 . 76 . 130
24 [ REMOTE_PORT ] = > 1059
25 [ SERVER_ADDR ] = > 122 . 0 . 66 . 162
26 [ SERVER_PORT ] = > 88
27 [ SERVER_NAME ] = > yuenshui.com
28 [ REDIRECT_STATUS ] = > 200
29 [ HTTP_USER_AGENT ] = > curl / 7 . 15 . 5 ( x 86 _ 64 - redhat - linux - gnu) libcurl / 7 . 15 . 5 OpenSSL / 0 . 9 . 8 b zlib / 1 . 2 . 3 libidn / 0 . 6 . 5
30 [ HTTP_HOST ] = > yuenshui.com: 88
31 [ HTTP_ACCEPT ] = > */*
32 [ HTTP_COOKIE ] = > uid = 12345 ;nickname = soga;token = aa 6 f 21 ec 0 fcf 008 aa 5250904985 a 817b
33 [HTTP_CHECK_LOGIN] => No
34 [ PHP_SELF ] = > / test.php
35 [ REQUEST_TIME ] = > 1340537366
36 [ argv ] = > Array
37 (
38 )
39
40 [ argc ] = > 0
41 )
42
43 $ _COOKIE:
44 Array
45 (
46 [uid] => 12345
47 [nickname] => soga
48 [token] => aa6f21ec0fcf008aa5250904985a817b
49 )
02 <pre> $ _SERVER:
03 Array
04 (
05 [ TMP ] = > / tmp
06 [ TMPDIR ] = > / tmp
07 [ TEMP ] = > / tmp
08 [ OSTYPE ] = >
09 [ MACHTYPE ] = >
10 [ MALLOC_CHECK_ ] = > 2
11 [ USER ] = > www
12 [ HOME ] = > / home / www
13 [ FCGI_ROLE ] = > RESPONDER
14 [ SERVER_SOFTWARE ] = > nginx
15 [ QUERY_STRING ] = >
16 [ REQUEST_METHOD ] = > GET
17 [ CONTENT_TYPE ] = >
18 [ CONTENT_LENGTH ] = >
19 [ SCRIPT_NAME ] = > / test.php
20 [ REQUEST_URI ] = > / test.php
21 [ DOCUMENT_URI ] = > / test.php
22 [ SERVER_PROTOCOL ] = > HTTP / 1 . 1
23 [ REMOTE_ADDR ] = > 114 . 93 . 76 . 130
24 [ REMOTE_PORT ] = > 1059
25 [ SERVER_ADDR ] = > 122 . 0 . 66 . 162
26 [ SERVER_PORT ] = > 88
27 [ SERVER_NAME ] = > yuenshui.com
28 [ REDIRECT_STATUS ] = > 200
29 [ HTTP_USER_AGENT ] = > curl / 7 . 15 . 5 ( x 86 _ 64 - redhat - linux - gnu) libcurl / 7 . 15 . 5 OpenSSL / 0 . 9 . 8 b zlib / 1 . 2 . 3 libidn / 0 . 6 . 5
30 [ HTTP_HOST ] = > yuenshui.com: 88
31 [ HTTP_ACCEPT ] = > */*
32 [ HTTP_COOKIE ] = > uid = 12345 ;nickname = soga;token = aa 6 f 21 ec 0 fcf 008 aa 5250904985 a 817b
33 [HTTP_CHECK_LOGIN] => No
34 [ PHP_SELF ] = > / test.php
35 [ REQUEST_TIME ] = > 1340537366
36 [ argv ] = > Array
37 (
38 )
39
40 [ argc ] = > 0
41 )
42
43 $ _COOKIE:
44 Array
45 (
46 [uid] => 12345
47 [nickname] => soga
48 [token] => aa6f21ec0fcf008aa5250904985a817b
49 )
token正确的测试:
01 [root@localhost soft]# curl -b "uid=1234;nickname=soga;token=aa6f21ec0fcf008aa5250904985a817b" "http://yuenshui.com:88/test.php"
02 <pre> $ _SERVER:
03 Array
04 (
05 [ TMP ] = > / tmp
06 [ TMPDIR ] = > / tmp
07 [ TEMP ] = > / tmp
08 [ OSTYPE ] = >
09 [ MACHTYPE ] = >
10 [ MALLOC_CHECK_ ] = > 2
11 [ USER ] = > www
12 [ HOME ] = > / home / www
13 [ FCGI_ROLE ] = > RESPONDER
14 [ SERVER_SOFTWARE ] = > nginx
15 [ QUERY_STRING ] = >
16 [ REQUEST_METHOD ] = > GET
17 [ CONTENT_TYPE ] = >
18 [ CONTENT_LENGTH ] = >
19 [ SCRIPT_NAME ] = > / test.php
20 [ REQUEST_URI ] = > / test.php
21 [ DOCUMENT_URI ] = > / test.php
22 [ SERVER_PROTOCOL ] = > HTTP / 1 . 1
23 [ REMOTE_ADDR ] = > 114 . 93 . 76 . 130
24 [ REMOTE_PORT ] = > 1059
25 [ SERVER_ADDR ] = > 122 . 0 . 66 . 162
26 [ SERVER_PORT ] = > 88
27 [ SERVER_NAME ] = > yuenshui.com
28 [ REDIRECT_STATUS ] = > 200
29 [ HTTP_USER_AGENT ] = > curl / 7 . 15 . 5 ( x 86 _ 64 - redhat - linux - gnu) libcurl / 7 . 15 . 5 OpenSSL / 0 . 9 . 8 b zlib / 1 . 2 . 3 libidn / 0 . 6 . 5
30 [ HTTP_HOST ] = > yuenshui.com: 88
31 [ HTTP_ACCEPT ] = > */*
32 [ HTTP_COOKIE ] = > uid = 1234 ;nickname = soga;token = aa 6 f 21 ec 0 fcf 008 aa 5250904985 a 817b
33 [HTTP_CHECK_LOGIN] => Yes
34 [ PHP_SELF ] = > / test.php
35 [ REQUEST_TIME ] = > 1340537463
36 [ argv ] = > Array
37 (
38 )
39
40 [ argc ] = > 0
41 )
42
43 $ _COOKIE:
44 Array
45 (
46 [uid] => 12345
47 [nickname] => soga
48 [token] => aa6f21ec0fcf008aa5250904985a817b
49 )
02 <pre> $ _SERVER:
03 Array
04 (
05 [ TMP ] = > / tmp
06 [ TMPDIR ] = > / tmp
07 [ TEMP ] = > / tmp
08 [ OSTYPE ] = >
09 [ MACHTYPE ] = >
10 [ MALLOC_CHECK_ ] = > 2
11 [ USER ] = > www
12 [ HOME ] = > / home / www
13 [ FCGI_ROLE ] = > RESPONDER
14 [ SERVER_SOFTWARE ] = > nginx
15 [ QUERY_STRING ] = >
16 [ REQUEST_METHOD ] = > GET
17 [ CONTENT_TYPE ] = >
18 [ CONTENT_LENGTH ] = >
19 [ SCRIPT_NAME ] = > / test.php
20 [ REQUEST_URI ] = > / test.php
21 [ DOCUMENT_URI ] = > / test.php
22 [ SERVER_PROTOCOL ] = > HTTP / 1 . 1
23 [ REMOTE_ADDR ] = > 114 . 93 . 76 . 130
24 [ REMOTE_PORT ] = > 1059
25 [ SERVER_ADDR ] = > 122 . 0 . 66 . 162
26 [ SERVER_PORT ] = > 88
27 [ SERVER_NAME ] = > yuenshui.com
28 [ REDIRECT_STATUS ] = > 200
29 [ HTTP_USER_AGENT ] = > curl / 7 . 15 . 5 ( x 86 _ 64 - redhat - linux - gnu) libcurl / 7 . 15 . 5 OpenSSL / 0 . 9 . 8 b zlib / 1 . 2 . 3 libidn / 0 . 6 . 5
30 [ HTTP_HOST ] = > yuenshui.com: 88
31 [ HTTP_ACCEPT ] = > */*
32 [ HTTP_COOKIE ] = > uid = 1234 ;nickname = soga;token = aa 6 f 21 ec 0 fcf 008 aa 5250904985 a 817b
33 [HTTP_CHECK_LOGIN] => Yes
34 [ PHP_SELF ] = > / test.php
35 [ REQUEST_TIME ] = > 1340537463
36 [ argv ] = > Array
37 (
38 )
39
40 [ argc ] = > 0
41 )
42
43 $ _COOKIE:
44 Array
45 (
46 [uid] => 12345
47 [nickname] => soga
48 [token] => aa6f21ec0fcf008aa5250904985a817b
49 )
http://yuenshui.com:88/test.php 打印信息的PHP代码:
01
<pre>
<?php
02 echo " \$ _SERVER: \r\n ";
03 unset( $_SERVER [ 'SCRIPT_FILENAME' ]);
04 unset( $_SERVER [ 'DOCUMENT_ROOT' ]);
05 unset( $_SERVER [ 'PATH' ]);
06 unset( $_SERVER [ 'HOSTNAME' ]);
07 unset( $_SERVER [ 'GATEWAY_INTERFACE' ]);
08 print_r( $_SERVER);
09 echo " \r\n\$ _COOKIE: \r\n ";
10 print_r( $_COOKIE);
11 ?>
02 echo " \$ _SERVER: \r\n ";
03 unset( $_SERVER [ 'SCRIPT_FILENAME' ]);
04 unset( $_SERVER [ 'DOCUMENT_ROOT' ]);
05 unset( $_SERVER [ 'PATH' ]);
06 unset( $_SERVER [ 'HOSTNAME' ]);
07 unset( $_SERVER [ 'GATEWAY_INTERFACE' ]);
08 print_r( $_SERVER);
09 echo " \r\n\$ _COOKIE: \r\n ";
10 print_r( $_COOKIE);
11 ?>
上面是所有的测试程序。大家也可以通过curl进行测试http://yuenshui.com:88/test.php