如何搭建Nginx+ElasticSearch实现用户认证并允许跨域CORS调用

ElasticSearch默认没有用户认证,任何人都能访问,这样很不安全,官方的安全解决方案Xpack是收费的,如果只想实现简单的用户认证,可以选择加入Nginx实现基本的用户认证即可。同时因为前端程序需要调用ElasticSearch的REST快速查询数据,所以需要保证ElasticSearch的REST API支持跨域用户调用。

ElasticSearch配置

将ElasticSearch设置为只能本地访问,只需要在配置文件elasticsearch.yaml文件里保证下一行是注释掉的。ElasticSearch默认配置就是只能localhost访问。

#network.host: 0.0.0.0 

同时加入CORS的支持,添加如下字段:

http.cors.enabled : true
http.cors.allow-origin : "*" 
http.cors.allow-methods : OPTIONS, HEAD,GET, POST, PUT, DELETE
http.cors.allow-headers:X-Requested-With,X-Auth-Token,Content-Type,Content-Length,Authorization

注意:

  • http.cors.allow-origin : "*" ,表示允许来自任意源站点的访问,如果想进一步限制,可以设成具体的站点列表,用逗号分隔,比如“a.com, b.com‘’。
  • http.cors.allow-methods定义了允许跨域调用的http方法,只允许REST查询的话,只需要OPTIONS,HEAD,GET即可
  • http.cors.allow-headers定义允许跨域调用的头字段。

Nginx安装配置

生成passwords文件

在ElasticSearch的同一台机器上安装Nginx,安装很简单。因为要在Nginx端作简单的用户认证,需要先生成密码文件(我的工作目录是/home/software/nginx):

htpasswd -c passwords 

按提示输入密码即可,当前目录下会生成一个passwords文件,即我们之后让Nginx使用的passwords文件。

注意:也可用openssl来生成passwords文件,不过我在ubuntu 14.0.2上试,发现不好用,认证总是说密码不对,用htpasswd没问题。

Nginx配置

从系统自带配置文件(/etc/nginx/nginx.conf)copy一份出来,自定义自己的nginx_es.conf,如下: 

upstream elasticsearch {

    server 127.0.0.1:9200;

}

location /  {

       //OPTIONS 设置为不用认证,否则CORS的prefight会有问题。其他操作都需要认证。

        limit_except OPTIONS {

                auth_basic "ProtectedElasticsearch";

                auth_basic_user_file passwords;

         }
        proxy_pass http://elasticsearch;
        proxy_redirect off;
    }
  }

启动Nginx

cd /home/software/nginx
nginx -c $PWD/nginx_es.conf

注意:nginx_es.conf和passwords文件必须在同一目录下,因为默认是到配置文件的同目录找passwords文件。

问题debug

如果认证不通过,首先需要查看Nginx的日志,日志地址:/var/log/nginx,access.log和error.log都需要查看。

常用命令

nginx -s stop  快速关闭nginxnginx -s quit  优雅的关闭nginxnginx -s reload 重新加载配置nginx -s reopen 重新打开日志文件

整个环境搭建配置要点

1. CORS的配置只需要在ElasticSearch上enable,无需再在Nginx上做重复配置。否则返回时会有两个http.cors.allow-origin头字段,前端程序会抛错,错误类似如下:
XMLHttpRequest cannot load http://localhost:41028/api/values/abc. The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed. 
Origin 'http://localhost:44796' is therefore not allowed access
我的理解是ElasticSearch和Nginx上配置一处即可,应该Nginx上配置ElasticSearch上不配置也是可以的。

2. 服务器对OPTIONS方法不要配置用户认证,否则会导致CORS的preflight操作无法成功从而导致REST调用无法成功。从前端看的错误如下:

OPTIONS http://elasticsearch:8080 401
这是因为带Authorization头字段的任何请求都会强制先做OPTIONS请求,但是相应的Authorization头字段并不会复制到OPTIONS请求上,相当于没有认证信息做了一次OPTIONS请求,因此在Nginx端认为401没有权限。详细的CORS介绍见下一小节。

OPTIONS方法不会改变服务器数据,所以不配置用户认证不会带来安全风险。

跨域调用CORS介绍

什么是CORS

CORS(Cross-Origin Resource Sharing ,跨域资源分享)是一种机制,当跨域访问另外站点的资源时,CORS要求使用额外的HTTP头字段来让请求端获得访问所需资源的访问权限。CORS机制保证浏览器和web服务器之间的跨域请求和数据传输的安全性。

CORS标准要求服务器添加HTTP头字段来描述允许来自哪些源网站的请求可以有权限读取相应信息。另外,对于可能导致服务器数据变化的HTTP请求方法(除了GET方法),要求浏览器强制做“preflight”请求,使用HTTP OPTIONS请求得到服务器支持的方法列表,随后再发送实际的HTTP请求。服务器还可以通知客户端发送的请求是否需要包含“credentials”(包括Cookie或者HTTP Authentication数据)。

什么样的请求会使用CORS?

如下跨站点的HTTP请求:

·       跨站点调用的XMLHttpRequest或者Fetch API

·       Web Fonts (for cross-domainfont usage in @font-face within CSS)

·       WebGLtextures.

·       Images/video frames drawn to acanvas using drawImage.

·       Stylesheets (for CSSOM access).

·       脚本

什么情况下不会触发CORS的preflight?

跨域的简单请求不会触发preflight,该请求只能是这三种方法:GET,HEAD,POST。并且头字段只能包含如下字段:

1.       客户端自动添加的字段,比如Connection,User-Agent等

2.    一些手动添加的字段,比如Accept, Accept-Language, Content-Language, Content-Type(只能是三种类型:application/x-www-form-urlencoded,multipart/form-data,text/plain), Last-Event-ID, DPR,Save-Data, Viewport-Width, Width.

交互图

如何搭建Nginx+ElasticSearch实现用户认证并允许跨域CORS调用_第1张图片

请求交互数据

GET /resources/public-data/ HTTP/1.1Host: bar.otherUser-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3preAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language: en-us,en;q=0.5Accept-Encoding: gzip,deflateAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7Connection: keep-aliveReferer: http://foo.example/examples/access-control/simpleXSInvocation.htmlOrigin: http://foo.example
HTTP/1.1 200 OKDate: Mon, 01 Dec 2008 00:23:53 GMTServer: Apache/2.0.61 Access-Control-Allow-Origin: *Keep-Alive: timeout=2, max=100Connection: Keep-AliveTransfer-Encoding: chunkedContent-Type: application/xml
[XML Data]

注意:响应里的头字段Access-Control-Allow-Origin:*,表示服务器允许来自任何源站点的请求。

什么情况下会触发CORS的preflight

上述请求之外的请求都会出发CORS的preflight。

交互图

如何搭建Nginx+ElasticSearch实现用户认证并允许跨域CORS调用_第2张图片

Preflight请求交互数据

OPTIONS /resources/post-here/ HTTP/1.1Host: bar.otherUser-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3preAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language: en-us,en;q=0.5Accept-Encoding: gzip,deflateAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7Connection: keep-aliveOrigin: http://foo.exampleAccess-Control-Request-Method: POSTAccess-Control-Request-Headers: X-PINGOTHER, Content-Type
HTTP/1.1 200 OKDate: Mon, 01 Dec 2008 01:15:39 GMTServer: Apache/2.0.61 (Unix)Access-Control-Allow-Origin: http://foo.exampleAccess-Control-Allow-Methods: POST, GET, OPTIONSAccess-Control-Allow-Headers: X-PINGOTHER, Content-TypeAccess-Control-Max-Age: 86400Vary: Accept-Encoding, OriginContent-Encoding: gzipContent-Length: 0Keep-Alive: timeout=2, max=100Connection: Keep-AliveContent-Type: text/plain

注意:Access-Control-Max-Age表示preflight的响应可以被缓存多久,这段时间之内无需再发起preflight请求。86400秒是24小时。

实际POST请求的交互数据

POST /resources/post-here/ HTTP/1.1Host: bar.otherUser-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3preAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language: en-us,en;q=0.5Accept-Encoding: gzip,deflateAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7Connection: keep-aliveX-PINGOTHER: pingpongContent-Type: text/xml; charset=UTF-8Referer: http://foo.example/examples/preflightInvocation.htmlContent-Length: 55Origin: http://foo.examplePragma: no-cacheCache-Control: no-cache
ArunHTTP/1.1 200 OKDate: Mon, 01 Dec 2008 01:15:40 GMTServer: Apache/2.0.61 (Unix)Access-Control-Allow-Origin: http://foo.exampleVary: Accept-Encoding, OriginContent-Encoding: gzipContent-Length: 235Keep-Alive: timeout=2, max=99Connection: Keep-AliveContent-Type: text/plain 
[Some GZIP'd payload]

参考文章

  1. https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests
  2. https://stackoverflow.com/questions/43871637/no-access-control-allow-origin-header-is-present-on-the-requested-resource-whe/43881141





你可能感兴趣的:(技术日常)