通过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的问题。

相关推荐
玩转4G物联网4 小时前
零基础玩转物联网-串口转以太网模块如何快速实现与TCP服务器通信
服务器·网络·物联网·网络协议·tcp/ip·http·fs100p
hie988945 小时前
HTTP常见的请求方法、响应状态码、接口规范介绍
http
一曝十寒7 小时前
那些常见的 HTTP 状态码
前端·http
眠修10 小时前
Nginx + Tomcat负载均衡群集
nginx·tomcat·负载均衡
酷爱码11 小时前
在 Linux 中修改 Apache HTTP Server(httpd)默认端口的完整指南
linux·http·apache
程序员祥云11 小时前
https相比http的区别
网络协议·http·https
熙客12 小时前
应用层协议:HTTPS
网络协议·https
小吕学编程14 小时前
HttpServletRequest常用方法
java·http
2501_9151063214 小时前
Flutter、React Native 项目如何搞定 iOS 上架?从构建 IPA 到上传 App Store 的实战流程全解析
websocket·网络协议·tcp/ip·http·网络安全·https·udp
snetlogon2016 小时前
JDK17 Http Request 异步处理 源码刨析
android·网络协议·http