Skip to main content

Spring Security with Boot - Adding LDAP Over Already Authenticated User from One Module

There are hundreds of Spring Tutorials and Stack of Questions/Answers online to add LDAP based Authentication in your web application using Spring Security

However, while working on one of my project assignments, I faced a strange conundrum whereas per the requirements of the application the REST-based application is required to be accessed from within a spring based web application of which this Rest API app. will be part of and also independently hosted for outside application or for 3rd Party Access.

Let's make some assumptions here for the sake of better understanding the issue/s and resolution/s

  • Main web-app as MWAPP
  • Rest Based API Module as RESTAPP
  • MWAPP is based on Spring MVC with Spring Security and all configuration based
  • RESTAPP is Spring Boot based Simple Web App.

Issues::

  1. MWAPP is having LDAP Security using spring config xml file as users can use this app with some features which does nt need API exposed in RESTAPP hence user logs in and is LDAP authenticated using FORM_LOGIN_FILTER which throws LDAP login page when users hit the URL but few users might need to use RESTAPP and as logged in user is already LDAP authenticated they should be allowed access but as the REST APP can be accessed separately as well we would need to again intercept the requests coming to different APIs of this module, this situation caused the issue where when we tried to add LDAP authentication to this Spring Boot app we get 401 ... Bad Credentials Exception from the Spring Filter triggered at the UserNamePasswordAuthenticationToken Filter.

         We have 2 issues here :

            1) We need to reauthenticate all requests but two, coming to RESTAPP using LDAP, as these two requests or request mappers will need to be exempted from Spring Security. Here because of the kind of configuration, we have for MWAPP the authentication manager was defined as :

                    <security:authentication-manager >  
                             <security:authentication-provider ref="ldapAuthenticationProvider" /> 
                    </security:authentication-manager>

           With this configuration when we try to reauthenticate against LDAP the requests going to RESTAPP  we will not get the password from authentication object , hence we will get 401 : Bad Credentials exception and hence access denied.

            2) We need to make sure the requests coming from 3rdParties to this RESTAPP are properly authenticated and authorized with LDAP as the 3rd parties will need to be in the specific group with certain access.

Solutions

       We need to change the configuration of auth manager as below :

        <security:authentication-manager erase-credentials="false">
             <security:authentication-provider ref="myAuthenticationProvider" />
       </security:authentication-manager>


With this config at the MWAPP level we will retain the Auth object in complete form to actually call the  Authentication auth = SecurityContextHolder.getContextHolder.getAuthentication()
     String password = auth.getCredentials().toString() will get you the password which was earlier set to null by authentication manager after first authentication process completed.


In Spring Boot when we want to implement Spring Security, we need to initiate Spring Security Filter Chain in Config based we add the bean in web.xml as springSecurityFilterChain

In Spring Boot we need to create a class (Configuration type) as :
public class SecurityWebApplicationInitializer 
   extends AbstractSecurityWebApplicationInitializer {
}
Or

import org.springframework.security.web.context.*;

public class SecurityWebApplicationInitializer
 extends AbstractSecurityWebApplicationInitializer {

 public SecurityWebApplicationInitializer() {
  super(WebSecurityConfig.class);
 }
}



What this does is it registers the springSecurityFilterChain with the war.

To Add Security specific to various requirements we can have below configurations to set Spring Security in Application Context

WebSecurityConfigurerAdapter

The @EnableWebSecurity annotation and WebSecurityConfigurerAdapter work together to provide web based security. By extending WebSecurityConfigurerAdapter and only a few lines of code we are able to do the following:
  • Require the user to be authenticated prior to accessing any URL within our application
  • Create a user with the username “user”, password “password”, and role of “ROLE_USER”
  • Enables HTTP Basic and Form based authentication
  • Spring Security will automatically render a login page and logout success page for you

@Configuration
@EnableWebSecurity
public class HelloWebSecurityConfiguration
   extends WebSecurityConfigurerAdapter {

  @Autowired
  public void configureGlobal(AuthenticationManagerBuilder auth) {
    auth
      .inMemoryAuthentication()
        .withUser("user").password("password").roles("USER");
  }
}
For your reference, this is similar to the following XML configuration with a few exceptions:
  • Spring Security will render the login, authentication failure url, and logout success URLs
  • The login-processing-url will only be processed for HTTP POST
  • The login-page will only be processed for HTTP GET

<http use-expressions="true">
  <intercept-url pattern="/**" access="authenticated"/>
  <logout
    logout-success-url="/login?logout"
    logout-url="/logout"
  />
  <form-login
    authentication-failure-url="/login?error"
    login-page="/login"
    login-processing-url="/login"
    password-parameter="password"
    username-parameter="username"
  />
</http>
<authentication-manager>
  <authentication-provider>
    <user-service>
      <user name="user" 
          password="password" 
          authorities="ROLE_USER"/>
    </user-service>
  </authentication-provider>
</authentication-manager>

CustomWebSecurityConfigurerAdapter

Our HelloWebSecurityConfiguration sample, demonstrates that Spring Security Java configuration can provide some very nice defaults for us. Let’s take a look at some basic customization.

@EnableWebSecurity
@Configuration
public class CustomWebSecurityConfigurerAdapter extends
   WebSecurityConfigurerAdapter {
  @Autowired
  public void configureGlobal(AuthenticationManagerBuilder auth) {
    auth
      .inMemoryAuthentication()
        .withUser("user")  // #1
          .password("password")
          .roles("USER")
          .and()
        .withUser("admin") // #2
          .password("password")
          .roles("ADMIN","USER");
  }

  @Override
  public void configure(WebSecurity web) throws Exception {
    web
      .ignoring()
         .antMatchers("/resources/**"); // #3
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
      .authorizeUrls()
        .antMatchers("/signup","/about").permitAll() // #4
        .antMatchers("/admin/**").hasRole("ADMIN") // #6
        .anyRequest().authenticated() // 7
        .and()
    .formLogin()  // #8
        .loginUrl("/login") // #9
        .permitAll(); // #5
  }
}
Assuming that we adjust AbstractAnnotationConfigDispatcherServletInitializer to load our new configuration, our CustomWebSecurityConfigurerAdapter will do the following:
  • Allow in memory authentication with a user named “user”
  • Allow in memory authentication with an administrative user named “admin”
  • Ignore any request that starts with “/resources/”. This is similar to configuring http@security=none when using the XML namespace configuration.
  • Allow anyone (including unauthenticated users) to access to the URLs “/signup” and “/about”
  • Allow anyone (including unauthenticated users) to access to the URLs “/login” and “/login?error”. The permitAll() in this case means, allow access to any URL that formLogin() uses.
  • Any URL that starts with “/admin/” must be an administrative user. For our example, that would be the user “admin”.
  • All remaining URLs require that the user be successfully authenticated
  • Setup form based authentication using the Java configuration defaults. Authentication is performed when a POST is submitted to the URL “/login” with the parameters “username” and “password”.
  • Explicitly state the login page, which means the developer is required to render the login page when GET /login is requested.
For those that are familiar with the XML based configuration, the configuration above is very similar to the following XML configuration:

<http security="none" pattern="/resources/**"/>
<http use-expressions="true">
  <intercept-url pattern="/logout" access="permitAll"/>
  <intercept-url pattern="/login" access="permitAll"/>
  <intercept-url pattern="/signup" access="permitAll"/>
  <intercept-url pattern="/about" access="permitAll"/>
  <intercept-url pattern="/**" access="hasRole('ROLE_USER')"/>
  <logout
      logout-success-url="/login?logout"
      logout-url="/logout"
  />
  <form-login
      authentication-failure-url="/login?error"
      login-page="/login"
      login-processing-url="/login"
      password-parameter="password"
      username-parameter="username"
  />
</http>
<authentication-manager>
  <authentication-provider>
    <user-service>
      <user name="user" 
          password="password" 
          authorities="ROLE_USER"/>
      <user name="admin" 
          password="password" 
          authorities="ROLE_USER,ROLE_ADMIN"/>
    </user-service>
  </authentication-provider>
</authentication-manager>

Comments

Popular posts from this blog

Spring MVC

++++++++++++++++++++++++++++ Spring Web MVC framework ++++++++++++++++++++++++++++ The Spring Web model-view-controller (MVC) framework is designed around a DispatcherServlet that dispatches requests to handlers, with configurable handler mappings, view resolution, locale, time zone and theme resolution as well as support for uploading files. a) With the introduction of Spring 3.0, the @Controller mechanism also allows you to create RESTful Web sites and applications, through the @PathVariable annotation and other features b) Upon initialization of a DispatcherServlet, Spring MVC looks for a file named [servlet-name]-servlet.xml in the WEB-INF directory of your web application and creates the beans defined there, overriding the definitions of any beans defined with the same name in the global scope. c) For Spring based validation at controller level - An @RequestBody method parameter can be annotated with @Valid, in which case it will be validated using the co

Spring OXM

Spring's Object/XML Mapping support : Converting an XML document to and from an object. This conversion process is also known as XML Marshalling, or XML Serialization. a marshaller is responsible for serializing an object (graph) to XML an unmarshaller deserializes the XML to an object graph <oxm:jaxb2-marshaller id="anyWSMarshaller" contextPath="com.utkarsh.org.phase2.jaxbgenerated"/> <bean id="anyServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate"> <property name="marshaller" ref="acbsPaymentsAdvancesWSMarshaller"/> <property name="unmarshaller" ref="acbsPaymentsAdvancesWSMarshaller"/> <property name="defaultUri" value="http://localhost:8080/SpringOXMApp/anyUrl/testService"/> <property name="messageSenders">  <list>   <ref bean="httpMsgSende