一、https 被redirect成了http
近期项目中踩到一个坑,记录下来,以免后面再踩。
背景:
目前mqrc项目并不是全站的https,而是仅在F5上配置了https,其架构大致下面这样(这里先省略架构中的apache):
.
浏览器
|
|(https)
F5 (LoadBalance) 外网
-----------------------------------------
| | | 内网
|(http)|(http)|(http)
| | |
web web web
应用1 应用2 应用3
也就是说https加密,仅限于浏览器到F5之间,而对web应用来说,所有的请求都是http的,这样如果应用中使用sendRedirect的请求,将会被转发到http。比如该项目中,在filter里的sendRedirect,原先的写法:
// 跳转到登陆页面
httpServletResponse.sendRedirect(httpServletRequest.getContextPath()+"/login");
或者spring MVC中的写法:
//跳转到登录页面
return "redirect:/login";
以上两种写法,当浏览器访问浏览器访问 https://bu.egtcp..com/index 时,其实相当于发了两次请求,第二次浏览器会访问到 http://bu.egtcp..com/login。
解决方案:
a、跳转的时候url使用绝对地址,而不是相对地址
String url = "/login";
// 将相对地址转换成https的绝对地址
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse)resp;
String scheme = request.getScheme();
String serverName = request.getServerName();
int port = request.getServerPort();
String contextPath = request.getContextPath();
String servletPath = request.getServletPath();
String queryString = request.getQueryString();
StringBuilder sbd = new StringBuilder();
// 强制使用https
sbd.append("https").append("://").append(serverName)
if(port != 80 && port != 443) {
sbd.append(":").append(port);
}
if(contextPath != null) {
sbd.append(contextPath);
}
if(servletPath != null) {
sbd.append(servletPath);
}
sbd.append(url);
if(queryString != null) {
sbd.append(queryString);
}
// 绝对地址
response.sendRedirect(sbd.toString());
可以的参考Spring Security的 org.springframework.security.web.util.RedirectUrlBuilder
代码。
b、使用forward方法
在servlet中使用RequestDispatcher的forward方法,它与sendRedirect的主要区别在于forward相当于一次请求,然后服务器端内部,以方法调用的方式转到目标地址。写法如下:
// 跳转到登陆页面
RequestDispatcher view = servletRequest.getRequestDispatcher("/login");
view.forward(servletRequest,servletResponse);
不过使用forward方法,地址栏中看到的只有一个地址,比如https://intlapppdev.95516.com/portal/index,服务器返回了login.jsp页面,但浏览器地址栏没有改变。
c、 在响应信息中设置HTTP状态码和location头信息
进行URL重定向时,服务器设置HTTP状态码和location头信息,当状态码为302时,表明资源位置临时发生了改变,需要进行重定向,location头信息标识了资源转向的位置,该地址写相对地址。写法如下:
//跳转到登录页面
httpServletResponse.setStatus(302);//或者303,兼容http1.1
httpServletResponse.setHeader("location", httpServletRequest.getContextPath()+"/login");
d、 spring mvc中,可以直接配置,将redirectHttp10Compatible属性设为false。如下:
<!-- redirectHttp10Compatible:解决https环境下使用redirect重定向地址变为http的协议,无法访问服务的问题,设置为false,即关闭了对http1.0协议的兼容支持
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/" />
<property name="suffix" value=".jsp" />
<property name="redirectHttp10Compatible" value="false" />
</bean>
事实上,翻一翻spring底层的源码,可以看到,当redirectHttp10Compatible设为false的时候,处理方法也和上面3中提到的方法一致。设置了response头中的location和状态码。
e、 还有可以在Apache设置https反向代理,解决该问题。
参考链接: