问题
Right now, the load balancers handle https and then pass along that https to my web servers. So dealing with https double for each request. What I want to do is completely offload https so my web servers don't have to deal with it.
How do I configure Spring Security and JSP pages given that the web servers think all requests are http? Obviously I'll have to modify the <intercept-url>
elements of my configuration to have their requires-channel
attribute always be http
or any
. In my JSP pages I'll have to prepend the <c:url value=''/>
links with a ${secureUrl}
and ${nonSecureUrl}
depending whether the resulting page needs to be https or http. Redirects from controllers need to be modified like this as well... Anything else?
Seems like quite a pain to modify all links in JSP pages to include the scheme and host too. Is there a better way to do that?
回答1:
If you terminate SSL at the load balancer then your load balancer should send a header indicating what protocol was originally requested. For example, the F5 adds X-Forwarded-Proto.
From here you can create custom ChannelProcessor
s that look at this header instead of looking at request.isSecure()
. Then you can continue using <intercept-url requires-channel="https">
and relative <c:url>
.
The steps:
Subclass SecureChannelProcessor and InsecureChannelProcessor overriding
decide()
. Indecide()
check the header sent by your load balancer.@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()); } } } }
Then set these ChannelProcessors on the ChannelDecisionManagerImpl bean using a
BeanPostProcessor
. See this Spring Security FAQ on why/how to use aBeanPostProcessor
for this.
回答2:
To complete the great sourcedelica answer, here is the full code :
For Step 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());
}
}
}
}
}
And step 2 :
@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;
}
}
回答3:
Looks like Grails supports this as a part of the security plugin. Checkout the bottom section of http://grails-plugins.github.com/grails-spring-security-core/docs/manual/guide/17%20Channel%20Security.html where they talk about checking request headers, which the LB will set.
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 = '...'
来源:https://stackoverflow.com/questions/8002272/offloading-https-to-load-balancers-with-spring-security