ChannelProcessingFilter通常是用来过滤哪些请求必须用https协议, 哪些请求必须用http协议, 哪些请求随便用哪个协议都行.
下面是自动配置SpringSecurity过滤器时的配置方式:
<sec:http entry-point-ref="myAuthenticationEntryPoint" >
<sec:intercept-url pattern="/my_login" requires-channel="https"/>
<sec:intercept-url pattern="/**" access="ROLE_USER"/>
<!-- 映射安全端口. 用于当用户通过非安全端口访问时, 应用会自动跳转用安全端口访问 -->
<sec:port-mappings>
<sec:port-mapping http="8020" https="8021"/>
</sec:port-mappings>
...
</sec:http>
下面是手动配置SpringSecurity过滤器时的配置方式:
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<constructor-arg>
<list>
<sec:filter-chain pattern="/login" filters="none"/>
<sec:filter-chain pattern="/register" filters="none"/>
<sec:filter-chain pattern="/accessDenied" filters="none"/>
<sec:filter-chain pattern="/**/*.js" filters="none"/>
<sec:filter-chain pattern="/**"
filters="channelProcessingFilter,
..."/>
</list>
</constructor-arg>
</bean>
<bean id="channelProcessingFilter" class="org.springframework.security.web.access.channel.ChannelProcessingFilter">
<property name="channelDecisionManager" ref="channelDecisionManager"/>
<property name="securityMetadataSource">
<sec:filter-security-metadata-source request-matcher="ant">
<!-- "/my_login"必须用https协议来发送 -->
<sec:intercept-url pattern="/my_login" access="REQUIRES_SECURE_CHANNEL"/>
<!-- 其他的可以用http或https协议来发送 -->
<sec:intercept-url pattern="/**" access="ANY_CHANNEL"/>
</sec:filter-security-metadata-source>
</property>
</bean>
<bean id="channelDecisionManager" class="org.springframework.security.web.access.channel.ChannelDecisionManagerImpl">
<property name="channelProcessors">
<list>
<ref local="secureChannelProcessor"/>
<ref local="insecureChannelProcessor"/>
</list>
</property>
</bean>
<bean id="secureChannelProcessor" class="org.springframework.security.web.access.channel.SecureChannelProcessor">
<property name="entryPoint" ref="httpsEntryPort"/>
</bean>
<bean id="insecureChannelProcessor" class="org.springframework.security.web.access.channel.InsecureChannelProcessor">
<property name="entryPoint" ref="httpEntryPort"/>
</bean>
<bean id="httpEntryPort" class="org.springframework.security.web.access.channel.RetryWithHttpEntryPoint">
<property name="portMapper" ref="securePortMapper"/>
</bean>
<bean id="httpsEntryPort" class="org.springframework.security.web.access.channel.RetryWithHttpsEntryPoint">
<property name="portMapper" ref="securePortMapper"/>
</bean>
<!-- 这里做http与https端口的映射配置. 如果不配置, 默认的端口映射是"80-443"和"8080-8443" -->
<bean id="securePortMapper" class="org.springframework.security.web.PortMapperImpl">
<property name="portMappings">
<map>
<entry key="8020" value="8021"/>
</map>
</property>
</bean>
下面看看ChannelProcessingFilter的doFilter()方法:
public class ChannelProcessingFilter extends GenericFilterBean {
private ChannelDecisionManager channelDecisionManager;
private FilterInvocationSecurityMetadataSource securityMetadataSource;
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
FilterInvocation fi = new FilterInvocation(request, response, chain);
// 找出当前请求所需要的权限(attr的值可以是: ANY_CHANNEL, REQUIRES_SECURE_CHANNEL, REQUIRES_INSECURE_CHANNEL)
Collection<ConfigAttribute> attr = this.securityMetadataSource.getAttributes(fi);
if (attr != null) {
if (logger.isDebugEnabled()) {
logger.debug("Request: " + fi.toString() + "; ConfigAttributes: " + attr);
}
// 判断当前请求与协议是否相符
channelDecisionManager.decide(fi, attr);
if (fi.getResponse().isCommitted()) {
return;
}
}
chain.doFilter(request, response);
}
...
}
ChannelProcessingFilter主要有两个属性:
channelDecisionManager: 判断当前请求与协议是否相符;
securityMetadataSource: 储存请求与协议的对应关系.
doFilter()方法主要做了两步:
第一步, 找出当前请求所需要的协议. attr的值可以是ANY_CHANNEL(http和https都可以), REQUIRES_SECURE_CHANNEL(必须是https协议), REQUIRES_INSECURE_CHANNEL(必须是http协议).
第二步, 判断当前请求是否与协议相符. 若不相符, 则修改协议并自动跳转. 若相符, 则跳到下一个过滤器.