搬运 nginx代理https
oauth2-client在Nginx代理后遇到的问题和解决方案
30 MINUTES READ (ABOUT 4442 WORDS)
OAuth2 Client在实际运用过程中遇到的问题
服务程序集成了OAuth2-Client,以便于用户能够方便集成到支持OAuth2第三方登录的自有业务系统中。开发完成后,本地测试、或者直连服务程序,都没有问题。但凡放到线上环境,经过了nginx 转发后,我们的服务程序OAuth登录永远是以失败告终。
现象如下:
访问需要授权的接口时 https://blog.95id.com:4005/user_attr
,期望是跳转到授权服务器 github.com
进行登录授权,但实际都是跳转到http://blog.95id.com/login`
因为当时直接用服务程序的端口没问题,就将解决思路放在了nginx 转发过程上。
当时线上环境路由规则类似于:
第一层:nginx1 4005 (ssl、负载均配置在这)
第二层:nginx2 4005
第三层:oauth2-client 8082
再看nginx 的配置,第一层nginx 配置:
1 |
server { |
第二层nginx 的配置如下:
1 |
server { |
因为当时nginx配置比较复杂,怎么调oauth的配置都不对,就对问题进行一个个简化拆分,一个问题一个问题的解决
场景一:nginx 80 代理 oauth-client 8082
有了nginx ,就会出现异常,那么从最简单的场景开始测试。
程序配置:
1 |
server.port=8082 |
nginx 配置:
nginx.conf
1 |
server { |
查看请求重定向详情
1 |
curl -Lv http://blog.95id.com/user_attr |
跳转一切正常
场景二: 非80端口nginx 代理 oauth-client 8082
用 4005 端口 代理 oauth-client的8082
nginx配置
1 |
server { |
这时候打印请求详情
1 |
curl -Lv http://blog.95id.com:4005/user_attr |
如果把 nginx 的port_in_redirect 配置设置为 on,结果也是一样的,这个跳转是oauth-client程序内 spring-security-oauth2 自动做的跳转,也就是spring-security-oauth2 未能正常拿到4005 这个端口
再来看应用程序日志(DEBUG模式下)
1 |
2020-01-14 10:58:43.388 DEBUG 1918 --- [http-nio-8082-exec-7] o.a.tomcat.util.net.SocketWrapperBase : Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@5fc01a1e:org.apache.tomcat.util.net.NioChannel@58241aaa:java.nio.channels.SocketChannel[connected local=/127.0.0.1:8082 remote=/127.0.0.1:43746]], Read from buffer: [0] |
从打印的日志看,因为user_attr
需要授权允许,所以跳转到 /login
。但是当时spring-security已经拿不到4005端口了
打印的request信息也证实了,4005丢失不见了
1 |
{ |
经过查找几次尝试,解决方法是在nginx -server 中加入配置:
1 |
proxy_set_header Host $host:$server_port; |
这时request信息中,ServerPort = 4005
1 |
|
该方案解决了端口丢失的问题,接着解决 https 变成 http的问题
场景三:HTTS 443端口 代理 oauth-client 8082
443 是 ssl的默认端口,场景二修改监听端口,再加上 ssl的配置,nginx配置如下:
nginx配置如下:
1 |
server { |
问题现象:
访问:https://blog.95id.com/user_attr
被redirect to :http://blog.95id.com/login
浏览器报 400异常(因为重定向的是http,没配80端口)
1 |
400 Bad Request |
curl 超时(因为重定向的是http,没配80端口)
1 |
[root@VM_0_17_centos oauth]# curl -Lv https://blog.95id.com/user_attr |
这样配置发现 https 被重定向到 http,并且htts默认的443端口,变成了http的默认端口80
解决方案:
在oauth-client 配置文件中
1 |
server.tomcat.remote-ip-header=x-forwarded-for |
nginx 配置中加入
1 |
proxy_set_header X-Forwarded-Proto $scheme; |
测试可行
最后nginx配置
1 |
server { |
场景四 HTTPS 非443端口代理oauth-client 8082
开启ssl,用4005端口代替默认端口443。并且沿用场景二的方案,便于将端口同时带过来,可是还是出问题了
问题现象
沿用场景三的解决方案,并且带了场景二的方案,发现还是无法重定向到oauth2-server进行登录
访问:https://blog.95id.com:4005/user_attr
最后被重定向到:https://blog.95id.com/login
nginx 配置和场景三类似,只是修改了端口
1 |
server { |
https 是被带过来了,但是 端口4005丢失了,浏览器报不是私密链接(因为443端口没配ssl),curl 报连接超时 443端口没做转发
在场景二中配置的
1 |
proxy_set_header Host $host:$server_port; |
也在其中,但是还是未生效
解决方案
在nginx 配置中增加
1 |
proxy_set_header X-Forwarded-Port $server_port; |
在oauth2-client 的配置文件application.properties中添加:
application.properties
1 |
server.tomcat.port-header=X-Forwarded-Port |
改动后的nginx 配置如下
1 |
server { |
oauth-client 配置
1 |
server.tomcat.remote-ip-header=x-forwarded-for |
其他尝试
测试发现X-Forwarded-Port
这个配置是否能可以解决端口丢失的问题,那么是否能用来解决场景二的问题?回到场景二的配置
1 |
#关闭ssl |
oauth-client 配置保持和场景三方案一样
测试的nginx 配置:
1 |
server { |
测试通过,也就是 proxy_set_header Host $host:$server_port;
可以通过X-Forwarded-Port
方案替代
场景五 双层nginx 转发 https协议头 port丢失
上述场景都是单层的nginx 环境,如果用上述配置放到线上(也就是开篇描述的现象),还是出了问题。
但是单层nginx的解决了,多层的还会远吗?而且经过这么多场景的模拟测试,该问题的本质也逐渐明了,就是nginx 在转发request过程中https和port丢失的问题,多层的情况就是要解决第一层nginx 到最内层的nginx 中一些参数的传递问题
第一层:nginx1 4005 (ssl、负载均配置在这)
第二层:nginx2 4005
第三层:oauth2-client 8082
问题现象
访问:https://blog.95id.com:4005/user_attr
最后被重定向到:http://blog.95id.com/login
那么经过这么多场景,能够很清晰的定位问题: 协议头和port在nginx 代理过程中丢失,最有可能是第一层nginx 到 第二层nginx 过程中丢失。
另外在最外层nginx 配置了场景4nginx的配置,port不丢失了,但是最终还是重定向到了 http://blog.95id.com:4005/login,https丢了
解决方案
两种解决方案:
两个方案均需要在外层的nginx 上将https 往下层传,所以都需要:
1 |
proxy_set_header Host $host; |
如果是多层,就是要将 schema 和 port 层层往下传
1)方案一:强制使用https
如果nginx服务并没有直接面向最终用户,而是在某些负载均衡/cdn后面,并且ssl证书是在这些负载均衡/cdn上面配置的,那么有可能会导致nginx无法正确获取客户端所使用的协议,从而导致无法将客户端使用的协议传递给最后面的应用程序。在这种情况下,可以修改nginx配置,强制通知服务使用https协议,也就是不使用 $scheme 变量,而是写死 https。
1 |
#proxy_set_header X-Forwarded-Proto $scheme; |
2) 方案二:利用$http_x_forwarded_proto
变量
除了最外层的nginx ,其余层的nginx在的http 内配置:
1 |
map $http_x_forwarded_proto $thescheme{ |
在转发服务的nginx -server 中配置:
1 |
#proxy_set_header X-Forwarded-Proto $scheme; |
Bingo !尝试大几十遍终于都可以了
终极配置方案
上述这些场景导致OAuth2-Client 无法正常使用,本质原因还是因为 request的schema 和port 无法下传到 应用程序导致的。如果java应用程序能正常拿到,就不存在这些问题,所以只需要解决,request从nginx到client,不丢失相关信息即可
通过这些配置,发现后面方案是可以兼容解决前面的场景,所以这里可以给个最终的配置方案
不管是 默认端口代理,还是开启Https ,还是层层代理,这个配置都可以使OAuth2Client 生效
1)在应用程序的主配置文件中加入:
1 |
server.tomcat.remote-ip-header=x-forwarded-for |
2)在nginx 配置中,http模块下,加一个map设置thescheme
参数用来传递scheme,在代理服务的server配置中,加入header
相关配置(proxy_set_header
开头):
1 |
http { |
3)在springboot应用程序中加入如下配置
1 |
server.tomcat.remote-ip-header=x-forwarded-for |
如果是非内嵌的tomcat , 则需要修改tomcat 的配置(无tomcat,未测试)
1 |
<!-- 放在<Valve className="org.apache.catalina.valves.AccessLogValve">的后面(同级关系)--> |
测试用的OAuth2-Client源码 和 nginx 最终配置方案可以见 https://github.com/ifengkou/oauth2-client-157
补习 springboot 下 HTTP HEADER 参数知识
server.tomcat.port-header
设定http header使用的,用来覆盖原来port的value.server.tomcat.protocol-header
设定Header包含的协议,通常是 X-Forwarded-Proto,如果remoteIpHeader有值,则将设置为RemoteIpValve.server.tomcat.protocol-header-https-value
设定使用SSL的header的值,默认https.server.tomcat.remote-ip-header
设定remote IP的header,如果remoteIpHeader有值,则设置为RemoteIpValveserver.use-forward-headers=true
该配置将指示tomcat从HTTP头信息中去获取协议信息(而非从HttpServletRequest中获取),同时,如果你的应用还用到了spring-security则也无需再配置。
补习Java 程序获得相关请求信息
可以具体见Oauth2-client 项目中 Oauth2Client157Application文件内的 printRequest方法
1 |
List<String> headerInfo = new ArrayList<>(); |
访问:https://blog.95id.com:4005/print_request
1 |
{ |
补习 Nginx 参数配置
nginx ssl 证书配置
1 |
ssl on; |
配置自动获取协议方式
通过这种方式配置,客户端可以使用http或者https协议,应用服务能够自动获取客户端使用的协议。
配置nginx 反向代理 proxy_set_header 。 在server里面,增加下面的配置:
1 |
server { |
如果您的nginx有多层,那么,您可能还需要额外的配置: 在http 模块中加入以下配置:
1 |
map $http_x_forwarded_proto $thescheme{ |
然后在server模块(或者http模块)里面,将前面配置中的proxy_set_header X-Forwarded-Proto $scheme;
,替换为下面的代码:
1 |
proxy_set_header X-Forwarded-Proto $thescheme; |
如果您的nginx服务,并没有直接面向最终用户,而是在某些负载均衡/cdn后面,并且您的ssl证书是在这些负载均衡/cdn上面配置的,那么有可能会导致nginx无法正确获取客户端所使用的协议,这时可以强制https
1 |
proxy_set_header X-Forwarded-Proto "https"; |
- 本文标题:oauth2-client在Nginx代理后遇到的问题和解决方案
- 本文作者:LoganShen
- 本文链接:https://blog.95id.com/oauth2-client-nginx-bugs.html
- 发布时间:2020-01-17
- 版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
搬运 nginx代理https的更多相关文章
- nginx代理https站点(亲测)
nginx代理https站点(亲测) 首先,我相信大家已经搞定了nginx正常代理http站点的方法,下面重点介绍代理https站点的配置方法,以及注意事项,因为目前大部分站点有转换https的需要所 ...
- nginx 代理 https 后,应用变成 http
需求:nginx 代理 https,后面的 tomcat 处理 http 请求,sso 的客户端,重定向时需要带上 target,而这个 target 默认是 tomcat 的 http,现在需要把这 ...
- nginx 代理https后,应用redirect https变成http --转
原文地址:http://blog.sina.com.cn/s/blog_56d8ea900101hlhv.html 情况说明nginx配置https,tomcat正常http接受nginx转发.ngi ...
- 开启Nginx代理HTTPS功能
1.首先查看是否已经安装SSL openssl version -a 2.生成SSL证书 在nginx目录下创建ssl文件夹 cd /etc/pki mkdir nginx cd nginx 生成20 ...
- 关于配置websocket,nginx转发https至wss问题
在本地测试通过的socket,再放到现在的有nginx代理之后发现会报:failed: Error in connection establishment: net::ERR_NAME_NOT_RES ...
- Nginx设置Https反向代理,指向Docker Gitlab11.3.9 Https服务
目录 目录 1.GitLab11.3.9的安装 2.域名在阿里云托管,申请免费的1年证书 3.Gitlab 的 https 配置 4.Nginx 配置 https,反向代理指向 Gitlab 配置 目 ...
- 使用nginx代理后以及配置https后,如何获取真实的ip地址
使用nginx代理后以及配置https后,如何获取真实的ip地址 Date:2018-8-27 14:15:51 使用nginx, apache等反向代理后,如果想获取请求的真实ip,要在nginx中 ...
- nginx通过https方式反向代理多实例tomcat
案例说明:前面一层nginx+Keepalived部署的LB,后端两台web服务器部署了多实例的tomcat,通过https方式部署nginx反向代理tomcat请求.配置一如下: 1)LB层的ngi ...
- centos7 下 apache nginx squid https正向代理 代理服务器
apache yum install httpd mod_ssl -y vim /etc/httpd/conf.d/ssl.conf Listen https <VirtualHost *:&g ...
- nginx 反向代理https
nginx 反向代理https 原来我用vertx创建了一个https apiserver,想着用nginx反向代理一下.证书是阿里云上免费一年的. 后来发现nginx要反向代理https自己也必 ...
随机推荐
- 常见的SPA首屏优化方式
核心是加载和解析的性能优化 加载优化的核心是资源体积和首屏资源数量. 解析优化的核心是资源体积和代码的执行性能. 加载优化 通过webpack 的code splitting合理分包: Code ...
- 怎么理解超几何分布概率公式:p=C(M,k)C(N-M,n-k)/C(N,n)
怎么理解超几何分布概率公式:p=C(M,k)C(N-M,n-k)/C(N,n) 前言:重在记录,可能出错. 超几何分布概率公式:p=C(M,k)C(N-M,n-k)/C(N,n),也就是: 到底要怎么 ...
- No.1.5
优先级:不同选择器具有不同的优先级,优先级高的选择器样式会覆盖优先级低选择器的样式 优先级公式:!imporant>行内样式>id选择器>类选择器>标签选择器>通配符选择 ...
- Es6中模块引入的相关内容
注意:AMD规范和commonJS规范 1.相同点:都是为了模块化. 2.不同点:AMD规范则是非同步加载模块,允许指定回调函数.CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行 ...
- PL/SQL Initialization error Could not initialize 问题
问题: PL/SQL Initialization error Could not initialize 问题 参考链接: https://blog.csdn.net/luoyanjiewade/ar ...
- SHR之员工合同解除
员工合同解除HRContractInfoFacadeControllerBean 这块的意思的源码可以自行翻阅该源码. 调用员工的实现这个合同自动解除 String sql="select ...
- js 表格分页,ajax请求后台数据前台分页
$(function(){ var url="后台给的地址"; var shuju=document.getElementById("shuju"); cons ...
- 供配电一次测 PT柜 解释
文章来源: PT柜_百度百科 (baidu.com) 视频介绍 很多电工不清楚的高压PT柜,老电工带你了解工作原理和作用_搜狐汽车_搜狐网 (sohu.com) 很多电工怕PT柜,今天电气成套设计实 ...
- redhat用unbound配置DNS
redhat配置unbound 1.配置IP地址 2.配置本地yum 3.安装unbound,没有nslookup命令自行安装bind-utlis 4.配置unbound.conf(vi /etc/u ...
- HDFS 内部工作机制
HDFS 内部工作机制 HDFS集群分为两大角色:NameNode.DataNode (Secondary Namenode) NameNode 负责管理整个文件系统的元数据 DataNode 负责管 ...