默认情况下ss3的<x509>标签只会取证书主题作为验证条件,如果想要自己指定证书的某一部分作为验证条件需要手动实现X509PrincipalExtractor接口:
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
public class MyX509PrincipalExtractor implements X509PrincipalExtractor{
Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 获取证书序列号
* @param cert x509证书对象
*/
@Override
public Object extractPrincipal(X509Certificate cert) {
String serialNumber = cert.getSerialNumber().toString(16);//取证书序列号作为判断条件
return serialNumber;
}
}
实现用户描述接口:
public class MyUserAuthority implements UserDetails{
……
}
载入用户信息:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
/**
*
* Company: xxx公司 <br>
*
* Description: 用户信息载入服务
*
* <br>Copyright: Copyright (c) 2010 - 2015
*
* <br>Author: JLCON
* <br>Created:2010-9-17
*
* <br>Modified:2010-9-17
*
* <br>version:V1.0
*/
public class MyUserDetailService implements AuthenticationUserDetailsService{
Logger logger = LoggerFactory.getLogger(this.getClass());
//载入用户信息
@Autowired
private UserAuthorityInfo userinfo;
/**
* 用户信息载入
* @param token 认证token
*/
@Override
public UserDetails loadUserDetails(Authentication token)
throws UsernameNotFoundException {//这里得到的就是刚才返回的证书ID
return userinfo.getUserDetails(token.getPrincipal().toString());
}
}
通过URL获取该URL具有的访问属性:
public class X509securityMetadataSource implements FilterInvocationSecurityMetadataSource{
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
…………
@Override
public Collection<ConfigAttribute> getAttributes(Object object)
throws IllegalArgumentException {
String url = ((FilterInvocation)object).getRequestUrl();
………………
return list;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
}
认证访问控制器:
public class X509AccessDecisionManager implements AccessDecisionManager{
Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 决定是否有权限访问资源
* @param authentication 登录用户权限信息
* @param object 访问的资源对象
* @param configAttributes 资源对象具有的配置属性
* @exception AccessDeniedException 访问被拒绝
*/
@Override
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
FilterInvocation filterInvocation = (FilterInvocation)object;
for(ConfigAttribute configAttribute:configAttributes)
{
for(GrantedAuthority grantedAuthority:authentication.getAuthorities())
{
if(configAttribute.getAttribute().equalsIgnoreCase(grantedAuthority.getAuthority()))
{
logger.debug("访问success! - {}",filterInvocation.getFullRequestUrl());
return;
}
}
}
logger.debug("无权访问! - {}",filterInvocation.getFullRequestUrl());
throw new AccessDeniedException("无权限!");
}
@Override
public boolean supports(ConfigAttribute attribute) {
return true;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
}
最后上配置:
<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<http access-denied-page="/accessdenied.jsp">
<custom-filter position="X509_FILTER" ref="x509Filter"/>
<custom-filter ref="x509Intercepter" before="FILTER_SECURITY_INTERCEPTOR"/>
<intercept-url pattern="/*" requires-channel="https"/>
<port-mappings>
<port-mapping http="8080" https="8443"/>
</port-mappings>
<form-login/>
</http>
<b:bean id="preAuthenticatedProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedProcessingFilterEntryPoint">
</b:bean>
<b:bean id="x509Filter" class="org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter">
<b:property name="authenticationManager" ref="authenticationmanager"></b:property>
<b:property name="principalExtractor">
<b:bean class=".....MyX509PrincipalExtractor"></b:bean>
</b:property>
</b:bean>
<b:bean id="x509Intercepter" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<b:property name="authenticationManager" ref="authenticationmanager"></b:property>
<b:property name="securityMetadataSource" ref="x509securityMetadataSource"></b:property>
<b:property name="accessDecisionManager" ref="x509AccessDecisionManager"></b:property>
</b:bean>
<b:bean id="x509securityMetadataSource" class="....X509securityMetadataSource"></b:bean>
<b:bean id="x509AccessDecisionManager" class="....X509AccessDecisionManager"></b:bean>
<authentication-manager alias="authenticationmanager" >
<authentication-provider ref="x509provider">
</authentication-provider>
</authentication-manager>
<b:bean id="x509provider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<b:property name="preAuthenticatedUserDetailsService" ref="UserDetailsService">
</b:property>
<b:property name="throwExceptionWhenTokenRejected" value="true"></b:property>
</b:bean>
<b:bean id="loggerListener" class="org.springframework.security.authentication.event.LoggerListener"/>
<b:bean id="UserDetailsService" class="....MyUserDetailService"></b:bean>
<b:bean id="UserAuthorityInfo" class="....UserAuthorityInfoImp"></b:bean>
</b:beans>
web.xml