Ocheja fix (#3572)

* Define beans for handling different message types in a lean chat app

* Add class based spring beans configuration

* Define spring configuration in XML for constructor based bean injection

* Refactor package structure to separate constructor based bean injection code set from setter based bean injection code set

* Define configuration and classes specific to setter-based bean injection.

* Implement tests for constructor-based and setter-based bean injections

* develop codes for explaining type erasure

* Write unit tests for type erasure examples

* Remove evaluation article code

* Modify type erasure examples and unit tests

* Modify type erasure examples and unit tests

* Add expected exception in TypeErasureUnitTest

* Correct grammar in class name

* Implement File Manager app to demonstrate Polymorphism. Develop unit tests for Polymorphism article code

* Add examples for static polymorphism

* Change sysout statments to slf4j log info statements

* Add assertions and expected errors check on Test

* Add assertions and expected errors check on Test

* Correct compile time error of symbol not found

* Removed commented out non-compiling test.

* Replace string concatenations with String.format

* Replace string concatenations with String.format

* Remove verbose file info descriptor and replace with simpler one

* Add example codes for Hibernate Interceptors article
Write tests for session-scoped and sessionFactory-scoped interceptors

* Implement serializable on customInterceptorImpl

* Implement examples for spring data with spring security integration

* Remove webapp example implementations; too extensive
This commit is contained in:
Grzegorz Piwowarek 2018-02-03 12:37:28 +01:00 committed by GitHub
parent ff76cbc1fe
commit 710c25fb01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 728 additions and 1 deletions

View File

@ -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 {

View File

@ -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)

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.baeldung</groupId>
<artifactId>spring-data-spring-security</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>intro-spring-data-spring-security</name>
<description>Spring Data with Spring Security</description>
<parent>
<artifactId>parent-boot-5</artifactId>
<groupId>com.baeldung</groupId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../parent-boot-5</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-data</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<!-- <scope>provided</scope> -->
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
</build>
</project>

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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<Tweet, Long> {
@Query("select twt from Tweet twt JOIN twt.likes as lk where lk = ?#{ principal?.username } or twt.owner = ?#{ principal?.username }")
Page<Tweet> getMyTweetsAndTheOnesILiked(Pageable pageable);
}

View File

@ -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, Long> {
AppUser findByUsername(String username);
List<AppUser> 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);
}

View File

@ -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;
}
}

View File

@ -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<String> 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<String> getLikes() {
return likes;
}
public void setLikes(Set<String> likes) {
this.likes = likes;
}
}

View File

@ -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<GrantedAuthority> 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;
}
}

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -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<AppUser> generateDummyUsers() {
List<AppUser> 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<Tweet> generateDummyTweets(List<AppUser> users) {
List<Tweet> 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<GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
GrantedAuthority grantedAuthority = new GrantedAuthority() {
public String getAuthority() {
return "ROLE_USER";
}
};
grantedAuthorities.add(grantedAuthority);
return grantedAuthorities;
}
}

View File

@ -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

View File

@ -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<AppUser> appUsers = (List<AppUser>) 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<Tweet> 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<Tweet> page = null;
do {
page = tweetRepository.getMyTweetsAndTheOnesILiked(new PageRequest(page != null ? page.getNumber() + 1 : 0, 5));
} while (page != null && page.hasNext());
}
}