diff --git a/README.md b/README.md index 05e5cc88..c607af67 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,8 @@ # Spring Boot Web Application -This repository has the project files for a tutorial series on Spring Boot available from by website at [Spring Framework Guru](https://springframework.guru) \ No newline at end of file + +##Part 6 +This repository has the project files for the post SPRING BOOT WEB APPLICATION, PART 6 – SPRING SECURITY WITH DAO Authentication Provider +that is part of the tutorial series on Spring Boot available from by website at [Spring Framework Guru](https://springfrspringframework.guru) + +## Checkout the full tutorial here! +[Spring Boot - making Spring Fun again!](https://springframework.guru/spring-boot-web-application-part-1-spring-initializr/) \ No newline at end of file diff --git a/pom.xml b/pom.xml index 575dff6a..cc4cce81 100644 --- a/pom.xml +++ b/pom.xml @@ -14,13 +14,13 @@ org.springframework.boot spring-boot-starter-parent - 1.2.4.RELEASE + 2.2.2.RELEASE UTF-8 - 1.8 + 11 @@ -36,21 +36,51 @@ org.springframework.boot spring-boot-starter-thymeleaf + + org.thymeleaf.extras + thymeleaf-extras-springsecurity4 + 2.1.2.RELEASE + org.springframework.boot spring-boot-starter-web - + + + + + org.webjars + jquery + 2.1.4 + + + org.webjars + bootstrap + 3.3.4 + com.h2database h2 - runtime org.springframework.boot spring-boot-starter-test test + + + + org.jasypt + jasypt + 1.9.2 + + + + org.jasypt + jasypt-springsecurity3 + 1.9.2 + + diff --git a/src/main/java/guru/springframework/bootstrap/SpringJpaBootstrap.java b/src/main/java/guru/springframework/bootstrap/SpringJpaBootstrap.java new file mode 100644 index 00000000..066efb7b --- /dev/null +++ b/src/main/java/guru/springframework/bootstrap/SpringJpaBootstrap.java @@ -0,0 +1,128 @@ +package guru.springframework.bootstrap; + +import guru.springframework.domain.Product; +import guru.springframework.domain.Role; +import guru.springframework.domain.User; +import guru.springframework.repositories.ProductRepository; +import guru.springframework.services.RoleService; +import guru.springframework.services.UserService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.List; + +@Component +public class SpringJpaBootstrap implements ApplicationListener { + + private ProductRepository productRepository; + private UserService userService; + private RoleService roleService; + + private Logger log = LoggerFactory.getLogger(SpringJpaBootstrap.class); + + @Autowired + public void setProductRepository(ProductRepository productRepository) { + this.productRepository = productRepository; + } + + @Autowired + public void setUserService(UserService userService) { + this.userService = userService; + } + + @Autowired + public void setRoleService(RoleService roleService) { + this.roleService = roleService; + } + + + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + loadProducts(); + loadUsers(); + loadRoles(); + assignUsersToUserRole(); + assignUsersToAdminRole(); + } + + private void loadProducts() { + Product shirt = new Product(); + shirt.setDescription("Spring Framework Guru Shirt"); + shirt.setPrice(new BigDecimal("18.95")); + shirt.setImageUrl("https://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_shirt-rf412049699c14ba5b68bb1c09182bfa2_8nax2_512.jpg"); + shirt.setProductId("235268845711068308"); + productRepository.save(shirt); + + log.info("Saved Shirt - id: " + shirt.getId()); + + Product mug = new Product(); + mug.setDescription("Spring Framework Guru Mug"); + mug.setImageUrl("https://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_coffee_mug-r11e7694903c348e1a667dfd2f1474d95_x7j54_8byvr_512.jpg"); + mug.setProductId("168639393495335947"); + mug.setPrice(new BigDecimal("11.95")); + productRepository.save(mug); + + log.info("Saved Mug - id:" + mug.getId()); + } + + private void loadUsers() { + User user1 = new User(); + user1.setUsername("user"); + user1.setPassword("user"); + userService.saveOrUpdate(user1); + + User user2 = new User(); + user2.setUsername("admin"); + user2.setPassword("admin"); + userService.saveOrUpdate(user2); + + } + + private void loadRoles() { + Role role = new Role(); + role.setRole("USER"); + roleService.saveOrUpdate(role); + log.info("Saved role" + role.getRole()); + Role adminRole = new Role(); + adminRole.setRole("ADMIN"); + roleService.saveOrUpdate(adminRole); + log.info("Saved role" + adminRole.getRole()); + } + private void assignUsersToUserRole() { + List roles = (List) roleService.listAll(); + List users = (List) userService.listAll(); + + roles.forEach(role -> { + if (role.getRole().equalsIgnoreCase("USER")) { + users.forEach(user -> { + if (user.getUsername().equals("user")) { + user.addRole(role); + userService.saveOrUpdate(user); + } + }); + } + }); + } + private void assignUsersToAdminRole() { + List roles = (List) roleService.listAll(); + List users = (List) userService.listAll(); + + roles.forEach(role -> { + if (role.getRole().equalsIgnoreCase("ADMIN")) { + users.forEach(user -> { + if (user.getUsername().equals("admin")) { + user.addRole(role); + userService.saveOrUpdate(user); + } + }); + } + }); + } +} + + diff --git a/src/main/java/guru/springframework/config/SpringSecConfig.java b/src/main/java/guru/springframework/config/SpringSecConfig.java new file mode 100644 index 00000000..b8016b12 --- /dev/null +++ b/src/main/java/guru/springframework/config/SpringSecConfig.java @@ -0,0 +1,62 @@ +package guru.springframework.config; + +import org.jasypt.util.password.StrongPasswordEncryptor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationProvider; +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.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +public class SpringSecConfig extends WebSecurityConfigurerAdapter { + + private AuthenticationProvider authenticationProvider; + + @Autowired + @Qualifier("daoAuthenticationProvider") + public void setAuthenticationProvider(AuthenticationProvider authenticationProvider) { + this.authenticationProvider = authenticationProvider; + } + + @Bean + public PasswordEncoder passwordEncoder(){ + PasswordEncoder passwordEncoder =new BCryptPasswordEncoder(); + return passwordEncoder; + } +@Bean("daoAuthenticationProvider") +public AuthenticationProvider daoAuthenticationProvider(UserDetailsService userDetailsService) { + DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setPasswordEncoder(new BCryptPasswordEncoder()); + provider.setUserDetailsService(userDetailsService); + return provider; +} + + + @Autowired + public void configureAuthManager(AuthenticationManagerBuilder authenticationManagerBuilder){ + authenticationManagerBuilder.authenticationProvider(authenticationProvider); + } + @Override + protected void configure(HttpSecurity httpSecurity) throws Exception { + httpSecurity + .authorizeRequests().antMatchers("/","/products","/product/show/*","/console/*","/h2-console/**").permitAll() + .anyRequest().authenticated() + .and() + .formLogin().loginPage("/login").permitAll() + .and() + .logout().permitAll(); + + httpSecurity.csrf().disable(); + httpSecurity.headers().frameOptions().disable(); + } + + +} \ No newline at end of file diff --git a/src/main/java/guru/springframework/config/WebConfig.java b/src/main/java/guru/springframework/config/WebConfig.java new file mode 100644 index 00000000..01425a1c --- /dev/null +++ b/src/main/java/guru/springframework/config/WebConfig.java @@ -0,0 +1,15 @@ +package guru.springframework.config; +import org.h2.server.web.WebServlet; +import org.springframework.boot.web.servlet.ServletRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class WebConfig { + @Bean + ServletRegistrationBean h2servletRegistration(){ + ServletRegistrationBean registrationBean = new ServletRegistrationBean( new WebServlet()); + registrationBean.addUrlMappings("/console/*"); + return registrationBean; + } +} diff --git a/src/main/java/guru/springframework/controllers/IndexController.java b/src/main/java/guru/springframework/controllers/IndexController.java new file mode 100644 index 00000000..95280b66 --- /dev/null +++ b/src/main/java/guru/springframework/controllers/IndexController.java @@ -0,0 +1,12 @@ +package guru.springframework.controllers; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +public class IndexController { + @RequestMapping("/") + String index(){ + return "index"; + } +} diff --git a/src/main/java/guru/springframework/controllers/ProductController.java b/src/main/java/guru/springframework/controllers/ProductController.java new file mode 100644 index 00000000..d6e9f1a9 --- /dev/null +++ b/src/main/java/guru/springframework/controllers/ProductController.java @@ -0,0 +1,63 @@ +package guru.springframework.controllers; + +import guru.springframework.domain.Product; +import guru.springframework.services.ProductService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@Controller +public class ProductController { + + private ProductService productService; + + @Autowired + public void setProductService(ProductService productService) { + this.productService = productService; + } + + @RequestMapping(value = "/products", method = RequestMethod.GET) + public String list(Model model){ + model.addAttribute("products", productService.listAllProducts()); + return "products"; + } + + @RequestMapping("product/show/{id}") + public String showProduct(@PathVariable Integer id, Model model){ + model.addAttribute("product", productService.getProductById(id)); + return "productshow"; + } + + @RequestMapping("product/edit/{id}") + public String edit(@PathVariable Integer id, Model model){ + model.addAttribute("product", productService.getProductById(id)); + return "productform"; + } + + @RequestMapping("product/new") + public String newProduct(Model model){ + model.addAttribute("product", new Product()); + return "productform"; + } + + @RequestMapping(value = "product", method = RequestMethod.POST) + public String saveProduct(Product product){ + productService.saveProduct(product); + return "redirect:/product/show/" + product.getId(); + } + + @RequestMapping("product/delete/{id}") + public String delete(@PathVariable Integer id){ + productService.deleteProduct(id); + return "redirect:/products"; + } + + @RequestMapping(value = "/login", method = RequestMethod.GET) + public String login(){ + return "login"; + } + +} diff --git a/src/main/java/guru/springframework/converters/UserToUserDetails.java b/src/main/java/guru/springframework/converters/UserToUserDetails.java new file mode 100644 index 00000000..2bfa6c90 --- /dev/null +++ b/src/main/java/guru/springframework/converters/UserToUserDetails.java @@ -0,0 +1,31 @@ +package guru.springframework.converters; + +import guru.springframework.domain.User; +import guru.springframework.services.security.UserDetailsImpl; +import org.springframework.core.convert.converter.Converter; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; +import java.util.ArrayList; +import java.util.Collection; + +@Component +public class UserToUserDetails implements Converter { + @Override + public UserDetails convert(User user) { + UserDetailsImpl userDetails = new UserDetailsImpl(); + + if (user != null) { + userDetails.setUsername(user.getUsername()); + userDetails.setPassword(user.getEncryptedPassword()); + userDetails.setEnabled(user.getEnabled()); + Collection authorities = new ArrayList<>(); + user.getRoles().forEach(role -> { + authorities.add(new SimpleGrantedAuthority(role.getRole())); + }); + userDetails.setAuthorities(authorities); + } + + return userDetails; + } +} diff --git a/src/main/java/guru/springframework/domain/AbstractDomainClass.java b/src/main/java/guru/springframework/domain/AbstractDomainClass.java new file mode 100644 index 00000000..2497cb1d --- /dev/null +++ b/src/main/java/guru/springframework/domain/AbstractDomainClass.java @@ -0,0 +1,56 @@ +package guru.springframework.domain; + +import javax.persistence.*; +import java.util.Date; + +/** + * Created by jt on 12/16/15. + */ +@MappedSuperclass +public class AbstractDomainClass implements DomainObject { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + Integer id; + + @Version + private Integer version; + + private Date dateCreated; + private Date lastUpdated; + + @Override + public Integer getId() { + return this.id; + } + + @Override + public void setId(Integer id) { + this.id = id; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public Date getDateCreated() { + return dateCreated; + } + + public Date getLastUpdated() { + return lastUpdated; + } + + @PreUpdate + @PrePersist + public void updateTimeStamps() { + lastUpdated = new Date(); + if (dateCreated==null) { + dateCreated = new Date(); + } + } +} diff --git a/src/main/java/guru/springframework/domain/DomainObject.java b/src/main/java/guru/springframework/domain/DomainObject.java new file mode 100644 index 00000000..5744c4e7 --- /dev/null +++ b/src/main/java/guru/springframework/domain/DomainObject.java @@ -0,0 +1,11 @@ +package guru.springframework.domain; + +/** + * Created by jt on 11/14/15. + */ +public interface DomainObject { + + Integer getId(); + + void setId(Integer id); +} diff --git a/src/main/java/guru/springframework/domain/Product.java b/src/main/java/guru/springframework/domain/Product.java new file mode 100644 index 00000000..a40c7045 --- /dev/null +++ b/src/main/java/guru/springframework/domain/Product.java @@ -0,0 +1,67 @@ +package guru.springframework.domain; + +import javax.persistence.*; +import java.math.BigDecimal; + +@Entity +public class Product { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Integer id; + + @Version + private Integer version; + + private String productId; + private String description; + private String imageUrl; + private BigDecimal price; + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getProductId() { + return productId; + } + + public void setProductId(String productId) { + this.productId = productId; + } + + public String getImageUrl() { + return imageUrl; + } + + public void setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } + + public BigDecimal getPrice() { + return price; + } + + public void setPrice(BigDecimal price) { + this.price = price; + } +} diff --git a/src/main/java/guru/springframework/domain/Role.java b/src/main/java/guru/springframework/domain/Role.java new file mode 100644 index 00000000..b3aa107b --- /dev/null +++ b/src/main/java/guru/springframework/domain/Role.java @@ -0,0 +1,57 @@ +package guru.springframework.domain; + +import guru.springframework.domain.AbstractDomainClass; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by jt on 12/18/15. + */ +@Entity +public class Role extends AbstractDomainClass { + + private String role; + + @ManyToMany(fetch = FetchType.EAGER) + @JoinTable + // ~ defaults to @JoinTable(name = "USER_ROLE", joinColumns = @JoinColumn(name = "role_id"), + // inverseJoinColumns = @joinColumn(name = "user_id")) + private List users = new ArrayList<>(); + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } + + public List getUsers() { + return users; + } + + public void setUsers(List users) { + this.users = users; + } + + public void addUser(User user){ + if(!this.users.contains(user)){ + this.users.add(user); + } + + if(!user.getRoles().contains(this)){ + user.getRoles().add(this); + } + } + + public void removeUser(User user){ + this.users.remove(user); + user.getRoles().remove(this); + } + +} diff --git a/src/main/java/guru/springframework/domain/User.java b/src/main/java/guru/springframework/domain/User.java new file mode 100644 index 00000000..cb32755e --- /dev/null +++ b/src/main/java/guru/springframework/domain/User.java @@ -0,0 +1,91 @@ +package guru.springframework.domain; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by jt on 12/14/15. + */ +@Entity +public class User extends AbstractDomainClass { + + private String username; + + @Transient + private String password; + + private String encryptedPassword; + private Boolean enabled = true; + + @ManyToMany(fetch = FetchType.EAGER) + @JoinTable + // ~ defaults to @JoinTable(name = "USER_ROLE", joinColumns = @JoinColumn(name = "user_id"), + // inverseJoinColumns = @joinColumn(name = "role_id")) + private List roles = new ArrayList<>(); + private Integer failedLoginAttempts = 0; + + 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 String getEncryptedPassword() { + return encryptedPassword; + } + + public void setEncryptedPassword(String encryptedPassword) { + this.encryptedPassword = encryptedPassword; + } + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + + public void addRole(Role role){ + if(!this.roles.contains(role)){ + this.roles.add(role); + } + + if(!role.getUsers().contains(this)){ + role.getUsers().add(this); + } + } + + public void removeRole(Role role){ + this.roles.remove(role); + role.getUsers().remove(this); + } + + public Integer getFailedLoginAttempts() { + return failedLoginAttempts; + } + + public void setFailedLoginAttempts(Integer failedLoginAttempts) { + this.failedLoginAttempts = failedLoginAttempts; + } +} diff --git a/src/main/java/guru/springframework/repositories/ProductRepository.java b/src/main/java/guru/springframework/repositories/ProductRepository.java new file mode 100644 index 00000000..820f086a --- /dev/null +++ b/src/main/java/guru/springframework/repositories/ProductRepository.java @@ -0,0 +1,7 @@ +package guru.springframework.repositories; + +import guru.springframework.domain.Product; +import org.springframework.data.repository.CrudRepository; + +public interface ProductRepository extends CrudRepository{ +} diff --git a/src/main/java/guru/springframework/repositories/RoleRepository.java b/src/main/java/guru/springframework/repositories/RoleRepository.java new file mode 100644 index 00000000..771382e5 --- /dev/null +++ b/src/main/java/guru/springframework/repositories/RoleRepository.java @@ -0,0 +1,7 @@ +package guru.springframework.repositories; + +import guru.springframework.domain.Role; +import org.springframework.data.repository.CrudRepository; + +public interface RoleRepository extends CrudRepository{ +} diff --git a/src/main/java/guru/springframework/repositories/UserRepository.java b/src/main/java/guru/springframework/repositories/UserRepository.java new file mode 100644 index 00000000..50cc69df --- /dev/null +++ b/src/main/java/guru/springframework/repositories/UserRepository.java @@ -0,0 +1,8 @@ +package guru.springframework.repositories; + +import guru.springframework.domain.User; +import org.springframework.data.repository.CrudRepository; + +public interface UserRepository extends CrudRepository{ + User findByUsername(String username); +} diff --git a/src/main/java/guru/springframework/services/CRUDService.java b/src/main/java/guru/springframework/services/CRUDService.java new file mode 100644 index 00000000..95ba2cea --- /dev/null +++ b/src/main/java/guru/springframework/services/CRUDService.java @@ -0,0 +1,13 @@ +package guru.springframework.services; + +import java.util.List; + +public interface CRUDService { + List listAll(); + + T getById(Integer id); + + T saveOrUpdate(T domainObject); + + void delete(Integer id); +} diff --git a/src/main/java/guru/springframework/services/ProductService.java b/src/main/java/guru/springframework/services/ProductService.java new file mode 100644 index 00000000..9c68033a --- /dev/null +++ b/src/main/java/guru/springframework/services/ProductService.java @@ -0,0 +1,14 @@ +package guru.springframework.services; + + +import guru.springframework.domain.Product; + +public interface ProductService { + Iterable listAllProducts(); + + Product getProductById(Integer id); + + Product saveProduct(Product product); + + void deleteProduct(Integer id); +} diff --git a/src/main/java/guru/springframework/services/ProductServiceImpl.java b/src/main/java/guru/springframework/services/ProductServiceImpl.java new file mode 100644 index 00000000..1aa28619 --- /dev/null +++ b/src/main/java/guru/springframework/services/ProductServiceImpl.java @@ -0,0 +1,36 @@ +package guru.springframework.services; + +import guru.springframework.domain.Product; +import guru.springframework.repositories.ProductRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class ProductServiceImpl implements ProductService { + private ProductRepository productRepository; + + @Autowired + public void setProductRepository(ProductRepository productRepository) { + this.productRepository = productRepository; + } + + @Override + public Iterable listAllProducts() { + return productRepository.findAll(); + } + + @Override + public Product getProductById(Integer id) { + return productRepository.findById(id).get(); + } + + @Override + public Product saveProduct(Product product) { + return productRepository.save(product); + } + + @Override + public void deleteProduct(Integer id) { + productRepository.deleteById(id); + } +} diff --git a/src/main/java/guru/springframework/services/RoleService.java b/src/main/java/guru/springframework/services/RoleService.java new file mode 100644 index 00000000..e6e1ecc7 --- /dev/null +++ b/src/main/java/guru/springframework/services/RoleService.java @@ -0,0 +1,6 @@ +package guru.springframework.services; + +import guru.springframework.domain.Role; + +public interface RoleService extends CRUDService { +} diff --git a/src/main/java/guru/springframework/services/RoleServiceImpl.java b/src/main/java/guru/springframework/services/RoleServiceImpl.java new file mode 100644 index 00000000..9efeea8f --- /dev/null +++ b/src/main/java/guru/springframework/services/RoleServiceImpl.java @@ -0,0 +1,45 @@ +package guru.springframework.services; + +import guru.springframework.domain.Role; +import guru.springframework.repositories.RoleRepository; +import guru.springframework.services.RoleService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Service +@Profile("springdatajpa") +public class RoleServiceImpl implements RoleService { + + private RoleRepository roleRepository; + + @Autowired + public void setRoleRepository(RoleRepository roleRepository) { + this.roleRepository = roleRepository; + } + + @Override + public List listAll() { + List roles = new ArrayList<>(); + roleRepository.findAll().forEach(roles::add); + return roles; + } + + @Override + public Role getById(Integer id) { + return roleRepository.findById(id).get(); + } + + @Override + public Role saveOrUpdate(Role domainObject) { + return roleRepository.save(domainObject); + } + + @Override + public void delete(Integer id) { + roleRepository.deleteById(id); + } +} diff --git a/src/main/java/guru/springframework/services/UserService.java b/src/main/java/guru/springframework/services/UserService.java new file mode 100644 index 00000000..f501e923 --- /dev/null +++ b/src/main/java/guru/springframework/services/UserService.java @@ -0,0 +1,9 @@ +package guru.springframework.services; + +import guru.springframework.domain.User; + +public interface UserService extends CRUDService { + + User findByUsername(String username); + +} diff --git a/src/main/java/guru/springframework/services/UserServiceImpl.java b/src/main/java/guru/springframework/services/UserServiceImpl.java new file mode 100644 index 00000000..a8f101ce --- /dev/null +++ b/src/main/java/guru/springframework/services/UserServiceImpl.java @@ -0,0 +1,64 @@ +package guru.springframework.services; + +import guru.springframework.domain.User; + +import guru.springframework.repositories.UserRepository; +import guru.springframework.services.UserService; +import guru.springframework.services.security.EncryptionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +@Service +@Profile("springdatajpa") +public class UserServiceImpl implements UserService { + + private UserRepository userRepository; + + @Autowired + public void setUserRepository(UserRepository userRepository) { + this.userRepository = userRepository; + } + + private EncryptionService encryptionService; + + @Autowired + public void setEncryptionService(EncryptionService encryptionService) { + this.encryptionService = encryptionService; + } + + + @Override + public List listAll() { + List users = new ArrayList<>(); + userRepository.findAll().forEach(users::add); //fun with Java 8 + return users; + } + + @Override + public User getById(Integer id) { + return userRepository.findById(id).get(); + } + + @Override + public User saveOrUpdate(User domainObject) { + if(domainObject.getPassword() != null){ + domainObject.setEncryptedPassword(encryptionService.encryptString(domainObject.getPassword())); + } + return userRepository.save(domainObject); + } + @Override + @Transactional + public void delete(Integer id) { + userRepository.deleteById(id); + } + + @Override + public User findByUsername(String username) { + return userRepository.findByUsername(username); + } +} diff --git a/src/main/java/guru/springframework/services/jpaservices/AbstractJpaDaoService.java b/src/main/java/guru/springframework/services/jpaservices/AbstractJpaDaoService.java new file mode 100644 index 00000000..e5396554 --- /dev/null +++ b/src/main/java/guru/springframework/services/jpaservices/AbstractJpaDaoService.java @@ -0,0 +1,17 @@ +package guru.springframework.services.jpaservices; + +import javax.persistence.EntityManagerFactory; +import javax.persistence.PersistenceUnit; + +/** + * Created by jt on 12/14/15. + */ +public class AbstractJpaDaoService { + + protected EntityManagerFactory emf; + + @PersistenceUnit + public void setEmf(EntityManagerFactory emf) { + this.emf = emf; + } +} diff --git a/src/main/java/guru/springframework/services/jpaservices/RoleServiceJpaImpl.java b/src/main/java/guru/springframework/services/jpaservices/RoleServiceJpaImpl.java new file mode 100644 index 00000000..1a853bcc --- /dev/null +++ b/src/main/java/guru/springframework/services/jpaservices/RoleServiceJpaImpl.java @@ -0,0 +1,51 @@ +package guru.springframework.services.jpaservices; + +import guru.springframework.domain.Role; +import guru.springframework.services.RoleService; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; + +import javax.persistence.EntityManager; +import java.util.List; + +/** + * Created by jt on 12/18/15. + */ +@Service +@Profile("jpadao") +public class RoleServiceJpaImpl extends AbstractJpaDaoService implements RoleService { + + @Override + public List listAll() { + EntityManager em = emf.createEntityManager(); + + return em.createQuery("from Role", Role.class).getResultList(); + } + + @Override + public Role getById(Integer id) { + EntityManager em = emf.createEntityManager(); + return em.find(Role.class, id); + } + + @Override + public Role saveOrUpdate(Role domainObject) { + EntityManager em = emf.createEntityManager(); + + em.getTransaction().begin(); + + Role saveRole = em.merge(domainObject); + em.getTransaction().commit(); + + return saveRole; + } + + @Override + public void delete(Integer id) { + EntityManager em = emf.createEntityManager(); + + em.getTransaction().begin(); + em.remove(em.find(Role.class, id)); + em.getTransaction().commit(); + } +} diff --git a/src/main/java/guru/springframework/services/jpaservices/UserServiceJpaDaoImpl.java b/src/main/java/guru/springframework/services/jpaservices/UserServiceJpaDaoImpl.java new file mode 100644 index 00000000..a6d3b761 --- /dev/null +++ b/src/main/java/guru/springframework/services/jpaservices/UserServiceJpaDaoImpl.java @@ -0,0 +1,72 @@ +package guru.springframework.services.jpaservices; + +import guru.springframework.domain.User; +import guru.springframework.services.UserService; +import guru.springframework.services.security.EncryptionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; + +import javax.persistence.EntityManager; +import java.util.List; + +/** + * Created by jt on 12/14/15. + */ +@Service +@Profile("jpadao") +public class UserServiceJpaDaoImpl extends AbstractJpaDaoService implements UserService { + + private EncryptionService encryptionService; + + @Autowired + public void setEncryptionService(EncryptionService encryptionService) { + this.encryptionService = encryptionService; + } + + @Override + public List listAll() { + EntityManager em = emf.createEntityManager(); + + return em.createQuery("from User", User.class).getResultList(); + } + + @Override + public User getById(Integer id) { + EntityManager em = emf.createEntityManager(); + + return em.find(User.class, id); + } + + @Override + public User saveOrUpdate(User domainObject) { + EntityManager em = emf.createEntityManager(); + + em.getTransaction().begin(); + + if(domainObject.getPassword() != null){ + domainObject.setEncryptedPassword(encryptionService.encryptString(domainObject.getPassword())); + } + + User saveduser = em.merge(domainObject); + em.getTransaction().commit(); + + return saveduser; + } + + @Override + public void delete(Integer id) { + EntityManager em = emf.createEntityManager(); + + em.getTransaction().begin(); + em.remove(em.find(User.class, id)); + em.getTransaction().commit(); + } + + @Override + public User findByUsername(String userName) { + EntityManager em = emf.createEntityManager(); + + return em.createQuery("from User where userName = :userName", User.class).setParameter("userName", userName).getSingleResult(); + } +} diff --git a/src/main/java/guru/springframework/services/mapservices/AbstractMapService.java b/src/main/java/guru/springframework/services/mapservices/AbstractMapService.java new file mode 100644 index 00000000..5a4832c7 --- /dev/null +++ b/src/main/java/guru/springframework/services/mapservices/AbstractMapService.java @@ -0,0 +1,47 @@ +package guru.springframework.services.mapservices; + +import guru.springframework.domain.DomainObject; + +import java.util.*; + +/** + * Created by jt on 11/14/15. + */ +public abstract class AbstractMapService { + protected Map domainMap; + + public AbstractMapService() { + domainMap = new HashMap<>(); + } + + public List listAll() { + return new ArrayList<>(domainMap.values()); + } + + public DomainObject getById(Integer id) { + return domainMap.get(id); + } + + public DomainObject saveOrUpdate(DomainObject domainObject) { + if (domainObject != null){ + + if (domainObject.getId() == null){ + domainObject.setId(getNextKey()); + } + domainMap.put(domainObject.getId(), domainObject); + + return domainObject; + } else { + throw new RuntimeException("Object Can't be null"); + } + } + + public void delete(Integer id) { + domainMap.remove(id); + } + + private Integer getNextKey(){ + return Collections.max(domainMap.keySet()) + 1; + } + +} diff --git a/src/main/java/guru/springframework/services/mapservices/RoleServiceMapImpl.java b/src/main/java/guru/springframework/services/mapservices/RoleServiceMapImpl.java new file mode 100644 index 00000000..2260c649 --- /dev/null +++ b/src/main/java/guru/springframework/services/mapservices/RoleServiceMapImpl.java @@ -0,0 +1,12 @@ +package guru.springframework.services.mapservices; + +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; + +/** + * Created by jt on 12/18/15. + */ +@Service +@Profile("map") +public class RoleServiceMapImpl extends AbstractMapService { +} diff --git a/src/main/java/guru/springframework/services/mapservices/UserServiceMapImpl.java b/src/main/java/guru/springframework/services/mapservices/UserServiceMapImpl.java new file mode 100644 index 00000000..c78f4a5a --- /dev/null +++ b/src/main/java/guru/springframework/services/mapservices/UserServiceMapImpl.java @@ -0,0 +1,67 @@ +package guru.springframework.services.mapservices; + +import guru.springframework.domain.DomainObject; +import guru.springframework.domain.User; +import guru.springframework.services.UserService; +import guru.springframework.services.security.EncryptionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; + +/** + * Created by jt on 12/14/15. + */ +@Service +@Profile("map") +public class UserServiceMapImpl extends AbstractMapService implements UserService { + + private EncryptionService encryptionService; + + @Autowired + public void setEncryptionService(EncryptionService encryptionService) { + this.encryptionService = encryptionService; + } + + @Override + public List listAll() { + return super.listAll(); + } + + @Override + public User getById(Integer id) { + return (User) super.getById(id); + } + + @Override + public User saveOrUpdate(User domainObject) { + + if(domainObject.getPassword() != null){ + domainObject.setEncryptedPassword(encryptionService.encryptString(domainObject.getPassword())); + } + + return (User) super.saveOrUpdate(domainObject); + } + + @Override + public void delete(Integer id) { + super.delete(id); + } + + @Override + public User findByUsername(String userName) { + + Optional returnUser = domainMap.values().stream().filter(new Predicate() { + @Override + public boolean test(DomainObject domainObject) { + User user = (User) domainObject; + return user.getUsername().equalsIgnoreCase(userName); + } + }).findFirst(); + + return (User) returnUser.get(); + } +} diff --git a/src/main/java/guru/springframework/services/security/EncryptionService.java b/src/main/java/guru/springframework/services/security/EncryptionService.java new file mode 100644 index 00000000..d24444ad --- /dev/null +++ b/src/main/java/guru/springframework/services/security/EncryptionService.java @@ -0,0 +1,6 @@ +package guru.springframework.services.security; + +public interface EncryptionService { + String encryptString(String input); + boolean checkPassword(String plainPassword, String encryptedPassword); +} diff --git a/src/main/java/guru/springframework/services/security/EncryptionServiceImpl.java b/src/main/java/guru/springframework/services/security/EncryptionServiceImpl.java new file mode 100644 index 00000000..e96aa701 --- /dev/null +++ b/src/main/java/guru/springframework/services/security/EncryptionServiceImpl.java @@ -0,0 +1,18 @@ +package guru.springframework.services.security; + +import org.jasypt.util.password.StrongPasswordEncryptor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +@Service +public class EncryptionServiceImpl implements EncryptionService { + @Autowired + PasswordEncoder passwordEncoder; + public String encryptString(String input){ + return passwordEncoder.encode(input); + } + public boolean checkPassword(String plainPassword, String encryptedPassword){ + return passwordEncoder.matches(plainPassword, encryptedPassword); + } +} diff --git a/src/main/java/guru/springframework/services/security/UserDetailsImpl.java b/src/main/java/guru/springframework/services/security/UserDetailsImpl.java new file mode 100644 index 00000000..aa54fd3a --- /dev/null +++ b/src/main/java/guru/springframework/services/security/UserDetailsImpl.java @@ -0,0 +1,68 @@ +package guru.springframework.services.security; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import java.util.Collection; + + +public class UserDetailsImpl implements UserDetails { + + private Collection authorities; + private String username; + private String password; + private Boolean enabled = true; + + public void setAuthorities(Collection authorities) { + this.authorities = authorities; + } + + public void setUsername(String username) { + this.username = username; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + @Override + public Collection getAuthorities() { + return authorities; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + +} diff --git a/src/main/java/guru/springframework/services/security/UserDetailsServiceImpl.java b/src/main/java/guru/springframework/services/security/UserDetailsServiceImpl.java new file mode 100644 index 00000000..d178a87b --- /dev/null +++ b/src/main/java/guru/springframework/services/security/UserDetailsServiceImpl.java @@ -0,0 +1,34 @@ +package guru.springframework.services.security; + +import guru.springframework.domain.User; +import guru.springframework.services.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.core.convert.converter.Converter; +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; + +@Service("userDetailsService") +public class UserDetailsServiceImpl implements UserDetailsService { + + private UserService userService; + private Converter userUserDetailsConverter; + + @Autowired + public void setUserService(UserService userService) { + this.userService = userService; + } + + @Autowired + @Qualifier(value = "userToUserDetails") + public void setUserUserDetailsConverter(Converter userUserDetailsConverter) { + this.userUserDetailsConverter = userUserDetailsConverter; + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + return userUserDetailsConverter.convert(userService.findByUsername(username)); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e69de29b..e367db1f 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.profiles.active=springdatajpa diff --git a/src/main/resources/static/css/guru.css b/src/main/resources/static/css/guru.css new file mode 100644 index 00000000..9412405b --- /dev/null +++ b/src/main/resources/static/css/guru.css @@ -0,0 +1,8 @@ +input[type=submit] { + background:none!important; + border:none; + padding:0!important; + color: blue; + text-decoration: underline; + cursor:pointer; +} \ No newline at end of file diff --git a/src/main/resources/static/images/FBcover1200x628.png b/src/main/resources/static/images/FBcover1200x628.png new file mode 100644 index 00000000..d28620ad Binary files /dev/null and b/src/main/resources/static/images/FBcover1200x628.png differ diff --git a/src/main/resources/static/images/NewBannerBOOTS_2.png b/src/main/resources/static/images/NewBannerBOOTS_2.png new file mode 100644 index 00000000..e6b7ea3d Binary files /dev/null and b/src/main/resources/static/images/NewBannerBOOTS_2.png differ diff --git a/src/main/resources/templates/fragments/header.html b/src/main/resources/templates/fragments/header.html new file mode 100644 index 00000000..77dea2e2 --- /dev/null +++ b/src/main/resources/templates/fragments/header.html @@ -0,0 +1,42 @@ + + + + + + + +
+
+ +
+ Welcome +
+
+
+
+

Spring Framework Guru

+ +

Spring Boot Web App

+
+
+
+ +
+
+
+
+ + + \ No newline at end of file diff --git a/src/main/resources/templates/fragments/headerinc.html b/src/main/resources/templates/fragments/headerinc.html new file mode 100644 index 00000000..521863d9 --- /dev/null +++ b/src/main/resources/templates/fragments/headerinc.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html new file mode 100644 index 00000000..99830d5e --- /dev/null +++ b/src/main/resources/templates/index.html @@ -0,0 +1,15 @@ + + + + + Spring Framework Guru + + + + + +
+ +
+ + \ No newline at end of file diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html new file mode 100644 index 00000000..f0468e95 --- /dev/null +++ b/src/main/resources/templates/login.html @@ -0,0 +1,36 @@ + + + + Login Form + + + +
+ +
+ +
+
+ +
+ +
+ + + + + + + + + + + +
+ +
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/productform.html b/src/main/resources/templates/productform.html new file mode 100644 index 00000000..2e891f7b --- /dev/null +++ b/src/main/resources/templates/productform.html @@ -0,0 +1,53 @@ + + + + + Spring Framework Guru + + + + +
+ + +
+
+

Product Create/Update

+
+ +
+
+
+ +
+
+ + +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+ +
+
+ + + \ No newline at end of file diff --git a/src/main/resources/templates/products.html b/src/main/resources/templates/products.html new file mode 100644 index 00000000..2e20e6b5 --- /dev/null +++ b/src/main/resources/templates/products.html @@ -0,0 +1,51 @@ + + + + + Spring Framework Guru + + + + +
+ + + + +
+
+

Product Listing

+
+ + + + + +
+
+ + + + + + + + + + + + + + + + + + + +
IdProduct IdDescriptionPriceViewEditDelete
IdProduct IddescirptionpriceViewEditDelete
+ +
+
+ + + \ No newline at end of file diff --git a/src/main/resources/templates/productshow.html b/src/main/resources/templates/productshow.html new file mode 100644 index 00000000..b6725a93 --- /dev/null +++ b/src/main/resources/templates/productshow.html @@ -0,0 +1,51 @@ + + + + + Spring Framework Guru + + + + +
+ + +
+
+

Product Details

+
+ + + + +
+
+
+ +
+

Product Id

+
+
+ +
+

description

+
+
+
+ +
+

Priceaddd

+
+
+
+ +
+

url....

+
+
+
+ +
+ + + \ No newline at end of file diff --git a/src/test/java/guru/springframework/SpringBootWebApplicationTests.java b/src/test/java/guru/springframework/SpringBootWebApplicationTests.java index a1db764b..fab8c483 100644 --- a/src/test/java/guru/springframework/SpringBootWebApplicationTests.java +++ b/src/test/java/guru/springframework/SpringBootWebApplicationTests.java @@ -2,12 +2,13 @@ import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.web.WebAppConfiguration; -import org.springframework.boot.test.SpringApplicationConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -@RunWith(SpringJUnit4ClassRunner.class) -@SpringApplicationConfiguration(classes = SpringBootWebApplication.class) + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = SpringBootWebApplication.class) @WebAppConfiguration public class SpringBootWebApplicationTests { diff --git a/src/test/java/guru/springframework/configuration/RepositoryConfiguration.java b/src/test/java/guru/springframework/configuration/RepositoryConfiguration.java new file mode 100644 index 00000000..4f8aad2e --- /dev/null +++ b/src/test/java/guru/springframework/configuration/RepositoryConfiguration.java @@ -0,0 +1,15 @@ +package guru.springframework.configuration; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +@Configuration +@EnableAutoConfiguration +@EntityScan(basePackages = {"guru.springframework.domain"}) +@EnableJpaRepositories(basePackages = {"guru.springframework.repositories"}) +@EnableTransactionManagement +public class RepositoryConfiguration { +} diff --git a/src/test/java/guru/springframework/repositories/ProductRepositoryTest.java b/src/test/java/guru/springframework/repositories/ProductRepositoryTest.java new file mode 100644 index 00000000..e38693f9 --- /dev/null +++ b/src/test/java/guru/springframework/repositories/ProductRepositoryTest.java @@ -0,0 +1,73 @@ +package guru.springframework.repositories; + +import guru.springframework.configuration.RepositoryConfiguration; +import guru.springframework.domain.Product; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.junit4.SpringRunner; + +import java.math.BigDecimal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +@ExtendWith(SpringExtension.class) +@DataJpaTest +public class ProductRepositoryTest { + + @Autowired + private ProductRepository productRepository; + + @Test + public void testSaveProduct(){ + //setup product + Product product = new Product(); + product.setDescription("Spring Framework Guru Shirt"); + product.setPrice(new BigDecimal("18.95")); + product.setProductId("1234"); + + //save product, verify has ID value after save + assertNull(product.getId()); //null before save + productRepository.save(product); + assertNotNull(product.getId()); //not null after save + + //fetch from DB + Product fetchedProduct = productRepository.findById(product.getId()).orElse(null); + + //should not be null + assertNotNull(fetchedProduct); + + //should equal + assertEquals(product.getId(), fetchedProduct.getId()); + assertEquals(product.getDescription(), fetchedProduct.getDescription()); + + //update description and save + fetchedProduct.setDescription("New Description"); + productRepository.save(fetchedProduct); + + //get from DB, should be updated + Product fetchedUpdatedProduct = productRepository.findById(fetchedProduct.getId()).orElse(null); + assertEquals(fetchedProduct.getDescription(), fetchedUpdatedProduct.getDescription()); + + //verify count of products in DB + long productCount = productRepository.count(); + assertEquals(productCount, 1); + + //get all products, list should only have one + Iterable products = productRepository.findAll(); + + int count = 0; + + for(Product p : products){ + count++; + } + + assertEquals(count, 1); + } +} \ No newline at end of file