搬运 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自己也必 ...
随机推荐
- 实现分页数据请求的思路/Element UI(Plus)的分页模板(Vue3.x写法),(直接使用<script>引入vue.js)
实现分页数据请求的思路/Element UI(Plus)的分页模板(Vue3.x写法),(直接使用<script>引入vue.js) 1. 效果图: 2.实现分页数据请求的思路: 分页 ...
- 32.自定义Java异常捕获处理类及其使用
自定义异常捕获处理类 /** * <h1>异常捕捉</h1> * */ public class ExceptionHandler implements Thread.Unca ...
- drf从入门到飞升仙界 09
接口文档 # 1.前后端分离 - 后端:写接口 - 前端:根据接口写app,小程序,pc端 # 2.作为后端开发 - 我们应该清楚: ---> /api/v1/login/ ---> 登录 ...
- Python3之并发(四)---线程锁
一.线程锁 保证多线程数据的一致性,对锁内的资源进行锁定,同一时间只能有一个线程来修改共享的数据多个线程同时加了同一个锁对象时,先获取到锁的线程会继续运行,未获取到锁的线程会处于堵塞状态,直到前面的线 ...
- Navicat连接Oracle时报错ORA-28547:完美解决
1. 先用你的IDEA或者别人的连接到oracle数据库(为了查询版本) 1.1 查询版本SQL:select * from v$version; 2. 引入对应的oci.dll文件 链接:https ...
- Position Based Dynamics【译】
绝大部分机翻,少部分手动矫正,仅供参考.本人水平有限,如有误翻,概不负责... Position Based Dynamics Abstract The most popular approaches ...
- 「SOL」E-Lite (Ural Championship 2013)
为什么这数据能水到可以枚举角度 ac 啊 # 题面 给你 \(n\) 个平面向量 \((x_i,y_i)\),对于每个 \(k=1\sim n\),求「从给出的 \(n\) 个向量中不重复地选择 \( ...
- 关于Java字符串、字符与数字之间的相互转换
一.数字转字符串 //将整个数字转化为字符串 int i=456; //方法一 String str1=Integer.toString(i); System.out.println(str1); / ...
- 音标s ed
1 p /s/ cups 2 t /s/ hats puts3 k /s/ cakes books desks works worked /t/4 f /s/ roofssiz ziz s加其他清辅 ...
- 修改element-ui 面包屑的样式
开发者模式,一层层的找啊 .el-breadcrumb__item{ .el-breadcrumb__inner{ &.is-link{ } } } 第二层的class 使用了& 如果 ...