现在,负载均衡器处理https,然后将https传递到我的web服务器。因此,处理每个请求的https加倍。我想做的是完全卸载https,这样我的web服务器就不必处理它了。
如果web服务器认为所有请求都是http,我如何配置Spring安全和JSP页面?显然,我必须修改配置的<intercept-url>
元素,使它们的requires-channel
属性始终是http
或any
。在我的JSP页面中,我必须在<c:url value=''/>
链接前加上${secureUrl}
和${nonSecureUrl}
,这取决于生成的页面是需要https还是http。来自控制器的重定向也需要这样修改……还有别的事吗?
修改JSP页面中的所有链接以包含方案和主机似乎相当痛苦。有更好的方法吗?
如果您在负载均衡器上终止SSL,那么负载均衡器应该发送一个头,指示最初请求的协议。例如,F5增加了x - forward - proto。
从这里你可以创建自定义的ChannelProcessor
查看这个头,而不是查看request.isSecure()
。然后你可以继续使用<intercept-url requires-channel="https">
和相对的<c:url>
。
步骤:
-
子类SecureChannelProcessor和InsecureChannelProcessor覆盖
decide()
。在decide()
中,检查负载均衡器发送的头。@Override public void decide(FilterInvocation invocation, Collection<ConfigAttribute> config) throws IOException, ServletException { for (ConfigAttribute attribute : config) { if (supports(attribute)) { if (invocation.getHttpRequest(). getHeader("X-Forwarded-Proto").equals("http")) { entryPoint.commence(invocation.getRequest(), invocation.getResponse()); } } } }
-
然后使用
BeanPostProcessor
在ChannelDecisionManagerImpl bean上设置这些ChannelProcessors。关于为什么/如何使用BeanPostProcessor
,请参阅Spring Security FAQ。
完整的源代码如下:
对于步骤1:
@Component
public class SecureChannelProcessorHack extends SecureChannelProcessor {
@Override
public void decide(FilterInvocation invocation, Collection<ConfigAttribute> config) throws IOException, ServletException {
for (ConfigAttribute attribute : config) {
if (supports(attribute)) {
if ("http".equals(invocation.getHttpRequest().getHeader("X-Forwarded-Proto"))) {
getEntryPoint().commence(invocation.getRequest(),
invocation.getResponse());
}
}
}
}
}
@Component
public class InsecureChannelProcessorHack extends InsecureChannelProcessor {
@Override
public void decide(FilterInvocation invocation, Collection<ConfigAttribute> config) throws IOException, ServletException {
for (ConfigAttribute attribute : config) {
if (supports(attribute)) {
if ("https".equals(invocation.getHttpRequest().getHeader("X-Forwarded-Proto"))) {
getEntryPoint().commence(invocation.getRequest(),
invocation.getResponse());
}
}
}
}
}
和第二步:
@Configuration
public class LoadBalancerHack implements BeanPostProcessor {
@Inject
SecureChannelProcessorHack secureChannelProcessorHack;
@Inject
InsecureChannelProcessorHack insecureChannelProcessorHack;
@Value("${behind.loadbalancer?false}")
boolean behindLoadBalancer;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (behindLoadBalancer && bean instanceof ChannelDecisionManagerImpl) {
System.out.println("********* Post-processing " + beanName);
((ChannelDecisionManagerImpl) bean).setChannelProcessors(newArrayList(
insecureChannelProcessorHack,
secureChannelProcessorHack
));
}
return bean;
}
}
看起来Grails支持这个作为安全插件的一部分。检查http://grails-plugins.github.com/grails-spring-security-core/docs/manual/guide/17%20Channel%20Security.html的底部部分,他们谈论检查请求头,LB将设置。
grails.plugins.springsecurity.secureChannel.useHeaderCheckChannelSecurity = true
grails.plugins.springsecurity.secureChannel.secureHeaderName = '...'
grails.plugins.springsecurity.secureChannel.secureHeaderValue = '...'
grails.plugins.springsecurity.secureChannel.insecureHeaderName = '...'
grails.plugins.springsecurity.secureChannel.insecureHeaderValue = '...'