在UsernamePasswordAuthenticationFilter

时间:2017-01-18 16:47:52

标签: java spring-mvc authentication spring-security

嗨,所有春季专家!

我有一个问题,我试图解决一段时间,但我认为我已经走到了尽头。

基本上我需要的是配置我的Spring-Security(在Spring-Boot中)有两种身份验证机制(一种用于Legacy JSP页面,一种用于REST API)。所以我按照以下帖子: multiple authentication mechanisms in a single app using java config

一个LDAP身份验证提供程序运行良好。但后来我尝试扩展我的LDAP连接,以便从第三方服务获取票证(将用于将来与其他服务的连接),并且我遇到了问题。

所以我创建了一个新的身份验证令牌,过滤器和身份验证提供程序,但无论我做什么,都会首先触发默认的UsernamePasswordAuthenticationFilter

我尝试关注此帖How to configure a custom filter programatically in Spring Security?,发现问题可能在于我的过滤器正在扩展UsernamePasswordAuthenticationFilter。所以我删除了这个并尝试了一个简单的AbstractAuthenticationProcessingFilter,仍然 - 没有运气。

我认为问题出在我的WebSecurity配置中。目前,通过以下代码我将要分享,REST Api身份验证返回 405 - 方法不允许并且旧版登录陷入无限循环并崩溃,甚至在我点击“登录”之前。

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) //Enables @PreAuthorize on methods
public class WebSecurityConfig  extends WebSecurityConfigurerAdapter {

    @Autowired
    private LDAPConfigurationBean ldapBean;

    @Autowired
    protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//HERE GOES LDAP CONNECTION STUFF               
//      Add the custom LDAP + Token provider to the Authentication provider chain
        auth.authenticationProvider(new TicketAndLDAPAuthenticationProvider(authenticator,authoritiesPopulator));

//        Creating an LDAP provider using the authenticator and the populator.
        auth.authenticationProvider(new LdapAuthenticationProvider(authenticator,authoritiesPopulator));

    }


    @Configuration
    @Order(1)
    public static class ConfigureFilters extends WebSecurityConfigurerAdapter {
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable();
            http.addFilterBefore(new TicketAndLDAPAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class);
        }
    }

    //Management Endpoints Authorization
    @Configuration
    @Order(2)
    public static class EndpointsWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .antMatcher("/manage/health")
                    .authorizeRequests()
                    .anyRequest().permitAll();
        }
    }

    //API Authentication+Authorization
    @Configuration
    @Order(3)
    public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {

        @Autowired
        private RestAuthenticationEntryPoint authenticationEntryPoint;
        @Autowired
        private RestAuthSuccessHandler authSuccessHandler;
        @Autowired
        private RestAuthFailureHandler authFailureHandler;
        @Autowired
        private RestLogoutSuccessHandler logoutSuccessHandler;

        private String LOGIN_PATH = "/api/authenticate";
        private String USERNAME = "username";
        private String PASSWORD = "password";

        protected void configure(HttpSecurity http) throws Exception {
            /*CSRF configuration*/
            http.csrf().disable();

            http
                    .antMatcher(LOGIN_PATH)
                    .authorizeRequests()
                    .anyRequest().permitAll();

            http
                    .antMatcher("/api/**")
                    //Stateless session creation - no session will be created or used by Spring Security
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    .exceptionHandling()
                        .authenticationEntryPoint(authenticationEntryPoint)
                    .and()
                    .formLogin().permitAll()
                        .loginProcessingUrl(LOGIN_PATH)
                        .usernameParameter(USERNAME)
                        .passwordParameter(PASSWORD)
                        .successHandler(authSuccessHandler)
                        .failureHandler(authFailureHandler)
                    .and()
                    .logout().permitAll()
                        .logoutSuccessHandler(logoutSuccessHandler);

            http
                    .authorizeRequests().anyRequest().authenticated();
        }
    }

    //JSP Authentication+Authorization
    @Configuration
    @Order(4)
    public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            /*CSRF configuration*/
            http.csrf().disable();

            /*Static content*/
            http
                    .authorizeRequests()
                    .antMatchers("/css*//**").permitAll()
                    .antMatchers("/images*//**").permitAll()
                    .antMatchers("/scripts*//**").permitAll()
                    .antMatchers("/fonts*//**").permitAll()
                    .antMatchers("/login*").anonymous();

        /*Login / Logout configuration*/
            http
                    .formLogin()
                        .loginPage("/login.htm").permitAll()
                        .defaultSuccessUrl("/index.htm?name=******")
                        .failureUrl("/login.htm?error=true")
                    .and()
                    .logout().permitAll()
                        .logoutSuccessUrl("/login.htm")
                        .invalidateHttpSession(true)
                        .deleteCookies("JSESSIONID");

        /*URL roles authorizations*/
            http
                    .authorizeRequests().anyRequest().authenticated();
        }
    }
}

正如您所看到的,我正在尝试在“配置过滤器”方法中配置我的过滤器 - 但我也尝试在适配器内配置它,带/不带@Bean注释 - 所有都没有运气

过滤器:

public class TicketAndLDAPAuthenticationFilter  extends AbstractAuthenticationProcessingFilter {
    public TicketAndLDAPAuthenticationFilter() {
        super("/*");
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        //Save the password for later
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        TicketAndLDAPAuthenticationToken token = new TicketAndLDAPAuthenticationToken(username,password,null);

        return token;
    }
}

修改:忘记添加到过滤器:

if ( request.getParameter( "username" ) == null || request.getParameter( "password" ) == null ) == null ) {
            return null;
        }

现在我在两种登录机制中都获得了405.

令牌:

public class TicketAndLDAPAuthenticationToken  extends UsernamePasswordAuthenticationToken {
    private AuthTicket otp;
    private String restoredPassword;


    public TicketAndLDAPAuthenticationToken( String username, String password, RestAuthLoginTicket otp ) {
        super( username, password );
        this.otp = otp;
    }

    public AuthTicket getOTP() {
        return otp;
    }

    public AuthTicket getOtp() {
        return otp;
    }

    public void setOtp(AuthTicket otp) {
        this.otp = otp;
    }
}

提供者:

public class TicketAndLDAPAuthenticationProvider extends LdapAuthenticationProvider {

    @Autowired
    TokenUtils tokenUtils;

    public TicketAndLDAPAuthenticationProvider(LdapAuthenticator authenticator, LdapAuthoritiesPopulator authoritiesPopulator) {
        super(authenticator, authoritiesPopulator);
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
              TicketAndLDAPAuthenticationToken token =  (TicketAndLDAPAuthenticationToken) super.authenticate(authentication);
        token.setOtp(tokenUtils.getTicket(token));
        return token;
    }


    @Override
    public boolean supports(Class<?> authentication) {
        return TicketAndLDAPAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

提前致谢!!

1 个答案:

答案 0 :(得分:0)

所以我找到了问题。

首先,配置身份验证管理器的正确方法不是我上面配置的方式,因为没有antMatcher这导致我的资源和页面对所有人开放。

其次,导致无限重定向和错误405的问题是我没有定义我的过滤器来接受帖子。

修复之后,我的JSP登录表单和身份验证机制运行正常,但是&#34; / api&#34;被重定向到登录页面而不是资源。

是什么让我想到了最后一点 - http.formLogin()正在创建一个UsernamePasswordAuthenticationFilter。我有两个 - 每次登录一个。所以我必须为每个登录添加http.addFilterBefore(),但使用不同的URL。 &#34; / api&#34; url再次使用Spring的默认重定向而不是我定义的重定向,所以我不得不重写它们。

这些是适用于我的配置和过滤器:

安全配置:

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) //Enables @PreAuthorize on methods
public class WebSecurityConfig  extends WebSecurityConfigurerAdapter {

    @Autowired
    private LDAPConfigurationBean ldapBean;

    @Autowired
    protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        //LDAP Stuff

        TicketAndLDAPAuthenticationProvider ticketAndLDAPAuthenticationProvider = new TicketAndLDAPAuthenticationProvider(authenticator,authoritiesPopulator);
        auth.authenticationProvider(ticketAndLDAPAuthenticationProvider);

        LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(authenticator,authoritiesPopulator);
        auth.authenticationProvider(ldapAuthenticationProvider);    
    }

    //Management Endpoints Authorization
    @Configuration
    @Order(1)
    public static class EndpointsWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .antMatcher("/manage/health")
                    .authorizeRequests()
                    .anyRequest().permitAll();
        }
    }

    //API Authentication+Authorization
    @Configuration
    @Order(2)
    public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {

        @Autowired
        private RestAuthenticationEntryPoint authenticationEntryPoint;
        @Autowired
        private RestAuthSuccessHandler authSuccessHandler;
        @Autowired
        private RestAuthFailureHandler authFailureHandler;
        @Autowired
        private RestLogoutSuccessHandler logoutSuccessHandler;

        private String LOGIN_PATH = "/api/authenticate";

        protected void configure(HttpSecurity http) throws Exception {
            /*CSRF configuration*/
            http.csrf().disable();

            http.addFilterBefore(new TicketAndLDAPAuthenticationFilter(LOGIN_PATH,authSuccessHandler,authFailureHandler), UsernamePasswordAuthenticationFilter.class);

            http
                    .antMatcher("/api/**")
//                    Stateless session creation - no session will be created or used by Spring Security
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    .exceptionHandling()
                        .authenticationEntryPoint(authenticationEntryPoint)
                    .and()
                    .logout().permitAll()
                        .logoutSuccessHandler(logoutSuccessHandler);

            http
                    .authorizeRequests().anyRequest().authenticated();

        }
    }

    //JSP Authentication+Authorization
    @Configuration
    @Order(3)
    public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

        private String LOGIN_PATH = "/login.htm";

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            /*CSRF configuration*/
            http.csrf().disable();

            http.addFilterBefore(new TicketAndLDAPAuthenticationFilter(LOGIN_PATH), UsernamePasswordAuthenticationFilter.class);

            /*Static content*/
            http
                    .authorizeRequests()
                    .antMatchers("/css*//**").permitAll()
                    .antMatchers("/images*//**").permitAll()
                    .antMatchers("/scripts*//**").permitAll()
                    .antMatchers("/fonts*//**").permitAll()
                    .antMatchers("/login*").anonymous();

        /*Login / Logout configuration*/
            http
                    .formLogin()
                        .loginPage(LOGIN_PATH).permitAll()
                        .defaultSuccessUrl("/index.htm?name=******")
                        .failureUrl("/login.htm?error=true")
                    .and()
                    .logout().permitAll()
                        .logoutSuccessUrl("/login.htm")
                        .invalidateHttpSession(true)
                        .deleteCookies("JSESSIONID");

        /*URL roles authorizations*/
            http
                    .authorizeRequests().anyRequest().authenticated();
        }
    }
}

过滤器:

public class TicketAndLDAPAuthenticationFilter  extends AbstractAuthenticationProcessingFilter {

    public TicketAndLDAPAuthenticationFilter(String defaultProcessUrl) {
        super(new AntPathRequestMatcher(defaultProcessUrl, "POST"));
    }

    public TicketAndLDAPAuthenticationFilter(String defaultProcessUrl, AuthenticationSuccessHandler authenticationSuccessHandler, AuthenticationFailureHandler authenticationFailureHandler) {
        super(new AntPathRequestMatcher(defaultProcessUrl, "POST"));
        setAuthenticationFailureHandler(authenticationFailureHandler);
        setAuthenticationSuccessHandler(authenticationSuccessHandler);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        //Save the password for later
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        if ( username==null || password==null) {
            return null;
        }

        TicketAndLDAPAuthenticationToken token = new TicketAndLDAPAuthenticationToken(username,password,null);

        return token;
    }
}