Spring Boot Examples

SpringBoot+SpringMVC+Spring Security+Spring JPA+SpringBoot Postgres

My Fist Application in Java Spring Boot with Spring MVC, Spring Security, Postgres

In this example we are creating a spring boot application that implements login process
 Using below tech stack:

·         Spring Boot(1.5.9)
·         Spring Security
·         Spring MVC
·         JPA
·         Thymeleaf
·         Postgres 9
·         Bootstrap  used for UI presentation.
·         Maven 3+
·         STS
·         Java 8

To create Project

First we will use the Spring initializer page to create our maven project with the dependencies listed above.
1.       Go to  https://start.spring.io/

2.       Leave everything as it is and select the following dependencies: Web, JPA, Security, Postgres, and Thymeleaf.



 Click on Generate Project button and it will download a zip file (logindemo.zip) with our maven project.
Import Project into Eclipse
1.       Unzip the zip file.
2.       Import into Eclipse as “Existing Maven Project
3.       Choose the root directory of the project generated (where the pom.xml file is located) and click on Finish.







To execute Thymeleaf in “LEGACYHTML5” mode, we need to add an extra dependency in our pom.xml file → nekohtml.
Also we need to add the following properties in the applicaiton.properties file
·         spring.thymeleaf.mode=LEGACYHTML5
·         spring.thymeleaf.cache=false
Open pom.xml file and add




<?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.icxl</groupId>
                <artifactId>logindemo</artifactId>
                <version>0.0.1-SNAPSHOT</version>
                <packaging>jar</packaging>

                <name>logindemo</name>
                <description>Demo project for Spring Boot</description>

                <parent>
                                <groupId>org.springframework.boot</groupId>
                                <artifactId>spring-boot-starter-parent</artifactId>
                                <version>1.5.9.RELEASE</version>
                                <relativePath/> <!-- lookup parent from repository -->
                </parent>

                <properties>
                                <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
                                <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
                                <java.version>1.8</java.version>
                </properties>

                <dependencies>
                                <dependency>
                                                <groupId>org.springframework.boot</groupId>
                                                <artifactId>spring-boot-starter-data-jpa</artifactId>
                                </dependency>
                                <dependency>
                                                <groupId>org.springframework.boot</groupId>
                                                <artifactId>spring-boot-starter-security</artifactId>
                                </dependency>
                                <dependency>
                                                <groupId>org.springframework.boot</groupId>
                                                <artifactId>spring-boot-starter-thymeleaf</artifactId>
                                </dependency>
                                <dependency>
                                                <groupId>org.springframework.boot</groupId>
                                                <artifactId>spring-boot-starter-web</artifactId>
                                </dependency>

                                <dependency>
                                                <groupId>org.postgresql</groupId>
                                                <artifactId>postgresql</artifactId>
                                                <scope>runtime</scope>
                                </dependency>
                                <dependency>
                                                <groupId>org.springframework.boot</groupId>
                                                <artifactId>spring-boot-starter-test</artifactId>
                                                <scope>test</scope>
                                </dependency>
                                <dependency>
                                                <groupId>org.springframework.security</groupId>
                                                <artifactId>spring-security-test</artifactId>
                                                <scope>test</scope>
                                </dependency>
                               
                                <dependency>
                                                <groupId>net.sourceforge.nekohtml</groupId>
                                                <artifactId>nekohtml</artifactId>
                                                <version>1.9.21</version>
                                </dependency>
                               
                </dependencies>

                <build>
                                <plugins>
                                                <plugin>
                                                                <groupId>org.springframework.boot</groupId>
                                                                <artifactId>spring-boot-maven-plugin</artifactId>
                                                </plugin>
                                </plugins>
                </build>


</project>


Now create Model for User and Role



package com.icxl.logindemo.model;

import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.data.annotation.Transient;

@Entity
@Table(name = "userp")
public class User {

                @Id
                @GeneratedValue(strategy = GenerationType.AUTO)
                @Column(name = "user_id")
                private int id;
                @Column(name = "email")
                @Email(message = "*Please provide a valid Email")
                @NotEmpty(message = "*Please provide an email")
                private String email;
                @Column(name = "password")
                @Length(min = 5, message = "*Your password must have at least 5 characters")
                @NotEmpty(message = "*Please provide your password")
                @Transient
                private String password;
                @Column(name = "name")
                @NotEmpty(message = "*Please provide your name")
                private String name;
                @Column(name = "last_name")
                @NotEmpty(message = "*Please provide your last name")
                private String lastName;
                @Column(name = "active")
                private int active;
                @ManyToMany(cascade = CascadeType.ALL)
                @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
                private Set<Role> roles;

                public int getId() {
                                return id;
                }

                public void setId(int id) {
                                this.id = id;
                }

                public String getPassword() {
                                return password;
                }

                public void setPassword(String password) {
                                this.password = password;
                }

                public String getName() {
                                return name;
                }

                public void setName(String name) {
                                this.name = name;
                }

                public String getLastName() {
                                return lastName;
                }

                public void setLastName(String lastName) {
                                this.lastName = lastName;
                }

                public String getEmail() {
                                return email;
                }

                public void setEmail(String email) {
                                this.email = email;
                }

                public int getActive() {
                                return active;
                }

                public void setActive(int active) {
                                this.active = active;
                }

                public Set<Role> getRoles() {
                                return roles;
                }

                public void setRoles(Set<Role> roles) {
                                this.roles = roles;
                }

}


Data Layer (JPA Repositories)

UserRepository


package com.icxl.logindemo.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.icxl.logindemo.model.User;

RoleRepository
@Repository("userRepository")
public interface UserRepository extends JpaRepository<User, Long> {
                 User findByEmail(String email);
}


package com.icxl.logindemo.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.icxl.logindemo.model.Role;

@Repository("roleRepository")
public interface RoleRepository extends JpaRepository<Role, Integer>{
                Role findByRole(String role);

}


Now we will create user service layer and will inject the UserRepository, RoleRepository and the BCryptPasswordEncoder into our service implementation.

package com.icxl.logindemo.services;

import com.icxl.logindemo.model.User;

public interface UserService {
                public User findUserByEmail(String email);
                public void saveUser(User user);
}


package com.icxl.logindemo.services;

import java.util.Arrays;
import java.util.HashSet;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import com.icxl.logindemo.model.Role;
import com.icxl.logindemo.model.User;
import com.icxl.logindemo.repository.RoleRepository;
import com.icxl.logindemo.repository.UserRepository;


@Service("userService")
public class UserServiceImpl implements UserService{

                @Autowired
                private UserRepository userRepository;
                @Autowired
    private RoleRepository roleRepository;
    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;
               
                @Override
                public User findUserByEmail(String email) {
                                return userRepository.findByEmail(email);
                }

                @Override
                public void saveUser(User user) {
                                user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
        user.setActive(1);
        Role userRole = roleRepository.findByRole("ADMIN");
        user.setRoles(new HashSet<Role>(Arrays.asList(userRole)));
                                userRepository.save(user);
                }

}


Now we will create configuration file that inject password encoder in service layer

WebMvcConfig.java

package com.icxl.logindemo.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {

                @Bean
                public BCryptPasswordEncoder passwordEncoder() {
                                BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
                                return bCryptPasswordEncoder;
                }

}


Add security configuration file

package com.icxl.logindemo.configuration;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
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.web.util.matcher.AntPathRequestMatcher;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

                @Autowired
                private BCryptPasswordEncoder bCryptPasswordEncoder;   //this is password encoder reference implemented in WebMvcConfig.java

                @Autowired
                private DataSource dataSource//Data source implemented is handled by Spring Boot itself. We just need add database information in the application.properties file
               
               
                //Reference to user and role queries stored in application.properties file
               
                @Value("${spring.queries.users-query}")
                private String usersQuery;
               
                @Value("${spring.queries.roles-query}")
                private String rolesQuery;

               
                //AuthenticationManagerBuilder provides a mechanism to get a user based on the password encoder, data source, user query and role query.
               
                @Override
                protected void configure(AuthenticationManagerBuilder auth)
                                                throws Exception {
                                auth.
                                                jdbcAuthentication()
                                                                .usersByUsernameQuery(usersQuery)
                                                                .authoritiesByUsernameQuery(rolesQuery)
                                                                .dataSource(dataSource)
                                                                .passwordEncoder(bCryptPasswordEncoder);
                }

                //Here we define the antMatchers to provide access based on the role(s) , the parameters for the login process , the success login page, the failure login page, and the logout page .
                @Override
                protected void configure(HttpSecurity http) throws Exception {
                               
                                http.
                                                authorizeRequests()
                                                                .antMatchers("/").permitAll()
                                                                .antMatchers("/login").permitAll()
                                                                .antMatchers("/registration").permitAll()
                                                                .antMatchers("/admin/**").hasAuthority("ADMIN").anyRequest()
                                                                .authenticated().and().csrf().disable().formLogin()
                                                                .loginPage("/login").failureUrl("/login?error=true")
                                                                .defaultSuccessUrl("/admin/home")
                                                                .usernameParameter("email")
                                                                .passwordParameter("password")
                                                                .and().logout()
                                                                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                                                                .logoutSuccessUrl("/").and().exceptionHandling()
                                                                .accessDeniedPage("/access-denied");
                }
               
                @Override
                public void configure(WebSecurity web) throws Exception {
                    web
                       .ignoring()
                       .antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**");
                }

}


Application.properties

# ===============================
# = DATA SOURCE
# ===============================
spring.datasource.url = jdbc:postgresql://127.0.0.1:5432/employees
spring.datasource.username = postgres
spring.datasource.password = postgres
spring.datasource.testWhileIdle = true
spring.datasource.validationQuery = SELECT 1

# ===============================
# = JPA / HIBERNATE
# ===============================
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = update
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect

# ===============================
# = Thymeleaf configurations
# ===============================
spring.thymeleaf.mode=LEGACYHTML5
spring.thymeleaf.cache=false

# ==============================================================
# = Spring Security / Queries for AuthenticationManagerBuilder 
# ==============================================================
spring.queries.users-query=select email, password, active from userp where email=?
spring.queries.roles-query=select u.email, r.role from userp u inner join user_role ur on(u.user_id=ur.user_id) inner join role r on(ur.role_id=r.role_id) where u.email=?



Controller Layer

MVC Logic

package com.icxl.logindemo.controllers;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import com.icxl.logindemo.model.User;
import com.icxl.logindemo.services.UserService;


@Controller
public class LoginController {
               
                @Autowired
                private UserService userService;

                @RequestMapping(value={"/", "/login"}, method = RequestMethod.GET)
                public ModelAndView login(){
                                ModelAndView modelAndView = new ModelAndView();
                                modelAndView.setViewName("login");
                                return modelAndView;
                }
               
               
                @RequestMapping(value="/registration", method = RequestMethod.GET)
                public ModelAndView registration(){
                                ModelAndView modelAndView = new ModelAndView();
                                User user = new User();
                                modelAndView.addObject("user", user);
                                modelAndView.setViewName("registration");
                                return modelAndView;
                }
               
                @RequestMapping(value = "/registration", method = RequestMethod.POST)
                public ModelAndView createNewUser(@Valid User user, BindingResult bindingResult) {
                                ModelAndView modelAndView = new ModelAndView();
                                User userExists = userService.findUserByEmail(user.getEmail());
                                if (userExists != null) {
                                                bindingResult
                                                                                .rejectValue("email", "error.user",
                                                                                                                "There is already a user registered with the email provided");
                                }
                                if (bindingResult.hasErrors()) {
                                                modelAndView.setViewName("registration");
                                } else {System.out.println("Tyring to create user ...");
                                                userService.saveUser(user);
                                                modelAndView.addObject("successMessage", "User has been registered successfully");
                                                modelAndView.addObject("user", new User());
                                                modelAndView.setViewName("registration");
                                               
                                }
                                return modelAndView;
                }
               
                @RequestMapping(value="/admin/home", method = RequestMethod.GET)
                public ModelAndView home(){
                                ModelAndView modelAndView = new ModelAndView();
                                Authentication auth = SecurityContextHolder.getContext().getAuthentication();
                                User user = userService.findUserByEmail(auth.getName());
                                modelAndView.addObject("userName", "Welcome " + user.getName() + " " + user.getLastName() + " (" + user.getEmail() + ")");
                                modelAndView.addObject("adminMessage","Content Available Only for Users with Admin Role");
                                modelAndView.setViewName("admin/home");
                                return modelAndView;
                }
               

}

Resources/templates/registration.html


<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
                xmlns:th="http://www.thymeleaf.org">
<head>
                <title>Registration Form</title>
                <link rel="stylesheet" type="text/css" th:href="@{/css/registration.css}" />
                <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
                <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
                <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
                <form th:action="@{/}" method="get">
                                <button class="btn btn-md btn-warning btn-block" type="Submit">Go To Login Page</button>
                </form> 
               
                <div class="container">
                                <div class="row">
                                                <div class="col-md-6 col-md-offset-3">
                                                                <form autocomplete="off" action="#" th:action="@{/registration}"
                                                                                th:object="${user}" method="post" class="form-horizontal"
                                                                                role="form">
                                                                                <h2>Registration Form</h2>
                                                                                <div class="form-group">
                                                                                                <div class="col-sm-9">
                                                                                                <label th:if="${#fields.hasErrors('name')}" th:errors="*{name}"
                                                                                                                                class="validation-message"></label>
                                                                                                <input type="text" th:field="*{name}" placeholder="Name"
                                                                                                                                class="form-control" />
                                                                                                </div>
                                                                                </div>

                                                                                <div class="form-group">
                                                                                                <div class="col-sm-9">
                                                                                                <label      th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}"
                                                                                                                                class="validation-message"></label>
                                                                                                                <input type="text" th:field="*{lastName}"
                                                                                                                                placeholder="Last Name" class="form-control" />
                                                                                                </div>
                                                                                </div>
                                                                                <div class="form-group">
                                                                                                <div class="col-sm-9">
                                                                                                                <input type="text" th:field="*{email}" placeholder="Email"
                                                                                                                                class="form-control" /> <label
                                                                                                                                th:if="${#fields.hasErrors('email')}" th:errors="*{email}"
                                                                                                                                class="validation-message"></label>
                                                                                                </div>
                                                                                </div>
                                                                                <div class="form-group">
                                                                                                <div class="col-sm-9">
                                                                                                                <input type="password" th:field="*{password}"
                                                                                                                                placeholder="Password" class="form-control" /> <label
                                                                                                                                th:if="${#fields.hasErrors('password')}" th:errors="*{password}"
                                                                                                                                class="validation-message"></label>
                                                                                                </div>
                                                                                </div>

                                                                                <div class="form-group">
                                                                                                <div class="col-sm-9">
                                                                                                                <button type="submit" class="btn btn-primary btn-block">Register User</button>
                                                                                                </div>
                                                                                </div>
                                                                               
                                                                                <span th:utext="${successMessage}"></span>
                                                                               
                                                                               
                                                                </form>
                                                </div>
                                </div>
                </div>

</body>
</html>




Login.html


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
                xmlns:th="http://www.thymeleaf.org">

<head>
                <title>Spring Security Tutorial</title>
                <link rel="stylesheet" type="text/css" th:href="@{/css/login.css}" />
                <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
                <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
                <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>

<body>
                <form th:action="@{/registration}" method="get">
                                <button class="btn btn-md btn-warning btn-block" type="Submit">Go To Registration Page</button>
                </form> 
               
                <div class="container">
                                <img th:src="@{/images/login.jpg}" class="img-responsive center-block" width="300" height="300" alt="Logo" />
                                <form th:action="@{/login}" method="POST" class="form-signin">
                                                <h3 class="form-signin-heading" th:text="Welcome"></h3>
                                                <br/>
                                                 
                                                <input type="text" id="email" name="email"  th:placeholder="Email"
                                                                class="form-control" /> <br/>
                                                <input type="password"  th:placeholder="Password"
                                                                id="password" name="password" class="form-control" /> <br />
                                                               
                                                <div align="center" th:if="${param.error}">
                                                                <p style="font-size: 20; color: #FF1C19;">Email or Password invalid, please verify</p>
                                                </div>
                                                <button class="btn btn-lg btn-primary btn-block" name="Submit" value="Login" type="Submit" th:text="Login"></button>
                                </form>
                </div>
</body>
</html>

CREATE SEQUENCE role_seq;

CREATE TABLE role (
  role_id int NOT NULL DEFAULT NEXTVAL ('role_seq'),
  role varchar(255) DEFAULT NULL,
  PRIMARY KEY (role_id)
)  ;



CREATE SEQUENCE userp_seq;

CREATE TABLE userp (
  user_id int NOT NULL DEFAULT NEXTVAL ('userp_seq'),
  active int DEFAULT NULL,
  email varchar(255) NOT NULL,
  last_name varchar(255) NOT NULL,
  name varchar(255) NOT NULL,
  password varchar(255) NOT NULL,
  PRIMARY KEY (user_id)
)  ;



CREATE TABLE user_role (
  user_id int NOT NULL,
  role_id int NOT NULL,
  PRIMARY KEY (user_id,role_id)
 ,
  CONSTRAINT FK859n2jvi8ivhui0rl0esws6o FOREIGN KEY (user_id) REFERENCES userp (user_id),
  CONSTRAINT FKa68196081fvovjhkek5m97n3y FOREIGN KEY (role_id) REFERENCES role (role_id)
) ;



Go to file LogindemoApplication

Run as Java Application or Spring boot App


  

No comments:

Post a Comment