通过https协议访问Tomcat部署并使用Shiro认证的应用跳转登到录页时协议变为http的问题

问题描述:

在最近的一个项目中,有一个存在较久,并且只在内部城域网可访问的一个使用Shiro框架进行安全管理的Java应用,该应用部署在Tomcat服务器上。起初,应用程序可以通过HTTP协议访问,一切运行都没问题。然而,当我们决定切换到HTTPS协议以增强客户端(通常是网络浏览器)与服务器之间的安全通信时,问题随之产生,主要问题现象如下:

例如登录页面的URL是https://***/login,任何尝试访问受Shiro保护的页面的操作都应该重定向用户到这个登录页面。但问题在于,即使在启用HTTPS协议并尝试使用HTTPS协议访问这些受保护的页面,系统仍然不断地将浏览器重定向到http://***/login,这就启用HTTPS协议后遇到的主要问题。

我们部署架构中一个值得注意的方面是,网络前端有一个物理负载均衡器,负责处理SSL认证。通过HTTPS协议发出的请求首先到达这个负载均衡器,然后再通过HTTP协议转发到Tomcat Web服务器,结构如下图,其中响应中的Location Header在用户使用https访问页面时,预期应该返回https://***/login,但实际却返回了http://***/login

问题解决:

想了下,问题应该就是因为负载卸载了证书实际到达Tomcat的请求协议是HTTP的,导致Shiro识别到的请求协议是HTTP,所以跳转地址便使用了HTTP而不是实际的HTTPS。

先是查了下资料,定位到Shiro会根据一个Header名字为X-Forwarded-Proto(值是https或http)来获取通过反向代理进来的请求的实际使用协议(因为大多数SSL证书处理都是在反向代理上卸载掉, 导致到达应用时的请求都是HTTP)。同时Tomcat的server.xml需要增加配置来识别这个Header用来初始化HttpServletRequest内的属性, 实际影响的就是request.getScheme()这个方法的返回值。

具体修改步骤:

  • 第一步:修改反向代理配置增加X-Forwarded-Proto Header

如果是硬负载比如A10或者F5,例如针对https协议需要配置如下 ,一般针对https和http协议会建立两个模板分别处理443端口和80端口的请求。

如果是Nginx,是下面这样配置:

  • 第二步:修改Tomcat配置

在Tomcat的server.xml配置文件中<Engine><Host>标签下增加如下配置,其中remoteIpHeader这个是用于获取外部实际ip的,一般一并加上了。protocolHeaderHttpsValue也可以不加因为https是默认值,加上这个一般一般情况下Java代码通过request.getScheme()方法就可以获取用户请求实际使用的协议了,Shiro也就可以根据实际协议去拼装登录页面URL了。

XML 复制代码
<Valve className="org.apache.catalina.valves.RemoteIpValve"
       remoteIpHeader="x-forwarded-for"
       protocolHeader="x-forwarded-proto"
       protocolHeaderHttpsValue="https" />
  • 第三步: Tomcat额外配置(可选)

我这里配置了以上两步以后,后端request.getScheme()方法仍然获取的是http,因为知道scheme这个值是RemoteIpValve这个类处理的,就看了下代码,发现server.xml中关于RemoteIpValve还有一个配置项,叫internalProxies是一个关于ip的正则表达式,代码中有一个if条件,只有当反向代理的ip可以通过正则匹配成功才会处理x-forwarded-*的header。并且Tomcat 7中正则缺少172段ip,更新版本的Tomcat可以匹配**10.x.x.x, 192.168.x.x, 169.254.x.x, 127.x.x.x, 和172.16.x.x172.31.x.x。 所以当反向代理服务器或者设备的ip不在以上范围的时候需要主动在RemoteIpValve配置中增加**internalProxies的配置,如下(这里是Tomcat 7所以需要手动处理172段ip):

XML 复制代码
<Valve className="org.apache.catalina.valves.RemoteIpValve"
       internalProxies="172\.31\.1\.22"
       remoteIpHeader="x-forwarded-for"
       protocolHeader="x-forwarded-proto"
       protocolHeaderHttpsValue="https" />

总结

成功修改以上配置后,Shiro可以正常通过request.getScheme()获取用户请求使用的实际协议,也就解决了用户通过https访问页面但是跳转登录页面时协议变成了http的问题。

相关推荐
purrrew2 小时前
【Java ee初阶】HTTP(2)
网络·网络协议·http
火星数据-Tina2 小时前
从HTTP轮询到WebSocket:如何让体育API性能提升100倍?
websocket·网络协议·http
神经毒素7 小时前
WEB安全--Java安全--shiro550反序列化漏洞
java·安全·web安全·shiro
李迟7 小时前
Golang实践录:在go中使用curl实现https请求
开发语言·golang·https
Think Spatial 空间思维7 小时前
【HTTPS基础概念与原理】TLS握手过程详解
数据库·网络协议·https
2501_915909068 小时前
开发日常中的抓包工具经验谈:Charles 抓包工具与其它选项对比
websocket·网络协议·tcp/ip·http·网络安全·https·udp
北极象12 小时前
Go语言处理HTTP下载中EOFFailed
开发语言·http·golang
{⌐■_■}15 小时前
【gRPC】HTTP/2协议,HTTP/1.x中线头阻塞问题由来,及HTTP/2中的解决方案,RPC、Protobuf、HTTP/2 的关系及核心知识点汇总
网络·网络协议·计算机网络·http·rpc·golang
越来越无动于衷15 小时前
手写tomcat:基本功能实现(3)
java·tomcat