0

I was trying to convert the following spring security java configuration to xml configuration;

Java config;

package com.careapple.webservice.security.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.careapple.webservice.security.filter.AuthenticationTokenFilter;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableTransactionManagement
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private AuthenticationEntryPoint authenticationEntryPoint;


    @Autowired
    public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder
                .userDetailsService(userDetailsService)
                .passwordEncoder(new BCryptPasswordEncoder());
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }


    @Bean
    public AuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
        AuthenticationTokenFilter authenticationTokenFilter = new AuthenticationTokenFilter();
        authenticationTokenFilter.setAuthenticationManager(super.authenticationManagerBean());
        return authenticationTokenFilter;
    }


    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                .csrf()
                .disable()
                .exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .antMatchers("/auth/**").permitAll()
                .anyRequest().authenticated();

        // Custom JWT based authentication
        httpSecurity
                .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
    }
}

Converted xml configuration; spring-security.xml

<?xml version="1.0" encoding="UTF-8"?>
<bean:beans
    xmlns:bean="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:c="http://www.springframework.org/schema/c"
    xmlns="http://www.springframework.org/schema/security"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/security
        http://www.springframework.org/schema/security/spring-security-4.0.xsd">

    <global-method-security secured-annotations="enabled"/>

    <http realm="Protected API"
        create-session="stateless"
        entry-point-ref="authenticationEntryPoint"
        authentication-manager-ref="authenticationManager">

        <csrf disabled="true"/>

        <custom-filter ref="authenticationTokenFilter" position="FORM_LOGIN_FILTER"/>

        <intercept-url pattern="/login/**" access="permitAll"/>
        <intercept-url pattern="/" access="isFullyAuthenticated()"/>
    </http>


    <bean:bean id="authenticationEntryPoint" class="com.careapple.webservice.security.EntryPointUnauthorizedHandler"/>

    <bean:bean id="userDetailService" class="com.careapple.webservice.security.service.UserDetailsServiceImpl"/>

    <bean:bean name="bcryptEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

    <authentication-manager id="authenticationManager">
        <authentication-provider user-service-ref="userDetailService">
            <password-encoder ref="bcryptEncoder"></password-encoder>
        </authentication-provider>
    </authentication-manager>

    <bean:bean id="authenticationTokenFilter" class="com.careapple.webservice.security.filter.AuthenticationTokenFilter"
        c:authenticationManager-ref="authenticationManager"/>

</bean:beans>

I'm new to spring security, first thing i want is, have i converted the java config to xml correctly. Or have i missed anything.

Next up, if i run the application with this xml file i'm getting the following error;

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.filterChains': Cannot resolve reference to bean 'org.springframework.security.web.DefaultSecurityFilterChain#0' while setting bean property 'sourceList' with key [0]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.web.DefaultSecurityFilterChain#0': Cannot resolve reference to bean 'authenticationTokenFilter' while setting constructor argument with key [3]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'authenticationTokenFilter' defined in ServletContext resource [/WEB-INF/spring-security.xml]: Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)

Where, authenticationTokenFilter is a custom filter that i'm using and it implements UsernamePasswordAuthenticationFilter of spring.

This is my web.xml file;

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    id="WebApp_ID" version="3.0">

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring-security.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <servlet>
        <servlet-name>spring</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>spring</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>authenticationTokenFilter</filter-name>
        <filter-class>com.careapple.webservice.security.filter.AuthenticationTokenFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>authenticationTokenFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

I want too things now, 1. Whether all my configuration xml files are correct. 2. If so how to rectify the exception i'm getting.

Please give your suggestions, i'm struggling with this for a long time. I referred all the related stackoverflow question but i was not able to find a solution from them.

Edit: This is my AuthenticationTokenFilter;

package com.careapple.webservice.security.filter;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.careapple.webservice.security.TokenUtils;
import com.careapple.webservice.util.AppConstant;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class AuthenticationTokenFilter extends UsernamePasswordAuthenticationFilter {

    @Autowired
    private TokenUtils tokenUtils;

    @Autowired
    private UserDetailsService userDetailsService;

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

        tokenUtils = WebApplicationContextUtils
                .getRequiredWebApplicationContext(this.getServletContext())
                .getBean(TokenUtils.class);
        userDetailsService = WebApplicationContextUtils
                .getRequiredWebApplicationContext(this.getServletContext())
                .getBean(UserDetailsService.class);

        HttpServletResponse resp = (HttpServletResponse) response;
        resp.setHeader("Access-Control-Allow-Origin", "*");
        resp.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE, PATCH");
        resp.setHeader("Access-Control-Max-Age", "3600");
        resp.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, " + AppConstant.tokenHeader);


        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String authToken = httpRequest.getHeader(AppConstant.tokenHeader);
        System.out.println("Token: " + authToken);
        String username = this.tokenUtils.getUsernameFromToken(authToken);
        System.out.println("Username: " + username);

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
            System.out.println("inside first if");
            if (this.tokenUtils.validateToken(authToken, userDetails)) {
                    UsernamePasswordAuthenticationToken authentication =
                            new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
                    SecurityContextHolder.getContext().setAuthentication(authentication);

            }
        }

            try {
                chain.doFilter(request, response);
            } catch (Exception e) {
                Map<String, String> tokenExpired = new HashMap<>();
                tokenExpired.put("TokenExpired", "Authentication token expired, please login again");
                resp.setContentType("application/json");
                resp.setStatus(HttpStatus.FORBIDDEN.value());
                resp.getWriter().write(convertObjectToJson(tokenExpired));
            }
    }

    public String convertObjectToJson(Object object) throws JsonProcessingException {
        if (object == null) {
            return null;
        }
        ObjectMapper mapper = new ObjectMapper();
        return mapper.writeValueAsString(object);
    }   
} 
karthi
  • 549
  • 3
  • 15
  • 26
  • 1. No it isn't. 2. Make your custom filter part of the spring security filter chain and not a plain file run the web.xml. Also why do you want xml configuration instead of java based one? More common is the other way around. – M. Deinum Jul 22 '16 at 08:13
  • Thanks for your reply, i'm new to spring security can you please tell how to make custom filter part of spring security filter chain. I'm not able to host the application to live server using java config, its giving me 404 that's why i'm changing my configuration to xml. I even posted these questions but was not able to get a solution from them; http://stackoverflow.com/questions/38507424/mixing-xml-and-java-config-for-spring-security – karthi Jul 22 '16 at 09:44
  • http://stackoverflow.com/questions/38482009/getting-404-when-deploying-spring-mvc-web-application-to-live-serverarvixe – karthi Jul 22 '16 at 09:44
  • See my comment on your initial question. Your issue isn't with the fact you use java configuration or xml. The fact is that you are trying to deploy something to a provider that doesn't support java. (Or at least not in the correct way). So changing it to xml won't help. – M. Deinum Jul 22 '16 at 10:22
  • I created two simple spring mvc applications one with xml config and other with java config. When i host these two to the live server only the xml configured application is working. The java configured one is giving 404. – karthi Jul 22 '16 at 10:54
  • Which server are you using (tomcat, jetty)? Which version? Do you have a web.xml in your application? – M. Deinum Jul 22 '16 at 10:55
  • This is the tomcat version i'm using in eclipse; – karthi Jul 22 '16 at 10:59
  • Tomcat Version : Apache Tomcat/7.0.62 Servlet Specification Version : 3.0 JSP version : 2.2 – karthi Jul 22 '16 at 10:59
  • And this is the tomcat version in live server. Tomcat Version : Apache Tomcat/7.0.42 Servlet Specification Version : 3.0 JSP version : 2.2 – karthi Jul 22 '16 at 11:00
  • At lest use the same version of the server locally and remote. There have been issues with earlier versions of tomcat and using no web.xml. Next to that you still are able to use the java config but use a web.xml to bootstrap your application. Just configure it accordingly. – M. Deinum Jul 22 '16 at 12:18

1 Answers1

0

Your custom authentification filter needs to be defined only on the spring-security.xml file, looking at it, it should be fine.

On your web.xml, you need to remove your filter (it's a Spring Security filter not a JavaEE filter) and add the spring security filter. The error message from Spring is that it cannot find the Spring Security filter and thus refusing the parse the pring-security.xml file.

So remove your filter from web.xml and add the spring security with these lines and all should be fine

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Regards,

Loïc

loicmathieu
  • 5,181
  • 26
  • 31
  • Hello, read again the error you are facing and it's basic spring dependancy injection related : Error creating bean with name 'authenticationTokenFilter' ... Could not resolve matching constructor ... Can you share the code of your AuthenticationTokenFilter apparently there is an issue with the constructor in it, is there an empty constructor on this class? – loicmathieu Jul 22 '16 at 11:53
  • I have edited my question with AuthenticationTokenFilter code. – karthi Jul 22 '16 at 12:12
  • Looking at the sources of the your filter and the UsernamePasswordAuthenticationFilter.java https://github.com/spring-projects/spring-security/blob/master/web/src/main/java/org/springframework/security/web/authentication/UsernamePasswordAuthenticationFilter.java there should not be any issue with constructor. But you autowire fields in it, try to inject them in the XML instead : create getters/setters and define the needed beans in spring-security.xml. – loicmathieu Jul 22 '16 at 12:18