【问题标题】:Spring Security - custom filterSpring Security - 自定义过滤器
【发布时间】:2014-07-28 11:32:18
【问题描述】:

我正在使用这个环境:

  • 春天 4.0.5
  • spring security 3.2.4

在我的环境中,我有一个 SSO 系统,我需要将我的 Web 应用程序集成到这个系统中。该系统是私有产品。 SSO机制的最终结果是在请求头中放了一个参数。所以我应该在我的应用程序中做的是:

  • 检查请求中是否存在标头参数
  • 如果头参数不存在,我必须重定向到 本系统登录页面
  • 如果存在标头参数,我必须在 Spring 安全上下文

此场景类似于 CAS 集成场景;所以我从CAS集成开始;我编写了自定义过滤器,编写了自定义入口点和处理请求所需的所有其他类,但我遇到了一些问题;现在我可以在 spring 上下文中进行身份验证,但是在经过身份验证之后,就像我没有经过身份验证一样。也许我错过了什么。

我将报告所有涉及的类和我的安全上下文。

security-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:security="http://www.springframework.org/schema/security"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
        <!-- Nessuna sicurezza per avere i CSS e JS -->
        <security:http pattern="/css/**" security="none"/>
        <security:http pattern="/DataTables-1.9.4/**" security="none"/>
        <security:http pattern="/images/**" security="none"/>
        <security:http pattern="/jquery-ui-1.10.3/**" security="none"/>
        <security:http pattern="/json/**" security="none"/>
        <security:http pattern="/jstree-v.pre1.0/**" security="none"/>
        <security:http pattern="/ol213/**" security="none"/>
        <security:http pattern="/sld/**" security="none"/>
        <!-- Configurazione sicurezza -->
        <security:http use-expressions="true" disable-url-rewriting="true" 
        entry-point-ref="webSealEntryPoint" request-matcher-ref="webSealReqMatcher">
            <security:custom-filter ref="webSealAuthFilter" position="FIRST"/>
            <security:intercept-url pattern="/comiajax/pages/*" access="hasAnyRole('OMNIA_ADMIN','DOMAIN_ADMIN','OMNIA_GUEST', 'DOMAIN_GUEST')"/>
            <security:session-management>
                <security:concurrency-control error-if-maximum-exceeded="true" max-sessions="1"/>
            </security:session-management>
        </security:http>
        <security:authentication-manager alias="authenticationManager">
            <security:authentication-provider ref="webSealAuthProvider" />
        </security:authentication-manager>
    <bean id="webSealAuthFilter" class="it.angelo.spring.web.security.auth.filter.WebSealAuthenticationFilter">
        <constructor-arg ref="webSealReqMatcher"/>
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="authenticationDetailsSource">
            <bean class="it.angelo.spring.web.security.auth.ServiceAuthenticationDetailsSource"/>
        </property>
    </bean>
    <bean id="webSealEntryPoint" class="it.angelo.spring.web.security.entrypoint.WebSealEntryPoint">
        <property name="usernameHeaderParam" value="iv-user" />
    </bean>
    <bean id="webSealAuthProvider" class="it.angelo.spring.web.security.auth.providers.WcmSealAuthProvider">
        <property name="authenticationUserDetailsService" ref="wcmSecurityUserSvc"/>
    </bean>
    <security:user-service id="userService">
        <security:user name="angelo" password="angelo" authorities="OMNIA_ADMIN,"/>
        <security:user name="gina" password="gina" authorities="DOMAIN_ADMIN"/>
        <security:user name="costa" password="costa" authorities="OMNIA_GUEST"/>
        <security:user name="marianna" password="marianna" authorities="DOMAIN_GUEST"/>
    </security:user-service>
    <bean id="webSealReqMatcher" class="it.angelo.spring.web.security.auth.requestmatchers.WebSealRequestMatcher"/>
    <bean id="wcmSecurityUserSvc" class="it.angelo.spring.web.security.userdetails.GrantedFromIvUsernameAttributesUserDetailsService"/>
</beans>

我的自定义过滤器

public class WebSealAuthenticationFilter extends AbstractAuthenticationProcessingFilter
{
    protected WebSealAuthenticationFilter(RequestMatcher requiresAuthenticationRequestMatcher)
    {
        super(requiresAuthenticationRequestMatcher);
    }

    private static final Log logger = LogFactory.getLog(WebSealAuthenticationFilter.class.getName());

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException
    {
        WebSealIvUserNameAuthenticationToken auth = new WebSealIvUserNameAuthenticationToken(request.getHeader(WebSealRequestMatcher.DEFAULT_HEADER_PARAM));
        auth.setDetails(this.authenticationDetailsSource.buildDetails(request));
        return this.getAuthenticationManager().authenticate(auth);
    }

    @Override
    protected final void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException
    {
        if (logger.isDebugEnabled()) {
            logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
        }

        SecurityContextHolder.getContext().setAuthentication(authResult);

        // Propaga l'evento
        if (this.eventPublisher != null) {
            eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
        }
        request.getSession().setAttribute("autenticato", new Boolean(true));
        chain.doFilter(request, response);
    }
}

我的请求匹配器(我知道我可以使用 org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher 类,以后我会使用它)

public class WebSealRequestMatcher implements RequestMatcher
{
    public static final String DEFAULT_HEADER_PARAM = "iv-user";
    @Override
    public boolean matches(HttpServletRequest request)
    {
        String headerParam = request.getHeader(DEFAULT_HEADER_PARAM);
        return !StringUtils.hasText(headerParam);
    }   
}

我的身份验证提供者

public class WcmSealAuthProvider implements AuthenticationProvider
{
    private static final Log logger = LogFactory.getLog(WcmSealAuthProvider.class.getName());
    private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
    private AuthenticationUserDetailsService<WebSealIvUserNameAuthenticationToken> authenticationUserDetailsService;
    public void setAuthenticationUserDetailsService(final AuthenticationUserDetailsService<WebSealIvUserNameAuthenticationToken> svc)
    {
        this.authenticationUserDetailsService = svc;
    }

    public AuthenticationUserDetailsService<WebSealIvUserNameAuthenticationToken> getAuthenticationUserDetailsService()
    {
        return authenticationUserDetailsService;
    }
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException
    {
        if( !supports(authentication.getClass()) )
        {
            if( logger.isInfoEnabled() )
            {
                logger.info("Classe "+authentication.getClass().getName()+" non supportata");
            }
            return null;
        }
        if( authentication instanceof WebSealIvUserNameAuthenticationToken )
        {
            return doAuthenticationNow(authentication);
        }
        return null;
    }

    @Override
    public boolean supports(Class<?> authentication)
    {
        return WebSealIvUserNameAuthenticationToken.class.isAssignableFrom(authentication);
    }
    private WebSealAuthenticationToken doAuthenticationNow(final Authentication authentication)
    {

        try {
            final String ivUserName = ((WebSealIvUserNameAuthenticationToken)authentication).getIvUserName();
            UserDetails userDetails = loadUserByIvUserName(ivUserName);

            return new WebSealAuthenticationToken(userDetails, userDetails.getAuthorities(), 
                    this.authoritiesMapper.mapAuthorities(userDetails.getAuthorities()), userDetails, userDetails.getUsername());
        } catch (final Exception e) {
            throw new BadCredentialsException(e.getMessage(), e);
        }
    }

    protected UserDetails loadUserByIvUserName( final String ivUserName )
    {
        return this.authenticationUserDetailsService.loadUserDetails(new WebSealIvUserNameAuthenticationToken(ivUserName));
    }
}

我的身份验证令牌

public class WebSealIvUserNameAuthenticationToken extends AbstractAuthenticationToken implements Serializable
{

    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
    private String ivUserName;
    public WebSealIvUserNameAuthenticationToken(String ivUserName)
    {
        super(new ArrayList<GrantedAuthority>());
        this.ivUserName = ivUserName;
    }
    @Override
    public Object getCredentials()
    {
        return null;
    }

    @Override
    public Object getPrincipal()
    {
        return null;
    }
    public String getIvUserName()
    {
        return ivUserName;
    }
}

public final class WebSealAuthenticationToken extends AbstractAuthenticationToken implements Serializable
{

    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
    private final Object credentials;
    private final Object principal;
    private final UserDetails userDetails;

    private String ivUserName;
    public WebSealAuthenticationToken(  final Object principal, 
                                        final Object credentials,
                                        final Collection<? extends GrantedAuthority> authorities, 
                                        final UserDetails userDetails,
                                        final String ivUserName)
    {
        super(authorities);
        if (((principal == null) || "".equals(principal) || (credentials == null)
                || "".equals(credentials) || (authorities == null) || (userDetails == null) 
                || !StringUtils.hasText(ivUserName))) {

                throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
            }
        this.principal = principal;
        this.credentials = credentials;
        this.userDetails = userDetails;
        this.ivUserName = ivUserName;
        setAuthenticated(true);
    }
    @Override
    public Object getCredentials()
    {
        return this.credentials;
    }

    @Override
    public Object getPrincipal()
    {
        return this.principal;
    }
    public UserDetails getUserDetails() 
    {
        return userDetails;
    }

    public String getIvUserName()
    {
        return ivUserName;
    }
    @Override
    public boolean equals(final Object obj) {
        if (!super.equals(obj)) {
            return false;
        }

        if (obj instanceof WebSealAuthenticationToken) {
            WebSealAuthenticationToken test = (WebSealAuthenticationToken) obj;

            if (!this.ivUserName.equals(test.getIvUserName())) {
                return false;
            }
            return true;
        }

        return false;
    }
}

My AuthenticationUserDetailsService

public abstract class AbstractUserDetailsService implements AuthenticationUserDetailsService<WebSealIvUserNameAuthenticationToken>
{

    @Override
    public UserDetails loadUserDetails(final WebSealIvUserNameAuthenticationToken token) throws UsernameNotFoundException
    {
        return loadUserDetails(token.getIvUserName());
    }
    protected abstract UserDetails loadUserDetails(String ivUserName);
}

public class GrantedFromIvUsernameAttributesUserDetailsService extends AbstractUserDetailsService
{
    private static final String NON_EXISTENT_PASSWORD_VALUE = "NO_PASSWORD";
    @Override
    protected UserDetails loadUserDetails(String ivUserName)
    {
        // TODO Inserire il codice per recuperare l'utente da PUMA
        final List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
        grantedAuthorities.add(new SimpleGrantedAuthority("OMNIA_ADMIN"));
        grantedAuthorities.add(new SimpleGrantedAuthority("DOMAIN_ADMIN"));
        grantedAuthorities.add(new SimpleGrantedAuthority("OMNIA_GUEST"));
        grantedAuthorities.add(new SimpleGrantedAuthority("DOMAIN_GUEST"));
        return new User("fromWebSeal", NON_EXISTENT_PASSWORD_VALUE, true, true, true, true, grantedAuthorities);
    }

}

我的 AuthenticationDetailsS​​ource

public class ServiceAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, ServiceAuthenticationDetails>
{

    @Override
    public ServiceAuthenticationDetails buildDetails(HttpServletRequest request)
    {
        return new DefaultServiceAuthenticationDetails(request);
    }

}

public class DefaultServiceAuthenticationDetails extends WebAuthenticationDetails implements ServiceAuthenticationDetails
{
    private static final long serialVersionUID = 6056034551086630421L;
    private String ivUserName;
    public DefaultServiceAuthenticationDetails(HttpServletRequest request)
    {
        super(request);
        String headerParam = request.getHeader(WebSealRequestMatcher.DEFAULT_HEADER_PARAM);
        this.ivUserName = StringUtils.hasText(headerParam) ? headerParam : "nonValorizzato"+System.nanoTime();
    }
    @Override
    public String getIvUserName()
    {
        return ivUserName;
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = super.hashCode();
        result = prime * result + ivUserName.hashCode();
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!super.equals(obj) || !(obj instanceof DefaultServiceAuthenticationDetails)) {
            return false;
        }
        ServiceAuthenticationDetails that = (ServiceAuthenticationDetails) obj;
        return ivUserName.equals(that.getIvUserName());
    }
}
public interface ServiceAuthenticationDetails extends Serializable
{

    public String getIvUserName();
}

在我看来都是正确的;无论如何,当我尝试访问页面 http://myHost:myPort/media/comiajax/pages/uploadmedia 时,我有一个循环并且页面无法加载..... 任何人都可以帮助我吗?

欢迎任何提示

谢谢

安杰洛

【问题讨论】:

    标签: spring spring-security


    【解决方案1】:

    我刚刚意识到我在代码中犯了一些错误 :) 并不是所有的工作都很好;首先我以这种方式修改了我的入口点:

    public class WebSealEntryPoint implements AuthenticationEntryPoint, InitializingBean
    {
        private String webSealUrl;
        @Override
        public void afterPropertiesSet() throws Exception
        {
            Assert.hasLength(this.webSealUrl, "Indicare l'URL del WebSeal");
        }
    
        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authExp) throws IOException, ServletException
        {
    
            String url = webSealUrl+"?service="+request.getRequestURL();
            response.sendRedirect(url);
        }
    
        public String getWebSealUrl()
        {
            return webSealUrl;
        }
    
        public void setWebSealUrl(String webSealUrl)
        {
            this.webSealUrl = webSealUrl;
        }
    }
    

    正如你所见,我添加了一个与 CAS 添加的重定向非常相似的重定向;然后我以这种方式修改了我的security-context.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:security="http://www.springframework.org/schema/security"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
    
            <security:http use-expressions="true" disable-url-rewriting="true" 
            entry-point-ref="webSealEntryPoint" request-matcher-ref="webSealReqMatcher">
                <security:intercept-url pattern="/css/**" access="permitAll"/>
                <security:intercept-url pattern="/DataTables-1.9.4/**" access="permitAll"/>
                <security:intercept-url pattern="/images/**" access="permitAll"/>
                <security:intercept-url pattern="/jquery-ui-1.10.3/**" access="permitAll"/>
                <security:intercept-url pattern="/json/**" access="permitAll"/>
                <security:intercept-url pattern="/jstree-v.pre1.0/**" access="permitAll"/>
                <security:intercept-url pattern="/ol213/**" access="permitAll"/>
                <security:intercept-url pattern="/sld/**" access="permitAll"/>
                <security:intercept-url pattern="/comiajax/pages/*" access="hasAnyRole('OMNIA_ADMIN','DOMAIN_ADMIN','OMNIA_GUEST', 'DOMAIN_GUEST')"/>
                <security:session-management>
                    <security:concurrency-control error-if-maximum-exceeded="true" max-sessions="1"/>
                </security:session-management>
                <security:custom-filter ref="webSealAuthFilter" position="LAST"/>
            </security:http>
            <security:authentication-manager alias="authenticationManager">
                <security:authentication-provider ref="webSealAuthProvider" />
            </security:authentication-manager>
        <bean id="webSealAuthFilter" class="it.angelo.spring.web.security.auth.filter.WebSealAuthenticationFilter">
            <constructor-arg ref="webSealReqMatcher"/>
            <property name="authenticationManager" ref="authenticationManager"/>
            <property name="authenticationDetailsSource">
                <bean class="it.angelo.spring.web.security.auth.ServiceAuthenticationDetailsSource"/>
            </property>
        </bean>
        <bean id="webSealEntryPoint" class="it.angelo.spring.web.security.entrypoint.WebSealEntryPoint">
            <property name="webSealUrl" value="http://localhost:8380/WebSeal" />
        </bean>
        <bean id="webSealAuthProvider" class="it.angelo.spring.web.security.auth.providers.WcmSealAuthProvider">
            <property name="authenticationUserDetailsService" ref="wcmSecurityUserSvc"/>
        </bean>
        <security:user-service id="userService">
            <security:user name="angelo" password="angelo" authorities="OMNIA_ADMIN,"/>
            <security:user name="gina" password="gina" authorities="DOMAIN_ADMIN"/>
            <security:user name="costa" password="costa" authorities="OMNIA_GUEST"/>
            <security:user name="marianna" password="marianna" authorities="DOMAIN_GUEST"/>
        </security:user-service>
        <bean id="webSealReqMatcher" class="it.angelo.spring.web.security.auth.requestmatchers.WebSealRequestMatcher"/>
        <bean id="wcmSecurityUserSvc" class="it.angelo.spring.web.security.userdetails.GrantedFromIvUsernameAttributesUserDetailsService"/>
    </beans>
    

    谢谢

    安杰洛

    【讨论】:

      猜你喜欢
      • 2011-08-23
      • 2013-07-22
      • 2014-10-24
      • 1970-01-01
      • 2011-07-23
      • 2018-05-25
      • 2017-02-02
      • 2016-03-08
      • 1970-01-01
      相关资源
      最近更新 更多