Category Archives for spring-security

Securing Spring Data REST with PreAuthorize

Securing Spring Data REST with PreAuthorize is an alternative method to securing Spring Data REST API’s, building on the previous apporach covered in Spring Security and Spring Data REST.

Securing Spring Data REST with Preauthorize

Securing Spring Data REST with PreAuthorize code is available on github –

https://github.com/farrelmr/introtospringdatarest/tree/3.0.0

https://github.com/farrelmr/introtospringdatarest/releases/tag/3.0.0

Run the code by typing –

mvnw spring-boot:run

Maven

In order to use the preAuthorise attributes you need to import spring-boot-security –

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

SecurityConfig

The key points are –

  • Maintain security for roles “ADMIN” and “USER” on “/rest/**” requests
  • @EnableGlobalMethodSecurity(prePostEnabled = true) – this enables the annotations on the JPA model
package com.javabullets.springdata.jparest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.annotation.method.configuration.EnableGlobalMethodSecurity;

import org.springframework.http.HttpMethod;

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth)
			throws Exception {
		auth.
			inMemoryAuthentication()
				.withUser("user").password("user").roles("USER").and()
				.withUser("admin").password("admin").roles("USER","ADMIN");
	}

    @Override
    protected void configure(HttpSecurity http) throws Exception {
	    http
          .authorizeRequests()
            .antMatchers("/rest/**").hasAnyRole("ADMIN","USER").and()
          .httpBasic()
            .and()
		   .csrf().disable();		   
    }
}

ParkrunCourseRepository

The key changes are the use of PreAuthorize on the ParkrunCourseRepository –

package com.javabullets.springdata.jparest;

import org.springframework.data.repository.CrudRepository;
import org.springframework.security.access.prepost.PreAuthorize;

@PreAuthorize("hasRole('ROLE_USER')")
public interface ParkrunCourseRepository extends CrudRepository<ParkrunCourse, Long> {
	@Override
	@PreAuthorize("hasRole('ROLE_ADMIN')")
	ParkrunCourse save(ParkrunCourse parkrunCourse);
}

The key points are –

  • Use of hasRole – note you need to append ROLE_ to your roles
  • We could also add PreAuthorize security to custom methods defined in the repository

Putting It Together

mvnw spring-boot:run

POST methods – Access Forbidden

curl -u user:user -X POST -H "Content-Type:application/json" -d "{  \"courseName\" : \"adminOnly\",  \"url\" : \"url\",  \"averageTime\" : \"10000\" }" http://localhost:8080/rest/parkrunCourses

{"timestamp":1496134011261,"status":403,"error":"Forbidden","message":"Access is denied","path":"/rest/parkrunCourses"}

POST methods – Access Allowed

curl -u admin:admin -X POST -H "Content-Type:application/json" -d "{  \"courseName\" : \"adminOnly\",  \"url\" : \"url\",  \"averageTime\" : \"10000\" }" http://localhost:8080/rest/parkrunCourses

{
  "courseName" : "adminOnly",
  "url" : "url",
  "averageTime" : 10000,
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/rest/parkrunCourses/13"
    },
    "parkrunCourse" : {
      "href" : "http://localhost:8080/rest/parkrunCourses/13"
    }
  }
}

Discussion

This post shows a different approach to role based security of Spring Data REST with PreAuthorize. It is not a question of which method is better, but which is practical. It may be that you cannot use preAuthorize in your codebase, but can use role based authentication as outlined in Spring Security and Spring Data REST

From a design point of view you may want to define a custom repository with your core PreAuthorize rules, allowing your model to inherit security.

Conclusions

This post shows how Spring Security and Spring Data REST can be combined to secure REST API URL’s and HTTP methods. It used a basic form of Spring authentication, combining a MemoryRealm with the security configuration. We have also demonstrated how to restrict access to REST methods based on user group.

My next post will look at how Spring Data REST can restrict access by deciding what methods it exposes, and what fields are exposed

Spring Security and Spring Data REST

Spring Security and Spring Data REST are discussed in this post. It builds on the previous post “Introduction to Spring Data REST” and my series of posts on Spring Security –

Spring Security and Spring Data REST

Spring Security is split into two components –

  • Authentication – Defined by AuthenticationManager, or the source of the authentication credentials
  • Authorisation – what we want to protect – URL’s, Roles, method

This tutorial is only considering BasicAuthentication with an memory realm. I will probably return to this subject to show how to properly harden a RESTful API.

Source Code

Code available on github –

https://github.com/farrelmr/introtospringdatarest/tree/2.0.0

https://github.com/farrelmr/introtospringdatarest/releases/tag/2.0.0

Run the code by typing –

mvnw spring-boot:run

Maven

You can secure spring boot by simply including this dependency –

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

You can then get the password when you startup spring boot, but that is not very practical for most usages.

SecurityConfig

Ive moved the Spring Data REST API URL to /rest in application.properties –

spring.data.rest.basePath=/rest

SecurityConfig has –

  • Two users – user(Role – USER), admin(Role – admin)
  • Restrictions –
    • Only “ADMIN” or “USER” roles can access “/rest”,
    • Only “ADMIN” users can POST to the web service –
package com.javabullets.springdata.jparest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.http.HttpMethod;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth)
            throws Exception {
        auth.
            inMemoryAuthentication()
                .withUser("user").password("user").roles("USER").and()
                .withUser("admin").password("admin").roles("USER","ADMIN");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
          .authorizeRequests()
            .antMatchers(HttpMethod.POST, "/rest/parkrunCourses/**").hasRole("ADMIN")
            .antMatchers("/rest/**").hasAnyRole("ADMIN","USER").and()
          .httpBasic()
            .and()
           .csrf().disable();
    }
}

Putting It Together

mvnw spring-boot:run

Ive switched to curl for calling the API’s as its clearer for tutorials –

No credentials – Doesnt Authenticate

curl  -X GET -H "Content-Type:application/json" http://localhost:8080/rest/parkrunCourses/1
{"timestamp":1496069649024,"status":401,"error":"Unauthorized","message":"Full authentication is required to access this resource","path":"/rest/parkrunCourses/1"}

user/user credentials – Authenticates

curl -u user:user -X GET -H "Content-Type:application/json" http://localhost:8080/rest/parkrunCourses/1
{
  "courseName" : "Inverness",
  "url" : "http://www.parkrun.org.uk/inverness/",
  "averageTime" : 1582,
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/rest/parkrunCourses/1"
    },
    "parkrunCourse" : {
      "href" : "http://localhost:8080/rest/parkrunCourses/1"
    }
  }
}

POST methods – Access Forbidden

curl -u user:user -X POST -H "Content-Type:application/json" -d "{  \"courseName\" : \"adminOnly\",  \"url\" : \"url\",  \"averageTime\" : \"10000\" }" http://localhost:8080/rest/parkrunCourses
{"timestamp":1496069812087,"status":403,"error":"Forbidden","message":"Access is denied","path":"/rest/parkrunCourses"}

POST methods – Access Allowed

curl -u admin:admin -X POST -H "Content-Type:application/json" -d "{  \"courseName\" : \"adminOnly\",  \"url\" : \"url\",  \"averageTime\" : \"10000\" }" http://localhost:8080/rest/parkrunCourses
{
  "courseName" : "adminOnly",
  "url" : "url",
  "averageTime" : 10000,
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/rest/parkrunCourses/13"
    },
    "parkrunCourse" : {
      "href" : "http://localhost:8080/rest/parkrunCourses/13"
    }
  }
}

Conclusions

This post shows how Spring Security and Spring Data REST can be combined to secure REST API URL’s and HTTP methods. It used a basic form of Spring authentication, combining a MemoryRealm with the security configuration. We have also demonstrated how to restrict access to REST methods based on user group.

What does Spring DelegatingFilterProxy do?

What does Spring DelegatingFilterProxy do? I had never given it much thought about how Spring Security integrates with web.xml until I had to diagnose an issue involving the DelegatingFilterProxy and my Spring Security configuration.

Isle Of Harris

What does Spring DelegatingFilterProxy do?

I knew the starting point is the springSecurityFilterChain which uses the DelegatingFilterProxy, and this would instantiate the Spring Security filters according to my spring configuration –

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

But what next?

DelegatingFilterProxy

A look at the javadoc for DelegatingFilterProxy states that –

Proxy for a standard Servlet Filter, delegating to a Spring-managed bean that implements the Filter interface

It further states that the filter-name corresponds to a bean in the Spring application context.

So in terms of Spring Security the DelegatingFilterProxy will look though the Spring Application Context for a bean “springSecurityFilterChain”. The only requirement for Delegated beans is that they must implement javax.servlet.Filter.

Initializing the springSecurityFilterChain

The springSecurityFilterChain is initialised in your spring configuration by, which will be passed to your DispatcherServlet –

<http ...> </http>

We can see this in action when we include code to add remove filters in Spring Security –

<http ...>
    ...        
    <custom-filter ref="mySecurityFilter" after="FORM_LOGIN_FILTER"  />
    ...
</http>

You can also see a list of filters created when you up spring security logging

What about Spring Boot?

Spring Security configuration for Spring Boot is simply a matter of adding a reference to “spring-boot-starter-security” to gradle or maven. We can then fine tune the security configurations through @EnableWebSecurity and overriding the configure method of a class extending WebSecurityConfigurerAdapter.

If we dig around under the hood we find that the DelegatingFilterProxy is still used. With the “springSecurityFilterChain” instantiated in SecurityFilterAutoConfiguration, which populates the DelegatingFilterProxyRegistrationBean with “springSecurityFilterChain” (AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) –

@Bean
@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(SecurityProperties securityProperties) {
    DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(DEFAULT_FILTER_NAME);
    registration.setOrder(securityProperties.getFilterOrder());
    registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
    return registration;
}

Reference

http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#delegating-filter-proxy

https://spring.io/blog/2013/07/03/spring-security-java-config-preview-web-security/

http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#delegating-filter-proxy

Auto-generating Spring Security Tutorial – Custom JDBC Realms

This post builds on the set of spring-security posts I have done, and particularly my last post on Default JDBC Realms. The code is available on github, and spring-security-generator and the instructions to run the application are contained in the previous tutorial.

We also have a requirement to use a custom JDBC realm with the structure –

USER PASSWORD ROLES
customadmin customadmin ROLE_CUSTOM_ADMIN

Custom JDBC Tables

The tables Ive used in this example are renamed versions of the default tables -

create table custom_users (
  username varchar(256),
  password varchar(256),
  enabled boolean
);
create table custom_authorities (
  username varchar(256),
  authority varchar(256)
);
With inserts - 

insert into custom_users (username, password, enabled) values ('customadmin', 'customadmin', true);
insert into custom_authorities (username, authority) values ('customadmin', 'ROLE_CUSTOM_ADMIN');

Spring-Security-Generator

Using spring-security-generator we now select “JDBC Realm (Custom)” and supply the queries –

select username, password, enabled from custom_users where username = ?
select username, authority from custom_authorities where username = ?

The screen configuration is then –

screen-shot-2016-11-07-at-21-00-50

We then generate the code –

package com.glenware.springboot;

import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth)
            throws Exception {
         auth
             .jdbcAuthentication()
                 .dataSource(dataSource)
                   .usersByUsernameQuery(
                   "select username, password, enabled from custom_users where username = ?")
                   .authoritiesByUsernameQuery(
                   "select username, authority from custom_authorities where username = ?");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/webjars/**","/about.html","/rest/**").permitAll()
                .antMatchers("/admin/**").hasAnyRole("CUSTOM_ADMIN")
                .anyRequest().authenticated()
            .and()
                .formLogin()
                    .loginPage("/login")
                    .defaultSuccessUrl("/admin/admin.html")
                    .failureUrl("/login")
                    .permitAll()
             .and()
                .logout()
                    .logoutSuccessUrl("/")
                    .permitAll()
                    ;
    }
    
}

The key difference between this tutorial and the previous is that we supply the SQL directly to the usersByUsernameQuery and authoritiesByUsernameQuery

We can now copy this code to the parkrunpb applicaiton, and login using customadmin/customadmin

Auto-generating Spring Security Tutorial – Default JDBC Realms

The previous tutorial showed how we can auto-generate of spring security using a memory realm. This tutorial expands on this to cover Default JDBC Realms using the source code from the parkrunPB application

Security Requirements

The site has the following links and security requirements –

http://localhost:8080/ Accessible to all
http://localhost:8080/webjars Static Resources – Accessible to all
http://localhost:8080/about.html Static page – Accessible to all
http://localhost:8080/login.html Accessible to all
http://localhost:8080/admin/ Admin User
http://localhost:8080/rest Accessible to all

We also have a requirement to use a users and roles with the structure –

USER PASSWORD ROLES
admin admin ADMIN

Getting Started

The first thing we need to do is uncomment spring security in the maven pom –

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

We can then compile and run the code –

mvn spring-boot:run

The whole application is now locked down

Luckily we can login using the default username (user), and the password from the logs. Im my case –

2016-11-06 21:16:56.877 INFO 8088 --- [main] b.a.s.AuthenticationManagerConfiguration :
 Using default security password: e1c87658-8b7e-4b1e-88da-902b5356ef66

Default JDBC Tables

We can now begin to create our SecurityConfiguration using Spring Security Generator

screen-shot-2016-11-06-at-21-33-53

We then get the generated source code –

package com.glenware.springboot;

import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	private DataSource dataSource;

	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth)
			throws Exception {
         auth
             .jdbcAuthentication()
                 .dataSource(dataSource)
                     .withDefaultSchema()
				.withUser("admin").password("admin").roles("ADMIN");
	}

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/webjars/*","/about.html","/rest/**").permitAll()
                .antMatchers("/admin/**").hasAnyRole("ADMIN")
                .anyRequest().authenticated()
            .and()
                .formLogin()
                    .loginPage("/login")
                    .defaultSuccessUrl("/admin/admin.html")
                    .failureUrl("/login")
                    .permitAll()
             .and()
                .logout()
                    .logoutSuccessUrl("/")
                    .permitAll()
                    ;
	}

}

Key Points

  • Using JDBC Realm(Default) – The default realm means Spring Security will use its default users.ddl
  • Same configuration as before

We can now access the site the same as the memory realm, but with user details stored in the database. The next post will look at using a custom JDBC table