Authenticating over a BASIC scheme

Authenticating through a BASIC scheme is a popular solution for stateless applications like ours. Credentials are sent over with HTTP requests.

Getting ready

In this recipe, we complete the Spring Security configuration. We make it support the BASIC authentication scheme required for the application.

We slightly customize the generated response-headers, so they don't trigger the browser to show-up a native BASIC authentication form (which is not an optimal experience for our users).

How to do it...

  1. In order to use the Spring security namespace, we add the following filter to the cloudstreetmarket-api web.xml:
    <filter>
      <filter-name>springSecurityFilterChain</filter-name>
      <filter-class> org.sfw.web.filter.DelegatingFilterProxy
      </filter-	class>
    </filter>
    <filter-mapping>
      <filter-name>springSecurityFilterChain</filter-name>
      <url-pattern>/*</url-pattern>
    </filter-mapping>
  2. A Spring configuration file has been created specifically for Spring Security in the cloudstreetmarket-api module. This file hosts the following bean definitions:
    <bean id="authenticationEntryPoint" class="edu.zc.csm.api.authentication.CustomBasicAuthenticationEntryPoint">
      <property name="realmName" value="cloudstreetmarket.com" />
    </bean> 
    <security:http create-session="stateless" authentication-manager-ref="authenticationManager" entry-point-ref="authenticationEntryPoint">
        <security:custom-filter ref="basicAuthenticationFilter" after="BASIC_AUTH_FILTER" />
       <security:csrf disabled="true"/>
    </security:http>
    
    <bean id="basicAuthenticationFilter" class="org.sfw.security.web.authentication.www.BasicAuthenticationFilter">
      <constructor-arg name="authenticationManager" ref="authenticationManager" />
      <constructor-arg name="authenticationEntryPoint" ref="authenticationEntryPoint" />
    </bean>
    <security:authentication-manager alias="authenticationManager">
        <security:authentication-provider user-service-ref='communityServiceImpl'>
          <security:password-encoder ref="passwordEncoder"/>
      </security:authentication-provider>
    </security:authentication-manager>
    
    <security:global-method-security secured-annotations="enabled" pre-post-annotations="enabled" authentication-manager-ref="authenticationManager"/>
  3. This new configuration refers to the CustomBasicAuthenticationEntryPoint class. This class has the following content:
    public class CustomBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
      @Override
      public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        response.setHeader("WWW-Authenticate", "CSM_Basic realm= + getRealmName() + ");
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage()
        );
      }
    }
  4. A new @ExceptionHandler has been added to catch authentication Exceptions:
    @ExceptionHandler({BadCredentialsException.class, AuthenticationException.class, AccessDeniedException.class})
    protected ResponseEntity<Object> handleBadCredentials(final RuntimeException ex, final WebRequest request) {
      return handleExceptionInternal(ex, "The attempted  operation has been denied!", new HttpHeaders(),   FORBIDDEN, request);
    }
    ...

    Note

    That's pretty much it! We have made our backend support a BASIC authentication. However, we haven't restricted our services (as secure objects) yet. We will do that now.

  5. For the example purpose, please do update the IMarketService interface in cloudstreetmarket-core. Add the @Secured("ROLE_BASIC") annotation to the Type as follows:
    @Secured ("ROLE_BASIC")
    public interface IMarketService {
      Page<IndexOverviewDTO> getLastDayIndicesOverview( MarketCode market, Pageable pageable);
      Page<IndexOverviewDTO> getLastDayIndicesOverview( Pageable pageable);
      HistoProductDTO getHistoIndex(String code, MarketCode market, Date fromDate, Date toDate, QuotesInterval interval);
    }
  6. Now restart the Tomcat server (doing this will drop your previous user creation).
  7. In your favorite web browser, open the developer-tab and observe the AJAX queries when you refresh the home page. You should notice that two AJAX queries have returned a 403 status code (FORBIDDEN).
    How to do it...

    These queries have also returned the JSON response:

    {"error":"Access is denied","message":"The attempted operation has been denied!","status":403","date":"2015-05-05 18:01:14.917"}
  8. Now, using the login feature/popup, do log in with one of the previously created users that have a BASIC role:
    Username: <userC> 
    Password: <123456>
  9. Refresh the page and observe the same two AJAX queries. Amongst the request headers, you can see that our frontend has sent a special Authorization header:
    How to do it...
  10. This Authorization header carries the value: Basic dXNlckM6MTIzNDU2. The encoded dXNlckM6MTIzNDU2 is the base64-encoded value for userC:123456.
  11. Let's have a look at the response to these queries:
    How to do it...

    The status is now 200 (OK) and you should also have received the right JSON result:

    How to do it...
  12. The Server sent back a WWW-Authenticate header in the response to the value: CSM_Basic realm"="cloudstreetmarket.com"
  13. Finally, do revert the change you made in IMarketService (in the 5th step).

How it works...

We are going to explore the concepts behind a BASIC authentication with Spring Security:

The Spring Security namespace

As always, a Spring configuration namespace brings a specific syntax that suits the needs and uses for a module. It lightens the overall Spring configuration with a better readability. Namespaces often come with configuration by default or auto configuration tools.

The Spring Security namespace comes with the spring-security-config dependency and can be defined as follows in a Spring configuration file:

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:security="http://www.springframework.org/schema/security" xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance xsi:schemaLocation"="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.0.xsd">
    ...
</beans>

The namespace stages three top-level components: <http> (about web and HTTP security), <authentication-manager>, and <global-method-security> (service or controller restriction).

Then, other concepts are referenced by those top-level components as attribute or as child element: <authentication-provider>, <access-decision-manager> (provides access decisions for web and security methods), and <user-service> (as UserDetailsService implementations).

The <http> component

The <http> component of the namespace provides an auto-config attribute that we didn't use here. The <http auto-config"="true"> definition would have been a shortcut for the following definition:

  <http>
    <form-login />
    <http-basic />
    <logout />
  </http>

It isn't worth it for our REST API because we didn't plan to implement a server-side generated view for a form login. Also, the <logout> component would have been useless for us since our API doesn't manage sessions.

Finally, the <http-basic> element creates underlying BasicAuthenticationFilter and BasicAuthenticationEntryPoint to the configuration.

We have made use of our own BasicAuthenticationFilter in order to customize the WWW-Authenticate response's header value from Basic base64token to CSM_Basic base64token. This because the AJAX HTTP responses (from our API) containing a WWW-Authenticate header with a value starting with a Basic keyword automatically trigger the web-browser to open a native Basic-form popup. It was not the type of user experience we wanted to set up.

The Spring Security filter-chain

In the very first step of the recipe, we have declared a filter named springSecurityFilterChain in web.xml:

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

Here, springSecurityFilterChain is also a Spring bean that is created internally by the Spring Security namespace (specifically the http component). A DelegatingFilterProxy is a Spring infrastructure that looks for a specific bean in the application context and invokes it. The targeted bean has to implement the Filter interface.

The whole Spring Security machinery is hooked-up in this way through finally one single bean.

The configuration of the <http> element plays a central-role in the definition of what the filter-chain is made of. It is directly the elements it defines, that create the related filters.

 

"Some core filters are always created in a filter chain and others will be added to the stack depending on the attributes and child elements which are present."

 
 --Spring Security

It is important to distinguish between the configuration-dependant filters and the core filters that cannot be removed. As core filters, we can count SecurityContextPersistenceFilter, ExceptionTranslationFilter, and FilterSecurityInterceptor. These three filters are natively bound to the <http> element and can be found in the next table.

This table comes from the Spring Security reference document and it contains all the core filters (coming with the framework) that can be activated using specific elements or attributes. They are listed here in the order of their position in the chain.

Alias

Filter Class

Namespace Element or Attribute

CHANNEL_FILTER

ChannelProcessingFilter

http/intercept-url@requires-channel

SECURITY_CONTEXT_FILTER

SecurityContextPersistenceFilter

http

CONCURRENT_SESSION_FILTER

ConcurrentSessionFilter

session-management/concurrency-control

HEADERS_FILTER

HeaderWriterFilter

http/headers

CSRF_FILTER

CsrfFilter

http/csrf

LOGOUT_FILTER

LogoutFilter

http/logout

X509_FILTER

X509AuthenticationFilter

http/x509

PRE_AUTH_FILTER

AbstractPreAuthenticatedProcessingFilter 

Subclasses

N/A

CAS_FILTER

CasAuthenticationFilter

N/A

FORM_LOGIN_FILTER

UsernamePasswordAuthenticationFilter

http/form-login

BASIC_AUTH_FILTER

BasicAuthenticationFilter

http/http-basic

SERVLET_API_SUPPORT_FILTER

SecurityContextHolderAwareRequestFilter

http/@servlet-api-provision

JAAS_API_SUPPORT_FILTER

JaasApiIntegrationFilter

http/@jaas-api-provision

REMEMBER_ME_FILTER

RememberMeAuthenticationFilter

http/remember-me

ANONYMOUS_FILTER

AnonymousAuthenticationFilter

http/anonymous

SESSION_MANAGEMENT_FILTER

SessionManagementFilter

session-management

EXCEPTION_TRANSLATION_FILTER

ExceptionTranslationFilter

http

FILTER_SECURITY_INTERCEPTOR

FilterSecurityInterceptor

http

SWITCH_USER_FILTER

SwitchUserFilter

N/A

Remember that custom filters can be positioned relatively, or can replace any of these filters using the custom-filter element:

<security:custom-filter ref="myFilter" after="BASIC_AUTH_FILTER"/>
Our <http> configuration

We have defined the following configuration for the <http> 'namespace's component:

<security:http create-session="stateless" entry-point-ref="authenticationEntryPoint" authentication-manager- ref="authenticationManager">
  <security:custom-filter ref="basicAuthenticationFilter" after="BASIC_AUTH_FILTER" />
  <security:csrf disabled="true"/>
</security:http>
<bean id="basicAuthenticationFilter" class="org.sfw.security.web.authentication.www.BasicAuthenticationFilter">
  <constructor-arg name="authenticationManager" ref="authenticationManager" />
  <constructor-arg name="authenticationEntryPoint" ref="authenticationEntryPoint" />
</bean>
<bean id="authenticationEntryPoint" class="edu.zc.csm.api. authentication.CustomBasicAuthenticationEntryPoint">
  <property name="realmName" value="${realm.name}" />
</bean>

Here, we tell Spring not to create sessions and to ignore incoming sessions using create-session=="stateless". We have done this to pursue the stateless and scalable Microservices design.

We have also disabled the Cross-Site Request Forgery (csrf) support for now, for the same reason. This feature has been enabled by default by the framework since the Version 3.2.

It has been necessary to define an entry-point-ref because we didn't implement any authentication strategy preconfigured by the namespace (http-basic or login-form).

We have defined a custom filter BasicAuthenticationFilter to be executed after the theoretical position of the core BASIC_AUTH_FILTER.

We are now going to see which roles play the three references made to: authenticationEntryPoint, authenticationManager, and basicAuthenticationFilter.

The AuthenticationManager interface

First of all, AuthenticationManager is a single-method interface:

public interface AuthenticationManager {
  Authentication authenticate(Authentication authentication) throws AuthenticationException;
}

Spring Security provides one implementation: ProviderManager. This implementation allows us to plug in several AuthenticationProviders. The ProviderManager tries all the AuthenticationProviders in order, calling their authenticate method. The code is as follows:

public interface AuthenticationProvider {
  Authentication authenticate(Authentication authentication)
    throws AuthenticationException;
  boolean supports(Class<?> authentication);
}

The ProviderManager stops its iteration when it finds a non-null Authentication object. Alternatively, it fails the Authentication when an AuthenticationException is thrown.

Using the namespace, a specific AuthenticationProviders can be targeted using the ref element as shown here:

<security:authentication-manager >
  <security:authentication-provider ref='myAuthenticationProvider'/>
</security:authentication-manager>

Now, here is our configuration:

<security:authentication-manager alias"="authenticationManager"">
  <security:authentication-provider user-service-ref='communityServiceImpl'>
    <security:password-encoder ref="passwordEncoder"/>
  </security:authentication-provider>
</security:authentication-manager>

There is no ref element in our configuration. The namespace will by default instantiate a DaoAuthenticationProvider. It will also inject our UserDetailsService implementation: communityServiceImpl, because we have specified it with user-service-ref.

This DaoAuthenticationProvider throws an AuthenticationException when the password submitted in a UsernamePasswordAuthenticationToken doesn't match the one which is loaded by UserDetailsService (making use of the loadUserByUsername method).

It exists a few other AuthenticationProviders that could be used in our projects, for example,, RememberMeAuthenticationProvider, LdapAuthenticationProvider, CasAuthenticationProvider, or JaasAuthenticationProvider.

Basic authentication

As we have said using a BASIC scheme is a great technique for REST applications. However when using it, it is critical to use an encrypted communication protocol (HTTPS) as the passwords are sent in plain text.

As demonstrated in the How to do it section, the principle is very simple. The HTTP requests are the same as usual with an extra header Authentication. This header has the value made of the keyword Basic followed by a space, followed by a String encoded in base 64.

We can find online a bunch of free services to quickly encode/decode in base 64 a String. The String to be encoded in base 64 has to be in the following form: <username>:<password>.

BasicAuthenticationFilter

To implement our Basic authentication, we have added BasicAuthenticationFilter to our filter chain. This BasicAuthenticationFilter (org.sfw.security.web.authentication.www.BasicAuthenticationFilter) requires an authenticationManager and optionally an authenticationEntryPoint.

The optional configuration of an authenticationEntryPoint drives the filter towards two different behaviours presented next.

Both starts the same way: the filter is triggered from its position in the chain. It looks for the authentication header in the request and delegates to the authenticationManager, which then relies on the UserDetailsService implementation to compare it with the user credentials from the database.

With an authenticationEntryPoint

This is our configuration, which behaves in the following way:

  • When the authentication succeeds, the filter-chain stops, and an Authentication object is returned.
  • When the authentication fails, the authenticationEntryPoint method is invoked in an interruption of the filter-chain. Our authentication entry-point sets a custom WWW-Authenticate response header and a 401 status-code (FORBIDDEN).

This type of configuration provides a preauthentication where the Authentication Header in the HTTP Request is checked to see whether or not the business services require an authorization (Secure Object).

This configuration allows a quick feedback with a potential native BASIC form prompted by the web browser. We have chosen this configuration for now in our application.

Without an authenticationEntryPoint

Without an authenticationEntryPoint, the filter behaves as follows:

  • When the authentication succeeds, the filter-chain stops, and an Authentication object is returned.
  • When the authentication fails, the filter chain continues. After that, if another authentication succeeds in the chain, the user is authenticated accordingly. But, if no other authentication succeeds in the chain, then the user is authenticated with an anonymous role and this may or may not suit the services access levels.

There is more…

In the Spring Security reference

This section has been largely inspired from the Spring rsecurity reference, which is again a great resource:

http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle

An appendix provides a very complete guide to the Spring Security namespace:

http://docs.spring.io/spring-security/site/docs/current/reference/html/appendix-namespace.html

The remember-me cookie/feature

We passed over the RememberMeAuthenticationFilter that provides different ways for the server to remember the identity of a Principal between sessions. The Spring Security reference provides extensive information on this topic.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset