【问题标题】:Spring Security authentication filter not being called未调用 Spring Security 身份验证过滤器
【发布时间】:2019-04-29 09:21:42
【问题描述】:

我正在尝试将自定义过滤器添加到 Spring HttpSecurity。此过滤器必须检查用户名是否在外部提供的列表中,并以Set 的形式注入过滤器。

无论我将过滤器放在哪里,它的方法attemptAuthentication 都不会被调用。这是过滤器代码:

import java.io.IOException;
import java.util.Base64;
import java.util.Set;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

public class MyRoleFilter extends AbstractAuthenticationProcessingFilter {

    final Set<String> authorisedUsers;

    public WhoRoleFilter(String url, AuthenticationManager authenticationManager, Set<String> authorisedUsers) {
        super(new AntPathRequestMatcher(url));
        this.authorisedUsers= authorisedUsers;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
        throws AuthenticationException, IOException, ServletException {
        // In BASIC authentication user:password come as Base64 in the Authorization header
        final String authorization = request.getHeader("Authorization");
        final String[] userPasswd = new String(Base64.getDecoder().decode(authorization)).split(":");
        // The docs of AbstractAuthenticationProcessingFilter says it must throw an exception in case authentication fails
        // https://docs.spring.io/spring-security/site/docs/4.2.6.RELEASE/apidocs/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.html#attemptAuthentication-javax.servlet.http.HttpServletRequest-javax.servlet.http.HttpServletResponse-
        if (userPasswd.length!=2)
                throw new BadCredentialsException("Bad Credentials");
        if (authorisedUsers.contains(userPasswd[0])) {
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(userPasswd[0], userPasswd[1]);
            return this.getAuthenticationManager().authenticate(authRequest);
        } else {
            throw new BadCredentialsException("User has not the correct role");
        }
    }

}

这就是我尝试将其添加到 HttpSecurity 的方式:

@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) {
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/disabled")
            .permitAll()
            .anyRequest()
            .authenticated()
            .and()
            .addFilterBefore(new MyRoleFilter("**/path/services/whatever/**", this.authenticationManager() ,myUserNamesSet), BasicAuthenticationFilter.class)
            .httpBasic();
    }

}

我不确定在构建链中addFilterBefore() 必须去哪里。此外,除了用户名列表过滤器之外,还需要 LDAP 服务器的标准用户+密码。 LDAP 身份验证已经到位并处于正常工作状态。

更新,这是configureGlobal(AuthenticationManagerBuilder)SecurityConfig

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
    try {
        auth.ldapAuthentication()
            .userDnPatterns(ConfigurationProvider.get().getProperty(Property.PROP_LDAP_USER_BASE_DN))
            .contextSource(contextSource())
            .passwordCompare()
            .passwordAttribute(ConfigurationProvider.get().getProperty(Property.PROP_LDAP_PASSWORD_ATTRIBUTE));
    } catch (Exception exc) {
        LOG.error(exc.getMessage(), exc);
    }
}

【问题讨论】:

    标签: spring-security


    【解决方案1】:

    经过研究回答我自己的问题。这就是我必须实现 SecurityConfig 和我的身份验证处理过滤器的方式,以便使用 LDAP 在基本身份验证上添加自定义角色检查以存储用户凭据。

    @EnableWebSecurity
    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        private String Set<String> myUsers;
    
        @Autowired
        public void configureGlobal(AuthenticationManagerBuilder auth) {
            myUsers = new HashSet<>(Arrays.asList("John","James","Jeremy"));
            try {
                auth.ldapAuthentication()
                        .userDnPatterns("ldap.user.base.dn")
                        .contextSource(contextSource())
                        .passwordCompare()
                        .passwordAttribute("ldap.password.attribute");
                auth.authenticationEventPublisher(defaultAuthenticationEventPublisher());
            } catch (Exception exc) {
                // LOG.error(exc.getMessage(), exc);
            }
        }
    
        @Override
        @Autowired
        public void setAuthenticationConfiguration(AuthenticationConfiguration authenticationConfiguration) {
            super.setAuthenticationConfiguration(authenticationConfiguration);
        }
    
        @Override
        @Autowired
        public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
            super.setObjectPostProcessor(objectPostProcessor);
        }
    
        @Override
        protected void configure(HttpSecurity http) {
            try {
                http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/disabled")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .addFilterAfter(new MyFilter("/services/my/path",  this.authenticationManager(), myUsers), BasicAuthenticationFilter.class)
                .httpBasic();
    
                http.logout().deleteCookies("JSESSIONID")
                .clearAuthentication(true)
                .invalidateHttpSession(true);
            } catch (Exception exc) {
                // LOG.error(exc.getMessage(), exc);
            }
        }
    }
    

    请注意,我必须从 AbstractAuthenticationProcessingFilter 覆盖 successfulAuthentication,因为它总是尝试执行重定向以查找包含目标 URL 的请求标头。

    public class MyFilter extends AbstractAuthenticationProcessingFilter {
    
        final Set<String> authorisedUsers;
    
        public MyFilter(String url, AuthenticationManager authenticationManager, Set<String> authorisedUsers) {
            super(new AntPathRequestMatcher(url));
            this.authorisedUsers = authorisedUsers;
            this.setAuthenticationManager(authenticationManager);
        }
    
        @Override
        public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException, IOException, ServletException {
            // In BASIC authentication user:password come as Base64 in the Authorization header
            final String authorization = request.getHeader("Authorization");
            String authorizationBasic = authorization; 
            if (authorization.startsWith("Basic")) {
                authorizationBasic = authorization.split(" ")[1];
            }
            final String[] userPasswd = new String(Base64.getDecoder().decode(authorizationBasic)).split(":");
            // The docs of AbstractAuthenticationProcessingFilter says it must throw an exception in case authentication fails
            // https://docs.spring.io/spring-security/site/docs/4.2.6.RELEASE/apidocs/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.html#attemptAuthentication-javax.servlet.http.HttpServletRequest-javax.servlet.http.HttpServletResponse-
            if (userPasswd==null || userPasswd.length!=2)
                throw new BadCredentialsException("Bad Credentials");
            if (authorisedUsers.contains(userPasswd[0])) {
                UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(userPasswd[0], userPasswd[1]);
                return getAuthenticationManager().authenticate(authRequest);
            } else {
                throw new BadCredentialsException("User " + userPasswd[0] + " has not the WHO role");
            }
        }
    
        @Override
        protected void successfulAuthentication(HttpServletRequest request,
                HttpServletResponse response, FilterChain chain, Authentication authResult)
                throws IOException, ServletException {
    
            SecurityContextHolder.getContext().setAuthentication(authResult);
    
            chain.doFilter(request, response);
        }
    }
    

    【讨论】:

      【解决方案2】:

      configure(HttpSecurity http) 下添加and().addFilter(new AuthenticationFilter(authenticationManager())),如下所示。它应该显式调用身份验证过滤器。

      @Override
          protected void configure(HttpSecurity http) throws Exception {
              http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
              http.csrf().disable().authorizeRequests().antMatchers(HttpMethod.POST, SecurityConstants.SIGN_UP_URL).permitAll().anyRequest()
                      .authenticated().and().addFilter(new AuthenticationFilter(authenticationManager())).sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
          }
      

      【讨论】:

        猜你喜欢
        • 2022-01-20
        • 2021-06-22
        • 1970-01-01
        • 2014-09-04
        • 2021-01-20
        • 1970-01-01
        • 2018-12-11
        • 2013-01-15
        • 2014-07-29
        相关资源
        最近更新 更多