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:
committed by
Oliver Gierke
parent
dce2994f1f
commit
67ab7716dc
@@ -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
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<module>interceptors</module>
|
||||
<module>java8</module>
|
||||
<module>jpa21</module>
|
||||
<module>security</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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> {
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()) {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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>{
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,4 +15,4 @@
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
</configuration>
|
||||
|
||||
Reference in New Issue
Block a user