1

I'm trying to convert my rest services authentication from basic authentication to form based authentication the below code works fine(Note I've commented out custom authentication filter) if I send authentication details in url as query parameter something like this http://localhost:8080/login?username=dfdf&&password=sdsdd

However I'm not keen on sending credentials as query parameter instead I would prefer to send it as json, Hence I've created custom authentication filter. When I add the custom authentication filter, my spring session stops working. I cannot find x-auth-token field in my response header. Any suggestion how to enable spring session and custom authentication together/or may be easier way to handle json input for credentials.

@Configuration
@EnableWebSecurity
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private ObjectMapper objectMapper;

    @Bean
    public PasswordEncoder passwordEncoder() {
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder builder) throws Exception {
        builder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/", "/register", "/index.html").permitAll().and().authorizeRequests()
                .anyRequest().authenticated().and().requestCache().requestCache(new NullRequestCache()).and()
                .formLogin().failureHandler(getRESTAuthenticationFailureHandler())
                .successHandler(getRESTAuthenticationSuccessHandler()).usernameParameter("username")
                .passwordParameter("password").and().exceptionHandling()
                .authenticationEntryPoint(getRESTAuthenticationEntryPoint()).and()
                //.addFilter(getAuthenticationFilter())
                .csrf().disable();
    }

    @Bean
    public HttpSessionStrategy httpSessionStrategy() {
        return new HeaderHttpSessionStrategy();
    }

    @Bean
    public RESTAuthenticationEntryPoint getRESTAuthenticationEntryPoint() {
        return new RESTAuthenticationEntryPoint();
    }

    @Bean
    public RESTAuthenticationSuccessHandler getRESTAuthenticationSuccessHandler() {
        return new RESTAuthenticationSuccessHandler();
    }

    @Bean
    public RESTAuthenticationFailureHandler getRESTAuthenticationFailureHandler() {
        return new RESTAuthenticationFailureHandler();
    }

    @Bean
    public AuthenticationFilter getAuthenticationFilter() {
        AuthenticationFilter filter = new AuthenticationFilter();
        try {
            filter.setAuthenticationManager(this.authenticationManager());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return filter;
    }

    public class RESTAuthenticationEntryPoint implements AuthenticationEntryPoint {

        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response,
                AuthenticationException authException) throws IOException, ServletException {

            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
        }
    }

    public class RESTAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                Authentication authentication) throws IOException, ServletException {
            clearAuthenticationAttributes(request);

        }
    }

    public class RESTAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                AuthenticationException exception) throws IOException, ServletException {

            super.onAuthenticationFailure(request, response, exception);
        }
    }

    public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {

        private final Logger LOG = LoggerFactory.getLogger(AuthenticationFilter.class);

        private boolean postOnly = true;

        public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
                throws AuthenticationException {
            if (postOnly && !request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
            }

            String username = null;
            String password = null;
            UserDetails userDetails = null;
            if ("application/json".equals(request.getHeader("Content-Type"))) {
                userDetails = getJson(request);
                if (userDetails != null) {
                    username = userDetails.getUsername();
                }
            } else {
                username = obtainUsername(request);
            }

            if ("application/json".equals(request.getHeader("Content-Type"))) {

                if (userDetails != null) {
                    password = userDetails.getPassword();
                }

            } else {
                password = obtainPassword(request);
            }

            if (username == null) {
                username = "";
            }

            if (password == null) {
                password = "";
            }

            username = username.trim();

            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username,
                    password);

            // Allow subclasses to set the "details" property
            setDetails(request, authRequest);

            return this.getAuthenticationManager().authenticate(authRequest);
        }

        private UserDetails getJson(HttpServletRequest request) {

            try {
                final List<String> data = IOUtils.readLines(request.getReader());
                final String jsonData = data.stream().collect(Collectors.joining());
                LOG.info(jsonData);
                UserDetails userDetails = objectMapper.readValue(jsonData, UserDetails.class);
                return userDetails;
            } catch (IOException e) {
                LOG.error("Failed to read data {}", e.getMessage(), e);
                return null;
            }

        }

    }

}
Karthik Prasad
  • 9,662
  • 10
  • 64
  • 112
  • Don't just add a filter add it before the login form filter (use another addFilter method for that). – M. Deinum Oct 20 '15 at 10:36
  • Do you mean to say add http.addFilter(getAuthenticationFilter()); before http.authorizeRequests() – Karthik Prasad Oct 20 '15 at 10:45
  • 1
    No as that doesn't change anything, you need to specify WHERE in the filter chain you want your filter to be added, currently it is added at the end after all other Spring Security filters already executed. You want to execute your filter before (or after) the form login not at the end of the chain. I strongly suggest a read of th javadoc of the `addFilter` method. – M. Deinum Oct 20 '15 at 10:46
  • This may help, http://stackoverflow.com/questions/17268855/spring-security-authentication-entry-point – Maleen Abewardana Oct 20 '15 at 17:02

1 Answers1

1

As suggested I've created custom filter which converts json object to request parameter and adds to HttpServletRequest, However are there any inbuilt spring security custom filters to do the same job?

@Configuration
@EnableWebSecurity
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private ObjectMapper objectMapper;

    @Bean
    public FilterRegistrationBean contextFilterRegistrationBean() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        CstFilter contextFilter = new CstFilter();
        registrationBean.addUrlPatterns("/login");
        registrationBean.setFilter(contextFilter);
        registrationBean.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
        return registrationBean;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder builder) throws Exception {

        builder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

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

        http
            .csrf()
                .disable()
            .exceptionHandling()
                .authenticationEntryPoint(getRESTAuthenticationEntryPoint())
            .and()
                .formLogin()
                    .permitAll()
                    .loginProcessingUrl("/login")
                    .failureHandler(getRESTAuthenticationFailureHandler())
                    .successHandler(getRESTAuthenticationSuccessHandler())
                    .usernameParameter("username")
                    .passwordParameter("password")
            .and()
                .logout()
                    .permitAll()
                    .logoutSuccessHandler(getRESTLogoutSuccessHandler())
            .and()
                .authorizeRequests()
                    .antMatchers("/", "/index.html")
                    .permitAll()
            .and()
                .authorizeRequests()
                        .anyRequest()
                            .authenticated()
            .and()
                .requestCache()
                    .requestCache(new NullRequestCache());
    }

    @Bean
    public HttpSessionStrategy httpSessionStrategy() {
        return new HeaderHttpSessionStrategy();
    }

    @Bean
    public RESTAuthenticationEntryPoint getRESTAuthenticationEntryPoint() {
        return new RESTAuthenticationEntryPoint();
    }

    @Bean
    public RESTAuthenticationSuccessHandler getRESTAuthenticationSuccessHandler() {
        return new RESTAuthenticationSuccessHandler();
    }

    @Bean
    public RESTAuthenticationFailureHandler getRESTAuthenticationFailureHandler() {
        return new RESTAuthenticationFailureHandler();
    }

    @Bean
    public RESTLogoutSuccessHandler getRESTLogoutSuccessHandler() {
        return new RESTLogoutSuccessHandler();
    }

    public class JsonConvertFilter extends HttpServletRequestWrapper {

        private final Logger LOG = LoggerFactory.getLogger(JsonConvertFilter.class);

        private UserDetails userDetails;

        public JsonConvertFilter(HttpServletRequest request) {
            super((HttpServletRequest)request);
            userDetails = getJson();
        }

        public String getParameter(String key){


            if(userDetails!=null){
                if("username".equals(key)){
                    return  userDetails.getUsername();
                }
                if("password".equals(key)){
                    return userDetails.getPassword();
                }
            }
            System.out.println("Called wrapper");
            return super.getParameter(key);
        }

        private UserDetails getJson() {

            try {
                final List<String> data = IOUtils.readLines(super.getReader());
                final String jsonData = data.stream().collect(Collectors.joining());
                LOG.info(jsonData);
                UserDetails userDetails = objectMapper.readValue(jsonData, UserDetails.class);
                return userDetails;
            } catch (IOException e) {
                LOG.warn("Failed to read data {}", e.getMessage(), e);
                return null;
            }

        }

    }

    public class CstFilter implements Filter{


        @Override
        public void destroy() {

        }

        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {

            chain.doFilter(new JsonConvertFilter((HttpServletRequest)request), response);
        }

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

        }

    }

    public class RESTLogoutSuccessHandler implements LogoutSuccessHandler{

        @Override
        public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
                Authentication authentication) throws IOException, ServletException {

            String uri = request.getRequestURI();
            if ("logout".equals(uri)) {
                response.sendError(HttpServletResponse.SC_OK, "Succesfully Logged out");
            }

        }

    }

    public class RESTAuthenticationEntryPoint implements AuthenticationEntryPoint {

        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response,
                AuthenticationException authException) throws IOException, ServletException {

            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().print("Unauthorizated....");

        }
    }

    public class RESTAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                Authentication authentication) throws IOException, ServletException {
            clearAuthenticationAttributes(request);

        }
    }

    public class RESTAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                AuthenticationException exception) throws IOException, ServletException {
            String uri = request.getRequestURI();
            if ("logout".equals(uri)) {
                response.sendError(HttpServletResponse.SC_OK, "Succesfully Logged out");
            } else {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
                        "Authentication Failed: " + exception.getMessage());
            }
        }
    }

}

For any one want to test this using jquery ajax.

/* Alerts the results */
        $.ajax({
            url : "login",
            type : "POST",
            async : false,
            contentType: "application/json",
            data : "{ \"username\":\""+username+"\", \"password\":\"" + password + "\"}",
            success : function(data, status, request) {
                alert("Success");
                authtokenKey = request.getResponseHeader('x-auth-token');
            },
            error : function(xhr, status, error) {
                alert(error);
            }
        });
Karthik Prasad
  • 9,662
  • 10
  • 64
  • 112