1

I have a Spring MVC application and now I want to protect all the Controller URLs using Cookie Authentication.

1.) A third party Site will be taking care of Basic Authentication before Setting the Cookie "A" in the User-agent, and then it will redirect to my /auth URL.

2.) When the /auth URL is called, then the Filter/Interceptor/Controller must authenticate the Cookie based on set of rules agreed with the third party. Post cookie "A" authentication success, a new Cookie "B" is set in the User-agent with some custom session token in the cookie.

3.) Now when the user browses to any other protected page other than /auth, then the Filter/Interceptor/Controller must verify the Cookie "B" before allowing. If cookie "B" verification fails then redirect to some error page.

I am using Spring MVC 4.0.3.RELEASE currently. I tried for several hours to solve this requirement using Spring Security, Interceptor but could not get any proper head-way.

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Perforce123</display-name>

<servlet>
    <servlet-name>SpringDispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </init-param>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.test.label</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>SpringDispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<session-config>
    <session-timeout>30</session-timeout>
</session-config>

<context-param>
    <param-name>contextClass</param-name>
    <param-value>
        org.springframework.web.context.support.AnnotationConfigWebApplicationContext
    </param-value>
</context-param>

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

<filter>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.test.label</groupId>
<artifactId>Perforce123</artifactId>
<version>1.0</version>
<packaging>war</packaging>

<name>Perforce123</name>
<url>http://maven.apache.org</url>

<properties>
    <java.version>1.7</java.version>
    <spring.version>4.0.3.RELEASE</spring.version>
    <cglib.version>2.2.2</cglib.version>
</properties>

<dependencies>
    <!-- Spring core & mvc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
        <exclusions>
          <exclusion>
            <artifactId>commons-logging</artifactId>
            <groupId>commons-logging</groupId>
          </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
        <exclusions>
          <exclusion>
            <artifactId>commons-logging</artifactId>
            <groupId>commons-logging</groupId>
          </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>${spring.version}</version>
        <type>jar</type>
        <scope>compile</scope>
        <exclusions>
          <exclusion>
            <artifactId>commons-logging</artifactId>
            <groupId>commons-logging</groupId>
          </exclusion>
        </exclusions>
    </dependency>

    <!-- CGLib for @Configuration -->
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib-nodep</artifactId>
        <version>${cglib.version}</version>
        <scope>runtime</scope>
    </dependency>


    <!-- Servlet Spec -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>javax.servlet.jsp-api</artifactId>
        <version>2.3.1</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>jstl</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>      
    </dependency>
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <!-- <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.9</version>
    </dependency> -->
    <dependency>
        <groupId>commons-lang</groupId>
        <artifactId>commons-lang</artifactId>
        <version>2.2</version>
    </dependency>

    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jcl-over-slf4j</artifactId>
        <version>1.7.25</version>
    </dependency>
    <dependency>
        <groupId>org.xerial</groupId>
        <artifactId>sqlite-jdbc</artifactId>
        <version>3.20.0</version>
    </dependency>
</dependencies>

<build>
    <finalName>Perforce123</finalName>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.3</version>
            <configuration>
                <source>${java.version}</source>
                <target>${java.version}</target>
            </configuration>
        </plugin>
        <!-- <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.12.4</version>
        </plugin> -->
    </plugins>
</build>
</project>

Mvc Config Class

@Configuration
@ComponentScan(basePackages="com.test.label")
@EnableWebMvc
@PropertySource(value= {"classpath:application.properties"})
public class MvcConfiguration extends WebMvcConfigurerAdapter{
   private static Logger logger =           
 LoggerFactory.getLogger(MvcConfiguration.class);

@Bean(name="myProp")
public static PropertySourcesPlaceholderConfigurer propertyPlaceHolderConfigurer() {
    return new PropertySourcesPlaceholderConfigurer();
}

@Bean
public ViewResolver getViewResolver(){
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix("/WEB-INF/views/");
    resolver.setSuffix(".jsp");
    return resolver;
}

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}

}

Please help provide a working code, or link to some working example for this use-case. Thanks in advance.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
sunny_dev
  • 765
  • 3
  • 15
  • 34
  • Its really not clear from your question as what challenges you face. You have described point 2 & 3 well and its hard to understand what is stopping you to implement those points two points. I guess, asking for a working code is not a policy on SO. – Sabir Khan Dec 25 '17 at 09:52
  • In my opinion, you basically need a single filter which will check URL and do necessary processing as per received URL. If url is `/auth` , verify Cookie A and Create B , else Verify B . – Sabir Khan Dec 25 '17 at 09:55
  • Sabir, I am able to do what you suggesting using plain vanilla Servlet Filters. I wanted to achieve this using Spring Security – sunny_dev Dec 26 '17 at 13:40

1 Answers1

1

This answer isn't about giving you a working code but just guidelines.

  1. Step-1 : Implement your single filter using AbstractAuthenticationProcessingFilter and write your business logic in attemptAuthentication method.

For me, I either return null or an instance of AbstractAuthenticationToken . I have a POJO implementing AbstractAuthenticationToken for my needs.

  1. Step -2 : Provide authentication manager in your filter by doing something like below ,

@Override @Autowired public void setAuthenticationManager( AuthenticationManager authenticationManager) { super.setAuthenticationManager(authenticationManager); }

  1. Step-3 : implement successfulAuthentication & unsuccessfulAuthentication methods in filter with something like below,

@Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { SecurityContext context = SecurityContextHolder.createEmptyContext(); context.setAuthentication(authResult); SecurityContextHolder.setContext(context); chain.doFilter(request, response); }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request,
        HttpServletResponse response, AuthenticationException failed)
        throws IOException, ServletException {
    SecurityContextHolder.clearContext();
    }`
  1. Step-4 AuthenticationProvider : Implement org.springframework.security.authentication.AuthenticationProvider because in your step-1, you would be calling something like this - getAuthenticationManager().authenticate( token)

Here in authenticate method, you would basically write your detailed business logic of authentication

  1. Step - 5 - Spring Security Configuration : Now here , you will write your actual security configuration overriding lots of HttpSecurity features but I am writing this step so you provide your authentication provider && written filter to security by doing something like this,

@Override protected void configure(AuthenticationManagerBuilder auth) { auth.authenticationProvider(your_provider); }

This security class will extend WebSecurityConfigurerAdapter & be annotated with @EnableWebSecurity

You will have to also put in your custom filter before one of pre defined spring security filters using beforeFilter or afterFilter methods . This is a lengthy step and you will find lots of documentation on Internet as how to override HttpSecurity and put in your filters & providers there.

Refer Add http security filter in java config

Section - Integrating the Security Filters on Spring Boot here

Sabir Khan
  • 9,826
  • 7
  • 45
  • 98