ngin Finding Everything About Nginx Here

nginx轻松实现api认证

发表于 2015-07-30 阅读数 4126

nginx本身是个极出色的http服务器,除此之外还可以跟应用服务器(比如api)结合,将独立业务(比如认证)分离出来,让应用服务器变的更灵活更强大。本文将以实际例子展现nginx的扩展性能力。

0. 需求:实现电影列表的接口

服务端程序员写了个接口,以提供其它人调用,接口要求以json格式返回一个电影列表数据。他用了世界上最好的语言开发,代码如下:

-- movie.php --

$rows = array(

    array('id' => 1, 'title' => 'Reggae'),

    array('id' => 4, 'title' => 'Indie'),

    array('id' => 5, 'title' => 'Rap'),

    array('id' => 6, 'title' => 'Cowbell')

);

$json = json_encode($rows);

echo $json;

?>

他很开心的提交了任务,但没过多久真正要花时间的考验来了。PM要求他给接口认证,不想让它成为公共接口。

1. 什么是认证

他是个google stackoverflow粉,几番翻墙后,决定以这种方式实现:让访问电影接口前先拿到一个token作为凭据,然后以此token访问接口。所以他把问题简化为两件事:

a)提供生成token的访问

b)在movie.php接口前作access token的验证

看着对接的程序员在那悠哉着等着他实现完提供接口,把接口格式定完先抛给他:

a)生成access_token

http://192.168.1.102/token?appid=some_id&secret=some_secret

b)给电影接口加个参数access_token

http://192.168.1.102/movie.php?access_token=some_token

并且多唠叨了几句:

* 你丫的把appid和secret的值保存好,不能暴露。

* access_token是有过期时间的。

2. 灵活的程序员

首先,他考虑到access_token需要存储、查找,越高效越好,用mysql+memcached or redis缓存吗?这次用点不一样的,快速高效的,于是他选了handlersocket的方式。handlersocket是mysql的一个插件,简单讲就是让操作mysql直通存储层。https://github.com/DeNA/HandlerSocket-Plugin-for-MySQL

其次,他开始不现实的默念:如果有个人帮我把认证处理了,我专心写api就可以,不要让我的代码看到一丝认证的影子。思维总是一张纸而已,分分钟他开始尝试了这个想法。

* 先建oauth_access_token表

CREATE TABLE oauth_access_token (

    id int(10) NOT NULL AUTO_INCREMENT,

    access_token varchar(255) DEFAULT NULL,

    expires_in int(10) NOT NULL,

    last_used_time int(10) NOT NULL,

    PRIMARY KEY (id),

    KEY ACCESS_TOKEN (access_token)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;


* 给api加上认证功能,不用改代码哦,直接在nginx配置里指定

upstream hsock_rsrv {

    server 192.168.100.133:9998;

    keepalive 1024;

}

upstream hsock_wsrv {

    server 192.168.100.133:9999;

    keepalive 1024;

}

server {

    listen       80;

    location / {

        root   html/api;

        index  index.php;

    }

    oauth_db        some_db;

    oauth_table     oauth_access_token;

    # 生成access_token

    location /token {

        oauth_token         hsock_wsrv;  # 指定handlersocket的写服务器,对应上面的upstream

        oauth_appid         some_appid;

        oauth_secret        some_secret;

        oauth_expires_in    2h;

    }

    location /check {

        oauth_check  hsock_rsrv; # 仅供api认证调用,返回200才是正常。外部不会访问

    }

    location ~ \.php$ {

        oauth_request /check; # 让api具备认证功能,对应上面的 location /check

        # 以下就是你的api配置了,以php为例

        root           html;

        fastcgi_pass   127.0.0.1:9000;

        fastcgi_index  index.php;

        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;

        include        fastcgi_params;

    }

}

3. 缺少了什么

细心的你可能发现了,上面有几个指定是nginx不支持的,都是以oauth_开头:oauth_token, oauth_appid, oauth_secret, oauth_expires_in, oauth_check, oauth_request。oauth本身是个协议,有多种方式,从复杂到简单,这里的api认证只是其中一个简单的方式,以oauth这个命名有点哗众取宠,但是挺直观,所以还是用它作为模块的命名。到此,开始安装模块:

> git clone git@github.com:hongzhidao/nginx-http-oauth-module.git

> cd /work/nginx-1.8.0 && ./configure --add-module=/work/nginx-http-oauth-module && make && make install

4. 集思广义

nginx具备很强的扩展能力,如果您的程序是构建在http之上,有些公共的模块可以交给nginx去处理,让程序本身只关注业务部分。github上已经有很多优秀的nginx模块。在nginx方面,我有不少有意思的想法和代码,期待交流。