diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/interceptors/CustomInterceptorImpl.java b/hibernate5/src/main/java/com/baeldung/hibernate/interceptors/CustomInterceptorImpl.java
index a84a981f7f..6736b39b64 100644
--- a/hibernate5/src/main/java/com/baeldung/hibernate/interceptors/CustomInterceptorImpl.java
+++ b/hibernate5/src/main/java/com/baeldung/hibernate/interceptors/CustomInterceptorImpl.java
@@ -9,7 +9,7 @@ import org.hibernate.Interceptor;
import org.hibernate.Transaction;
import org.hibernate.type.Type;
-public class CustomInterceptorImpl implements Interceptor {
+public class CustomInterceptorImpl implements Interceptor, Serializable {
@Override
public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) throws CallbackException {
diff --git a/spring-data-spring-security/README.md b/spring-data-spring-security/README.md
new file mode 100644
index 0000000000..15b4b50870
--- /dev/null
+++ b/spring-data-spring-security/README.md
@@ -0,0 +1,14 @@
+# About this project
+This project contains examples from the [Spring Data with Spring Security](http://www.baeldung.com/spring-data-with-spring-security) article from Baeldung.
+
+# Running the project
+The application uses [Spring Boot](http://projects.spring.io/spring-boot/), so it is easy to run. You can start it any of a few ways:
+* Run the `main` method from `SpringDataRestApplication`
+* Use the Maven Spring Boot plugin: `mvn spring-boot:run`
+* Package the application as a JAR and run it using `java -jar spring-data-spring-security.jar`
+
+# Viewing the running application
+To view the running application, visit [http://localhost:8080](http://localhost:8080) in your browser
+
+###Relevant Articles:
+- [Spring Data with Spring Security](http://www.baeldung.com/spring-data-with-spring-security)
diff --git a/spring-data-spring-security/pom.xml b/spring-data-spring-security/pom.xml
new file mode 100644
index 0000000000..d6b671ee57
--- /dev/null
+++ b/spring-data-spring-security/pom.xml
@@ -0,0 +1,67 @@
+
+
+ 4.0.0
+
+ com.baeldung
+ spring-data-spring-security
+ 1.0
+ jar
+
+ intro-spring-data-spring-security
+ Spring Data with Spring Security
+
+
+ parent-boot-5
+ com.baeldung
+ 0.0.1-SNAPSHOT
+ ../parent-boot-5
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.security
+ spring-security-data
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-jasper
+
+
+
+ com.h2database
+ h2
+
+
+ javax.servlet
+ jstl
+
+
+
+
+ ${project.artifactId}
+
+
+
+
diff --git a/spring-data-spring-security/src/main/java/com/baeldung/AppConfig.java b/spring-data-spring-security/src/main/java/com/baeldung/AppConfig.java
new file mode 100644
index 0000000000..16bbe8b326
--- /dev/null
+++ b/spring-data-spring-security/src/main/java/com/baeldung/AppConfig.java
@@ -0,0 +1,64 @@
+package com.baeldung;
+
+import java.util.Properties;
+
+import javax.sql.DataSource;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.core.env.Environment;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.jdbc.datasource.DriverManagerDataSource;
+import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
+import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+
+@SpringBootApplication
+@PropertySource("classpath:persistence-h2.properties")
+@EnableJpaRepositories(basePackages = { "com.baeldung.data.repositories" })
+@EnableWebMvc
+@Import(SpringSecurityConfig.class)
+public class AppConfig extends WebMvcConfigurerAdapter {
+
+ @Autowired
+ private Environment env;
+
+ @Bean
+ public DataSource dataSource() {
+ final DriverManagerDataSource dataSource = new DriverManagerDataSource();
+ dataSource.setDriverClassName(env.getProperty("driverClassName"));
+ dataSource.setUrl(env.getProperty("url"));
+ dataSource.setUsername(env.getProperty("user"));
+ dataSource.setPassword(env.getProperty("password"));
+ return dataSource;
+ }
+
+ @Bean
+ public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
+ final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
+ em.setDataSource(dataSource());
+ em.setPackagesToScan(new String[] { "com.baeldung.models" });
+ em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
+ em.setJpaProperties(additionalProperties());
+ return em;
+ }
+
+ final Properties additionalProperties() {
+ final Properties hibernateProperties = new Properties();
+ if (env.getProperty("hibernate.hbm2ddl.auto") != null) {
+ hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
+ }
+ if (env.getProperty("hibernate.dialect") != null) {
+ hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
+ }
+ if (env.getProperty("hibernate.show_sql") != null) {
+ hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
+ }
+ return hibernateProperties;
+ }
+
+}
diff --git a/spring-data-spring-security/src/main/java/com/baeldung/SpringSecurityConfig.java b/spring-data-spring-security/src/main/java/com/baeldung/SpringSecurityConfig.java
new file mode 100644
index 0000000000..ee13678a24
--- /dev/null
+++ b/spring-data-spring-security/src/main/java/com/baeldung/SpringSecurityConfig.java
@@ -0,0 +1,89 @@
+package com.baeldung;
+
+import javax.annotation.PostConstruct;
+import javax.sql.DataSource;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
+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.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.data.repository.query.SecurityEvaluationContextExtension;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.baeldung.security.AuthenticationSuccessHandlerImpl;
+import com.baeldung.security.CustomUserDetailsService;
+
+@Configuration
+@EnableWebSecurity
+@ComponentScan("com.baeldung.security")
+public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
+
+ @Autowired
+ private WebApplicationContext applicationContext;
+ private CustomUserDetailsService userDetailsService;
+ @Autowired
+ private AuthenticationSuccessHandlerImpl successHandler;
+ @Autowired
+ private DataSource dataSource;
+
+ @PostConstruct
+ public void completeSetup() {
+ userDetailsService = applicationContext.getBean(CustomUserDetailsService.class);
+ }
+
+ @Override
+ protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
+ auth.userDetailsService(userDetailsService)
+ .passwordEncoder(encoder())
+ .and()
+ .authenticationProvider(authenticationProvider())
+ .jdbcAuthentication()
+ .dataSource(dataSource);
+ }
+
+ @Override
+ public void configure(WebSecurity web) throws Exception {
+ web.ignoring()
+ .antMatchers("/resources/**");
+ }
+
+ @Override
+ protected void configure(final HttpSecurity http) throws Exception {
+ http.authorizeRequests()
+ .antMatchers("/login")
+ .permitAll()
+ .and()
+ .formLogin()
+ .permitAll()
+ .successHandler(successHandler)
+ .and()
+ .csrf()
+ .disable();
+ }
+
+ @Bean
+ public DaoAuthenticationProvider authenticationProvider() {
+ final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
+ authProvider.setUserDetailsService(userDetailsService);
+ authProvider.setPasswordEncoder(encoder());
+ return authProvider;
+ }
+
+ @Bean
+ public PasswordEncoder encoder() {
+ return new BCryptPasswordEncoder(11);
+ }
+
+ @Bean
+ public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
+ return new SecurityEvaluationContextExtension();
+ }
+}
diff --git a/spring-data-spring-security/src/main/java/com/baeldung/data/repositories/TweetRepository.java b/spring-data-spring-security/src/main/java/com/baeldung/data/repositories/TweetRepository.java
new file mode 100644
index 0000000000..7d6446ed0d
--- /dev/null
+++ b/spring-data-spring-security/src/main/java/com/baeldung/data/repositories/TweetRepository.java
@@ -0,0 +1,14 @@
+package com.baeldung.data.repositories;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.PagingAndSortingRepository;
+
+import com.baeldung.models.Tweet;
+
+public interface TweetRepository extends PagingAndSortingRepository {
+
+ @Query("select twt from Tweet twt JOIN twt.likes as lk where lk = ?#{ principal?.username } or twt.owner = ?#{ principal?.username }")
+ Page getMyTweetsAndTheOnesILiked(Pageable pageable);
+}
diff --git a/spring-data-spring-security/src/main/java/com/baeldung/data/repositories/UserRepository.java b/spring-data-spring-security/src/main/java/com/baeldung/data/repositories/UserRepository.java
new file mode 100644
index 0000000000..9f13c3197e
--- /dev/null
+++ b/spring-data-spring-security/src/main/java/com/baeldung/data/repositories/UserRepository.java
@@ -0,0 +1,27 @@
+package com.baeldung.data.repositories;
+
+import java.util.Date;
+import java.util.List;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.baeldung.models.AppUser;
+import com.baeldung.models.Tweet;
+
+public interface UserRepository extends CrudRepository {
+ AppUser findByUsername(String username);
+
+ List findByName(String name);
+
+ @Query("UPDATE AppUser u SET u.lastLogin=:lastLogin WHERE u.username = ?#{ principal?.username }")
+ @Modifying
+ @Transactional
+ public void updateLastLogin(@Param("lastLogin") Date lastLogin);
+}
diff --git a/spring-data-spring-security/src/main/java/com/baeldung/models/AppUser.java b/spring-data-spring-security/src/main/java/com/baeldung/models/AppUser.java
new file mode 100644
index 0000000000..e48233f90a
--- /dev/null
+++ b/spring-data-spring-security/src/main/java/com/baeldung/models/AppUser.java
@@ -0,0 +1,83 @@
+package com.baeldung.models;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "users")
+public class AppUser {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.SEQUENCE)
+ private long id;
+
+ private String name;
+ @Column(unique = true)
+ private String username;
+ private String password;
+ private boolean enabled = true;
+ private Date lastLogin;
+
+ private AppUser() {
+ }
+
+ public AppUser(String name, String email, String password) {
+ this.username = email;
+ this.name = name;
+ this.password = password;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public Date getLastLogin() {
+ return lastLogin;
+ }
+
+ public void setLastLogin(Date lastLogin) {
+ this.lastLogin = lastLogin;
+ }
+}
diff --git a/spring-data-spring-security/src/main/java/com/baeldung/models/Tweet.java b/spring-data-spring-security/src/main/java/com/baeldung/models/Tweet.java
new file mode 100644
index 0000000000..b2e45009f6
--- /dev/null
+++ b/spring-data-spring-security/src/main/java/com/baeldung/models/Tweet.java
@@ -0,0 +1,63 @@
+package com.baeldung.models;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+
+@Entity
+public class Tweet {
+ @Id
+ @GeneratedValue(strategy = GenerationType.SEQUENCE)
+ private long id;
+ private String tweet;
+ private String owner;
+ @ElementCollection(targetClass = String.class, fetch = FetchType.EAGER)
+ private Set likes = new HashSet();
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ private Tweet() {
+ }
+
+ public Tweet(String tweet, String owner) {
+ this.tweet = tweet;
+ this.owner = owner;
+ }
+
+ public String getTweet() {
+ return tweet;
+ }
+
+ public void setTweet(String tweet) {
+ this.tweet = tweet;
+ }
+
+ public String getOwner() {
+ return owner;
+ }
+
+ public void setOwner(String owner) {
+ this.owner = owner;
+ }
+
+ public Set getLikes() {
+ return likes;
+ }
+
+ public void setLikes(Set likes) {
+ this.likes = likes;
+ }
+
+}
diff --git a/spring-data-spring-security/src/main/java/com/baeldung/security/AppUserPrincipal.java b/spring-data-spring-security/src/main/java/com/baeldung/security/AppUserPrincipal.java
new file mode 100644
index 0000000000..195f9f7bf6
--- /dev/null
+++ b/spring-data-spring-security/src/main/java/com/baeldung/security/AppUserPrincipal.java
@@ -0,0 +1,67 @@
+package com.baeldung.security;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import com.baeldung.models.AppUser;
+
+public class AppUserPrincipal implements UserDetails {
+
+ private final AppUser user;
+
+ //
+
+ public AppUserPrincipal(AppUser user) {
+ this.user = user;
+ }
+
+ //
+
+ @Override
+ public String getUsername() {
+ return user.getUsername();
+ }
+
+ @Override
+ public String getPassword() {
+ return user.getPassword();
+ }
+
+ @Override
+ public Collection extends GrantedAuthority> getAuthorities() {
+ final List authorities = Collections.singletonList(new SimpleGrantedAuthority("User"));
+ return authorities;
+ }
+
+ @Override
+ public boolean isAccountNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isAccountNonLocked() {
+ return true;
+ }
+
+ @Override
+ public boolean isCredentialsNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return true;
+ }
+
+ //
+
+ public AppUser getAppUser() {
+ return user;
+ }
+
+}
diff --git a/spring-data-spring-security/src/main/java/com/baeldung/security/AuthenticationSuccessHandlerImpl.java b/spring-data-spring-security/src/main/java/com/baeldung/security/AuthenticationSuccessHandlerImpl.java
new file mode 100644
index 0000000000..3fc2bc6559
--- /dev/null
+++ b/spring-data-spring-security/src/main/java/com/baeldung/security/AuthenticationSuccessHandlerImpl.java
@@ -0,0 +1,28 @@
+package com.baeldung.security;
+
+import java.io.IOException;
+import java.util.Date;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+import org.springframework.stereotype.Component;
+
+import com.baeldung.data.repositories.UserRepository;
+
+@Component
+public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
+
+ @Autowired
+ private UserRepository userRepository;
+
+ @Override
+ public void onAuthenticationSuccess(HttpServletRequest arg0, HttpServletResponse arg1, Authentication arg2) throws IOException, ServletException {
+ userRepository.updateLastLogin(new Date());
+ }
+
+}
diff --git a/spring-data-spring-security/src/main/java/com/baeldung/security/CustomUserDetailsService.java b/spring-data-spring-security/src/main/java/com/baeldung/security/CustomUserDetailsService.java
new file mode 100644
index 0000000000..016f4f7fa9
--- /dev/null
+++ b/spring-data-spring-security/src/main/java/com/baeldung/security/CustomUserDetailsService.java
@@ -0,0 +1,40 @@
+package com.baeldung.security;
+
+import javax.annotation.PostConstruct;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.baeldung.data.repositories.UserRepository;
+import com.baeldung.models.AppUser;
+
+@Service
+public class CustomUserDetailsService implements UserDetailsService {
+
+ @Autowired
+ private WebApplicationContext applicationContext;
+ private UserRepository userRepository;
+
+ public CustomUserDetailsService() {
+ super();
+ }
+
+ @PostConstruct
+ public void completeSetup() {
+ userRepository = applicationContext.getBean(UserRepository.class);
+ }
+
+ @Override
+ public UserDetails loadUserByUsername(final String username) {
+ final AppUser appUser = userRepository.findByUsername(username);
+ if (appUser == null) {
+ throw new UsernameNotFoundException(username);
+ }
+ return new AppUserPrincipal(appUser);
+ }
+
+}
diff --git a/spring-data-spring-security/src/main/java/com/baeldung/util/DummyContentUtil.java b/spring-data-spring-security/src/main/java/com/baeldung/util/DummyContentUtil.java
new file mode 100644
index 0000000000..f1640264d2
--- /dev/null
+++ b/spring-data-spring-security/src/main/java/com/baeldung/util/DummyContentUtil.java
@@ -0,0 +1,63 @@
+package com.baeldung.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Random;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+import com.baeldung.models.AppUser;
+import com.baeldung.models.Tweet;
+
+public class DummyContentUtil {
+
+ public static final List generateDummyUsers() {
+ List appUsers = new ArrayList<>();
+ BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+ appUsers.add(new AppUser("Lionel Messi", "lionel@messi.com", passwordEncoder.encode("li1234")));
+ appUsers.add(new AppUser("Cristiano Ronaldo", "cristiano@ronaldo.com", passwordEncoder.encode("c1234")));
+ appUsers.add(new AppUser("Neymar Dos Santos", "neymar@neymar.com", passwordEncoder.encode("n1234")));
+ appUsers.add(new AppUser("Luiz Suarez", "luiz@suarez.com", passwordEncoder.encode("lu1234")));
+ appUsers.add(new AppUser("Andres Iniesta", "andres@iniesta.com", passwordEncoder.encode("a1234")));
+ appUsers.add(new AppUser("Ivan Rakitic", "ivan@rakitic.com", passwordEncoder.encode("i1234")));
+ appUsers.add(new AppUser("Ousman Dembele", "ousman@dembele.com", passwordEncoder.encode("o1234")));
+ appUsers.add(new AppUser("Sergio Busquet", "sergio@busquet.com", passwordEncoder.encode("s1234")));
+ appUsers.add(new AppUser("Gerard Pique", "gerard@pique.com", passwordEncoder.encode("g1234")));
+ appUsers.add(new AppUser("Ter Stergen", "ter@stergen.com", passwordEncoder.encode("t1234")));
+ return appUsers;
+ }
+
+ public static final List generateDummyTweets(List users) {
+ List tweets = new ArrayList<>();
+ Random random = new Random();
+ IntStream.range(0, 9)
+ .sequential()
+ .forEach(i -> {
+ Tweet twt = new Tweet(String.format("Tweet %d", i), users.get(random.nextInt(users.size()))
+ .getUsername());
+ twt.getLikes()
+ .addAll(users.subList(0, random.nextInt(users.size()))
+ .stream()
+ .map(AppUser::getUsername)
+ .collect(Collectors.toSet()));
+ tweets.add(twt);
+ });
+ return tweets;
+ }
+
+ public static Collection getAuthorities() {
+ Collection grantedAuthorities = new ArrayList();
+ GrantedAuthority grantedAuthority = new GrantedAuthority() {
+ public String getAuthority() {
+ return "ROLE_USER";
+ }
+ };
+ grantedAuthorities.add(grantedAuthority);
+ return grantedAuthorities;
+ }
+
+}
diff --git a/spring-data-spring-security/src/main/resources/application.properties b/spring-data-spring-security/src/main/resources/application.properties
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/spring-data-spring-security/src/main/resources/persistence-h2.properties b/spring-data-spring-security/src/main/resources/persistence-h2.properties
new file mode 100644
index 0000000000..a4b2af6361
--- /dev/null
+++ b/spring-data-spring-security/src/main/resources/persistence-h2.properties
@@ -0,0 +1,8 @@
+driverClassName=org.h2.Driver
+url=jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1
+username=sa
+password=
+
+hibernate.dialect=org.hibernate.dialect.H2Dialect
+hibernate.show_sql=false
+hibernate.hbm2ddl.auto=create-drop
\ No newline at end of file
diff --git a/spring-data-spring-security/src/test/java/com/baeldung/relationships/SpringDataWithSecurityTest.java b/spring-data-spring-security/src/test/java/com/baeldung/relationships/SpringDataWithSecurityTest.java
new file mode 100644
index 0000000000..dbbfe7e85e
--- /dev/null
+++ b/spring-data-spring-security/src/test/java/com/baeldung/relationships/SpringDataWithSecurityTest.java
@@ -0,0 +1,100 @@
+package com.baeldung.relationships;
+
+import static org.springframework.util.Assert.isTrue;
+
+import java.util.Date;
+import java.util.List;
+
+import javax.servlet.ServletContext;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.InvalidDataAccessApiUsageException;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
+
+import com.baeldung.AppConfig;
+import com.baeldung.data.repositories.TweetRepository;
+import com.baeldung.data.repositories.UserRepository;
+import com.baeldung.models.AppUser;
+import com.baeldung.models.Tweet;
+import com.baeldung.security.AppUserPrincipal;
+import com.baeldung.util.DummyContentUtil;
+
+@RunWith(SpringRunner.class)
+@WebAppConfiguration
+@ContextConfiguration
+@DirtiesContext
+public class SpringDataWithSecurityTest {
+ AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
+ @Autowired
+ private ServletContext servletContext;
+ private static UserRepository userRepository;
+ private static TweetRepository tweetRepository;
+
+ @Before
+ public void testInit() {
+ ctx.register(AppConfig.class);
+ ctx.setServletContext(servletContext);
+ ctx.refresh();
+ userRepository = ctx.getBean(UserRepository.class);
+ tweetRepository = ctx.getBean(TweetRepository.class);
+ List appUsers = (List) userRepository.save(DummyContentUtil.generateDummyUsers());
+ tweetRepository.save(DummyContentUtil.generateDummyTweets(appUsers));
+ }
+
+ @AfterClass
+ public static void tearDown() {
+ tweetRepository.deleteAll();
+ userRepository.deleteAll();
+ }
+
+ @Test
+ public void givenAppUser_whenLoginSuccessful_shouldUpdateLastLogin() {
+ AppUser appUser = userRepository.findByUsername("lionel@messi.com");
+ Authentication auth = new UsernamePasswordAuthenticationToken(new AppUserPrincipal(appUser), null, DummyContentUtil.getAuthorities());
+ SecurityContextHolder.getContext()
+ .setAuthentication(auth);
+ userRepository.updateLastLogin(new Date());
+ }
+
+ @Test(expected = InvalidDataAccessApiUsageException.class)
+ public void givenNoAppUserInSecurityContext_whenUpdateLastLoginAttempted_shouldFail() {
+ userRepository.updateLastLogin(new Date());
+ }
+
+ @Test
+ public void givenAppUser_whenLoginSuccessful_shouldReadMyPagedTweets() {
+ AppUser appUser = userRepository.findByUsername("lionel@messi.com");
+ Authentication auth = new UsernamePasswordAuthenticationToken(new AppUserPrincipal(appUser), null, DummyContentUtil.getAuthorities());
+ SecurityContextHolder.getContext()
+ .setAuthentication(auth);
+ Page page = null;
+ do {
+ page = tweetRepository.getMyTweetsAndTheOnesILiked(new PageRequest(page != null ? page.getNumber() + 1 : 0, 5));
+ for (Tweet twt : page.getContent()) {
+ isTrue((twt.getOwner() == appUser.getUsername()) || (twt.getLikes()
+ .contains(appUser.getUsername())), "I do not have any Tweets");
+ }
+ } while (page.hasNext());
+ }
+
+ @Test(expected = InvalidDataAccessApiUsageException.class)
+ public void givenNoAppUser_whenPaginatedResultsRetrievalAttempted_shouldFail() {
+ Page page = null;
+ do {
+ page = tweetRepository.getMyTweetsAndTheOnesILiked(new PageRequest(page != null ? page.getNumber() + 1 : 0, 5));
+ } while (page != null && page.hasNext());
+ }
+}