Added example for SpringSecurity integration.

Fixed some Java 6 incompatibilities.
Added mention for the security module in readme.

Original pull request: #1.
This commit is contained in:
Thomas Darimont
2014-07-10 12:51:59 +02:00
committed by Oliver Gierke
parent dce2994f1f
commit 67ab7716dc
12 changed files with 249 additions and 39 deletions

View File

@@ -10,6 +10,7 @@ We have separate folders for the samples of individual modules:
* `java8` - Example of how to use Spring Data JPA auditing with Java 8 date time types as well as the usage of `Optional` as return type for repository methods. Note, this project requires to be build with JDK 8.
* `showcase` - Refactoring show case of how to improve a plain-JPA-based persistence layer by using Spring Data JPA (read: removing close to all of the implementation code). Follow the `demo.txt` file for detailed instructions.
* `interceptors` - Example of how to enrich the repositories with AOP.
* `security` - Example of how to integrate Spring Data JPA Repositories with Spring Security.
## Spring Data MongoDB

View File

@@ -22,6 +22,7 @@
<module>interceptors</module>
<module>java8</module>
<module>jpa21</module>
<module>security</module>
</modules>
<properties>

View File

@@ -11,23 +11,9 @@
<artifactId>spring-data-jpa-security</artifactId>
<name>Spring Data JPA - Spring Security integration</name>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.7.0.DATAJPA-564-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>1.9.0.DATACMNS-533-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
<properties>
<spring-data-releasetrain.version>Evans-BUILD-SNAPSHOT</spring-data-releasetrain.version>
</properties>
<dependencies>
<dependency>
@@ -36,4 +22,4 @@
</dependency>
</dependencies>
</project>
</project>

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.jpa.security;
import javax.persistence.*;
/**
* @author Thomas Darimont
*/
@Entity
public class BusinessObject {
@Id @GeneratedValue Long id;
String data;
@ManyToOne
User owner;
public BusinessObject(String data, User owner) {
this.data = data;
this.owner = owner;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BusinessObject that = (BusinessObject) o;
if (data != null ? !data.equals(that.data) : that.data != null) return false;
if (id != null ? !id.equals(that.id) : that.id != null) return false;
if (owner != null ? !owner.equals(that.owner) : that.owner != null) return false;
return true;
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (data != null ? data.hashCode() : 0);
result = 31 * result + (owner != null ? owner.hashCode() : 0);
return result;
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.jpa.security;
import org.springframework.data.repository.CrudRepository;
/**
* Repository to manage {@link BusinessObject} instances.
*
* @author Thomas Darimont
*/
public interface BusinessObjectRepository extends CrudRepository<BusinessObject, Long> {
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.jpa.security;
import java.util.List;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
/**
* @author Thomas Darimont
*/
public interface SecureBusinessObjectRepository extends Repository<BusinessObject,Long>{
/**
* Here we demonstrate the usage of SpEL expression within a custom query.
* With the {@link example.springdata.jpa.security.SecurityEvaluationContextExtension} in place
* we can safely access auth information provided by the Spring Security Context.
*
* The Spring Data Repository infrastructure will translate the given query string into the
* parameterized form:
*
* <code>
* select o from BusinessObject o where o.owner.emailAddress like ?
* </code>
*
* and set the the result SpEL expression evaluated at method invocation time as parameter value.
*
* @return
*/
@Query("select o from BusinessObject o where o.owner.emailAddress like ?#{hasRole('ROLE_ADMIN') ? '%' : principal.emailAddress}")
List<BusinessObject> findBusinessObjectsForCurrentUser();
}

View File

@@ -18,14 +18,18 @@ package example.springdata.jpa.security;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.repository.query.spi.EvaluationContextExtensionSupport;
import org.springframework.data.repository.query.spi.EvaluationContextExtension;
/**
* @author Oliver Gierke
* @author Thomas Darimont
*/
@Configuration
@EnableAutoConfiguration
class SecurityConfiguration {
@Bean
EvaluationContextExtensionSupport securityExtension() {
EvaluationContextExtension securityExtension() {
return new SecurityEvaluationContextExtension();
}
}

View File

@@ -15,15 +15,16 @@
*/
package example.springdata.jpa.security;
import java.util.Collections;
import java.util.Map;
import org.springframework.data.repository.query.spi.EvaluationContextExtension;
import org.springframework.data.repository.query.spi.EvaluationContextExtensionSupport;
import org.springframework.security.access.expression.SecurityExpressionRoot;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* {@link EvaluationContextExtension} to expose Spring Security's principal via a SpEL property.
*
* Note that this is just for the sake of demonstration - Spring Security will eventually provide its
* own EvaluationContext extension.
*
* @author Oliver Gierke
*/
@@ -38,12 +39,12 @@ class SecurityEvaluationContextExtension extends EvaluationContextExtensionSuppo
return "security";
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.spi.EvaluationContextExtensionSupport#getProperties()
* @see org.springframework.data.repository.query.spi.EvaluationContextExtension#getRootObject()
*/
@Override
public Map<String, Object> getProperties() {
return Collections.singletonMap("principal", SecurityContextHolder.getContext().getAuthentication().getPrincipal());
public SecurityExpressionRoot getRootObject() {
return new SecurityExpressionRoot(SecurityContextHolder.getContext().getAuthentication()) {};
}
}

View File

@@ -20,17 +20,36 @@ import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* @author Oliver Gierke
* @author Thomas Darimont
*/
@Entity
public class Customer {
public class User {
@Id @GeneratedValue Long id;
String firstname, lastname;
public Customer(String firstname, String lastname) {
String firstname;
String lastname;
String emailAddress;
public User(String firstname, String lastname, String emailAddress) {
this.firstname = firstname;
this.lastname = lastname;
this.emailAddress = emailAddress;
}
public Long getId() {
return id;
}
public String getFirstname() {
return firstname;
}
public String getLastname() {
return lastname;
}
public String getEmailAddress() {
return emailAddress;
}
}

View File

@@ -18,10 +18,7 @@ package example.springdata.jpa.security;
import org.springframework.data.repository.CrudRepository;
/**
* Repository to manage {@link Customer} instances.
*
* @author Oliver Gierke
* @author Thomas Darimont
*/
public interface CustomerRepository extends CrudRepository<Customer, Long> {
public interface UserRepository extends CrudRepository<User,Long>{
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors.
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,19 +15,85 @@
*/
package example.springdata.jpa.security;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.junit.Assert.assertThat;
import java.util.Collections;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
/**
* Integration test to show the usage of Java 8 date time APIs with Spring Data JPA auditing.
* Integration test to show the usage of Spring Security constructs within Repository query methods.
*
* @author Oliver Gierke
* @author Thomas Darimont
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SecurityConfiguration.class)
@Transactional
public class SecurityIntegrationTests {
@Autowired UserRepository userRepository;
@Autowired BusinessObjectRepository businessObjectRepository;
@Autowired SecureBusinessObjectRepository secureBusinessObjectRepository;
User tom;
User olli;
User admin;
BusinessObject object1;
BusinessObject object2;
BusinessObject object3;
@Before
public void setup(){
tom = userRepository.save(new User("thomas","darimont","tdarimont@example.org"));
olli = userRepository.save(new User("oliver","gierke","ogierke@example.org"));
admin = userRepository.save(new User("admin","admin","admin@example.org"));
object1 = businessObjectRepository.save(new BusinessObject("object1", olli));
object2 = businessObjectRepository.save(new BusinessObject("object2", olli));
object3 = businessObjectRepository.save(new BusinessObject("object3", tom));
}
@Test
public void findBusinessObjectsForCurrentUserShouldReturnOnlyBusinessObjectsWhereCurrentUserIsOwner(){
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(tom,"x"));
List<BusinessObject> businessObjects = secureBusinessObjectRepository.findBusinessObjectsForCurrentUser();
assertThat(businessObjects,hasSize(1));
assertThat(businessObjects,contains(object3));
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(olli,"x"));
businessObjects = secureBusinessObjectRepository.findBusinessObjectsForCurrentUser();
assertThat(businessObjects,hasSize(2));
assertThat(businessObjects,contains(object1,object2));
}
@Test
public void findBusinessObjectsForCurrentUserShouldReturnAllObjectsForAdmin(){
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(admin,"x", Collections.singleton(new SimpleGrantedAuthority("ROLE_ADMIN"))));
List<BusinessObject> businessObjects = secureBusinessObjectRepository.findBusinessObjectsForCurrentUser();
assertThat(businessObjects,hasSize(3));
assertThat(businessObjects,contains(object1,object2, object3));
}
}

View File

@@ -15,4 +15,4 @@
<appender-ref ref="console" />
</root>
</configuration>
</configuration>