0

I want to check captcha if use wants to be logined and if captcha was correct, call filterChain.doFilter() to resume authentication and if captcha was incorrect rediredt user to login page to re-enter username, password and captcha. So, i want to put my CaptchaFilter with /login filterMapping in the first of spring securtiy fiter chain.

login.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html; charset=UTF-8" language="java" pageEncoding="UTF-8" session="true" %>
<html>
<head>
    <title>Login Page</title>
</head>
<body onload='document.loginForm.username.focus();'>
<div id="login-box">

    <form name='loginForm' action="<c:url value='/login' />" method='POST'>
        <table>
            <tr>
                <td>User:</td>
                <td><input type='text' name='username'></td>
            </tr>
            <tr>
                <td>Password:</td>
                <td><input type='password' name='password'/>
                </td>
            </tr>
            <tr>
                <td colspan="2">
                    <img id="imgCaptcha" src="<c:url value = '/j-captcha.jpg' />" onclick='this.src="<c:url value='/j-captcha.jpg'/>";' style="cursor: pointer"/>
                </td>
            </tr>
            <tr>
                <td colspan="2">
                    <input name="jcaptcha" type="text" placeholder="captcha"/>
                </td>
            <tr>
                <td colspan='2'><input name="submit" type="submit" value="submit"/></td>
            </tr>
        </table>
    </form>
</div>
</body>
</html>

CaptchaFilter

public class CaptchaFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        if (request.getParameter("jcaptcha") != null) {
            checkCaptcha(request, response, filterChain);
        } else {
            filterChain.doFilter(request, response);
        }
    }

    private void checkCaptcha(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        try {
            String userCaptchaResponse = request.getParameter("jcaptcha");
            boolean isResponseCorrect = CaptchaService.getInstance().validateResponseForID(request.getRequestedSessionId(), userCaptchaResponse);
            if (isResponseCorrect) {
                filterChain.doFilter(request, response);
            } else {
                String url = request.getHeader("referer").replaceAll("[&?]error.*?(?=&|\\?|$)", "");
                url += "?error=" + SecurityUtility.CAPTCHA_IS_WRONG;

                redirect(request, response, url);
            }
        } catch (Exception e) {
            e.printStackTrace();
            filterChain.doFilter(request, response);
        }
    }

    private void redirect(HttpServletRequest request, HttpServletResponse response, String url) {
        try {
            response.sendRedirect(request.getContextPath() + url);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    @Override
    public void destroy() {

    }
}

SpringSecurityConfig:

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    @Qualifier("userDetailsService")
    UserDetailsService userDetailsService;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests().antMatchers("/admin/**")
                .access("hasRole('ROLE_USER')").and().formLogin()
                .loginPage("/login").failureUrl("/login?error")
                .usernameParameter("username")
                .passwordParameter("password")
                .and().logout().logoutSuccessUrl("/login?logout")
                .and().exceptionHandling().accessDeniedPage("/403");
    }
}

SpringWebConfig

@EnableWebMvc
@Configuration
@ComponentScan({"com.rgh.*"})
@EnableTransactionManagement
@Import({SpringSecurityConfig.class})
public class SpringWebConfig {
    @Bean
    public SessionFactory sessionFactory() {
        LocalSessionFactoryBuilder builder = new LocalSessionFactoryBuilder(dataSource());
        builder.scanPackages("com.rgh.*.model").addProperties(getHibernateProperties());
        return builder.buildSessionFactory();
    }

    private Properties getHibernateProperties() {
        // set and return properties
    }

    @Bean(name = "dataSource")
    public BasicDataSource dataSource() {
        // set and return datasource
    }

    @Bean
    public HibernateTransactionManager txManager() {
        return new HibernateTransactionManager(sessionFactory());
    }
}

SpringWebInitializer

public class SpringWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SpringWebConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/", "/rest/*"};
    }
}

SpringSecurityInitializer

public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer {

}

i'm new to spring 4 and spring java config.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Rasool Ghafari
  • 4,128
  • 7
  • 44
  • 71

1 Answers1

2

You can add your filter like this :

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
...
http.addFilterAfter(yourFilter, CsrfFilter.class);
...
}

There are other methods addFilterBefore(..) and addFilter(..) to add filters. addFilterBefore and addFilterAfter expect as second argument a filter class that is part of the SecurityFilterChain, and they will be added relative to it. addFilter requires some comperator which I never tried.

To find the filter classes that are actually in the SecurityFilterChain set a breakpoint in a controller method and search in the stack for the SecurityFilterChain (or DefaultSecurityFilterChain). There you can see which filter classes are configured e.g. in DefaultSecurityFilterChain.fiters Find the first filter class than use addFilterBefore to add your CaptchaFilter.