#21 - Polished Spring Data REST + Spring Security example.

Removed obsolete dependency declarations from pom.xml. Rewrote test cases to use Spring MVC test support instead of a running server and RestTemplate. Fixed Security configuration to allow bootstrap in Spring MVC test context. Formatting, JavaDoc.

Original pull request: #22.
This commit is contained in:
Oliver Gierke
2014-10-17 10:14:57 +02:00
parent c5920a64d9
commit 1d7554b57a
10 changed files with 161 additions and 212 deletions

View File

@@ -60,5 +60,4 @@ public class Application {
SecurityContextHolder.clearContext();
}
}

View File

@@ -19,29 +19,28 @@ import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* Domain object for an employee.
*
* @author Greg Turnquist
*/
@Entity
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
@Entity
@RequiredArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class Employee {
@Id @GeneratedValue private Long id;
private @Id @GeneratedValue Long id;
private final String firstName, lastName, title;
Employee() {
this.firstName = null;
this.lastName = null;
this.title = null;
}
}

View File

@@ -23,6 +23,4 @@ import org.springframework.data.repository.CrudRepository;
*
* @author Greg Turnquist
*/
public interface EmployeeRepository extends CrudRepository<Employee, Long> {
}
public interface EmployeeRepository extends CrudRepository<Employee, Long> {}

View File

@@ -26,18 +26,17 @@ import lombok.RequiredArgsConstructor;
* Domain object for an item managed by the company.
*
* @author Greg Turnquist
* @author Oliver Gierke
*/
@Entity
@Data
@RequiredArgsConstructor
public class Item {
@Id @GeneratedValue private Long id;
private @Id @GeneratedValue Long id;
private final String description;
Item() {
this.description = null;
}
}

View File

@@ -23,15 +23,24 @@ import org.springframework.security.access.prepost.PreAuthorize;
* operations require ROLE_ADMIN.
*
* @author Greg Turnquist
* @author Oliver Gierke
*/
@PreAuthorize("hasRole('ROLE_USER')")
public interface ItemRepository extends CrudRepository<Item, Long> {
@PreAuthorize("hasRole('ROLE_ADMIN')")
/*
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#save(S)
*/
@Override
Item save(Item s);
@PreAuthorize("hasRole('ROLE_ADMIN')")
<S extends Item> S save(S s);
@PreAuthorize("hasRole('ROLE_ADMIN')")
/*
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#delete(java.io.Serializable)
*/
@Override
@PreAuthorize("hasRole('ROLE_ADMIN')")
void delete(Long aLong);
}

View File

@@ -15,7 +15,6 @@
*/
package example.company;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
@@ -26,10 +25,10 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
/**
* This application is secured at both the URL level for some parts, and the method level for other parts. The URL
* security is shown inside this code, while method-level annotations are enabled at by
* {@link org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity
* EnableGlobalMethodSecurity}
* {@link EnableGlobalMethodSecurity}.
*
* @author Greg Turnquist
* @author Oliver Gierke
*/
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@@ -37,35 +36,38 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
/**
* This section defines the user accounts which can be used for authentication as well as the roles each user has.
*
* @param auth
* @throws Exception
*
* @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder)
*/
@Autowired
public void configureAuth(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("greg").password("turnquist").roles("USER").and()
.withUser("ollie").password("gierke").roles("USER", "ADMIN");
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().//
withUser("greg").password("turnquist").roles("USER").and().//
withUser("ollie").password("gierke").roles("USER", "ADMIN");
}
/**
* This section defines the security policy for the app. - BASIC authentication is supported (enough for this
* REST-based demo) - /employees is secured using URL security shown below - CSRF headers are disabled since we are
* only testing the REST interface, not a web one. NOTE: GET is not shown which defaults to permitted.
* This section defines the security policy for the app.
* <p>
* <ul>
* <li>BASIC authentication is supported (enough for this REST-based demo).</li>
* <li>/employees is secured using URL security shown below.</li>
* <li>CSRF headers are disabled since we are only testing the REST interface, not a web one.</li>
* </ul>
* NOTE: GET is not shown which defaults to permitted.
*
* @param http
* @throws Exception
* @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(org.springframework.security.config.annotation.web.builders.HttpSecurity)
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/employees").hasRole("ADMIN")
.antMatchers(HttpMethod.PUT, "/employees/**").hasRole("ADMIN")
.antMatchers(HttpMethod.PATCH, "/employees/**").hasRole("ADMIN")
.and()
.csrf().disable();
http.httpBasic().and().authorizeRequests().//
antMatchers(HttpMethod.POST, "/employees").hasRole("ADMIN").//
antMatchers(HttpMethod.PUT, "/employees/**").hasRole("ADMIN").//
antMatchers(HttpMethod.PATCH, "/employees/**").hasRole("ADMIN").and().//
csrf().disable();
}
}

View File

@@ -17,18 +17,32 @@ package example.company;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
/**
* Some convenient security utilities.
*
* @author Greg Turnquist
* @author Oliver Gierke
*/
public class SecurityUtils {
class SecurityUtils {
/**
* Configures the Spring Security {@link SecurityContext} to be authenticated as the user with the given username and
* password as well as the given granted authorities.
*
* @param username must not be {@literal null} or empty.
* @param password must not be {@literal null} or empty.
* @param roles
*/
public static void runAs(String username, String password, String... roles) {
Assert.notNull(username, "Username must not be null!");
Assert.notNull(password, "Password must not be null!");
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(username, password, AuthorityUtils.createAuthorityList(roles)));
new UsernamePasswordAuthenticationToken(username, password, AuthorityUtils.createAuthorityList(roles)));
}
}