[Java] Let’s create a book management Web application with Spring Boot part2

9 minute read

Introduction

Hi, I’m @Ikuto19, a college student studying programming. This time, I’ll start from Continued from last time (part1). After briefly explaining the previous review, I will explain the creation procedure and actually make the application.

Last review and commentary

About the last time

In part1, we created and published a test application in order to understand the preparation and flow for creating a book management application. Specifically, I installed tools for using the Spring Framework and registered an account with Heroku.

Notes after this! !

I will explain the role of code and annotations from this part, but this is the first time for me to touch the Spring Framework myself. In short, I’m a beginner, so I can’t explain in detail. It’s just a brief explanation of my interpretation, so don’t take it for granted if you’re the same beginner looking at this article. It fits roughly, but I think it may be strictly different, so please check it yourself. Conversely, if you are an advanced person, please point out as I said last time.

Previous code explanation

App.java

This App.java is executing the web application. Therefore, if you do not include this main function, the application will not start. The class name can be anything, but I named it App. If you want to use a different class name, please change the content “App.class” to “class name.class”.

About @SpringBootApplication, [Spring Document Japanese Translation](https://spring.pleiades.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-using-In"[email protected]@SpringBootApplicationannotation”of(springbootapplication-annotation), it was written as follows.

@EnableAutoConfiguration: Enable Spring Boot’s auto-configuration mechanism @ComponentScan: Enable @Component Scan on the package where the application is located (see best practices) @Configuration: You can register additional beans in the context and import additional configuration classes.

I thought it would be like this when I interpreted it as a whole.

  • @SpringBootApplication → A collection of the following three functions
  • @EnableAutoConfiguration → Whether to automatically configure
  • @ComponentScan → perform component scan
  • @Configuration → Register to Bean or import class

App.java


package com.app;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
IndexController.java

IndexController.java is equivalent to Controller in MVC model. Controller is to control Model and View based on user input, but this time I try to display index.html when the URL is “/”. “Model.setAttribute(“message”, message)” can handle the character string stored in message in the called html file.

  • @Controller → Assign to the class that becomes Controller
  • @GetMapping("(URL)") → Make a GET request to “(URL)”

IndexController.java


package com.app.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class IndexController {
It's a sequel.
@GetMapping("/")
public String getIndexPage(Model model) {
String message = "Hello, World!!";
model.addAttribute("message",message);
return "index";
}
}
index.html

I understood that “xmlns:th=~” part is for using Thymeleaf which is a template engine. This allows the controller to handle values and strings on HTML as well. The contents (character string) of the message stored by “th:text=”${message}” can be displayed.

index.html


<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8">
<head>
<title>Test app</title>
</head>
<body>
<p th:text="${message}"></p>
</body>
</html>
Procfile

Java: I tried Heroku deployment of Spring Boot app I imitated the way this one was written. I searched for $JAVA_OPTS and –server.port=$PORT, but I didn’t understand because it didn’t hit very much. Roughly, as far as I can see from The Procfile of Heroku Dev Center, I think that I am writing “App type: Execution command” arbitrarily. I will.

Procfile


web: java $JAVA_OPTS -jar target/*.jar --server.port=$PORT

Creation procedure

  1. Creating a project
  2. Implementation of home screen
  3. Implementation of login screen and screen transition by DB authentication
  4. Implementation of screen transition from home screen to pages for book search/book information display/copyright display
  5. Implementation of function to register/delete from DB after searching books
  6. Apply CSS
  7. Operation check

Create project

Create the project as you did last time. The project name is “BookManagement-webapp”, and other settings are created with the same settings as the previous time. However, delete BookManagementWebappApplication.java.

Implementation of home screen

Create new file

App.java

Create and place the following App.java in the com.app package. As explained earlier, the Spring application is started by executing this class.

App.java


package com.app;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
index.html

Create index.html that will be the home screen in the templates folder. The home screen has the following functions.

  • Acquisition of book information by API (operateCollate.html)
  • Acquire and display book information in database (operateCheck.html)
  • Acquire and display the drawing of the database (operateCheck.html)

indexhtml


<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8">
<head>
<title>Book management app</title>
</head>
<body>
<div class="main_container">
<div class=title>
<h1>Book Management Web Application</h1>
</div>
<div class="middle_container">
<div class="contains collate">
<form method="get" action="operateCollate">
<input type="text" class="texts" name="isbn" value=""
placeholder="ISBN code"> <input class="submit"
type="submit" value="book matching">
</form>
</div>
<div class="contains flexbox">
<form method="get" action="operateCheck">
<input class="submit" type="submit" value="Display book information">
</form>
<form method="get" action="operateCover">
<input class="submit" type="submit" value="Book cover display">
</form>
</div>
</div>
</div></body>
</html>

Execution confirmation

You should see something like this: ![Screenshot 2020-07-29 16.49.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520861/4efd2970-5009-782e-92f7-(8160ab490fb7.png)

Login implementation using Spring Security DB authentication

Next, we will implement login by DB authentication of Spring Security. The file to be modified (blue) and the file to be added (red) are as follows. This login authentication is Implement authentication function with Spring Security ①~ImplementauthenticationfunctionwithSpringSecurity③ and make changes as necessary. So I think the code is almost the same. Also, the comments written in each file belong to the person who wrote this article, so I deleted them. So if you want to see the comments, read this article. ![Screenshot 2020-07-29 23.24.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520861/6016c474-af50-e5d6-c53c-(4ceb94054772.png)

Create new file / modify existing file

WebSecurityConfig.java

Security related settings are made in this class. configure-WebSecurity configure method removes the required files and folders from authentication. The configure method of configure-HttpSecurity configures the screen transition and the user who can access when authentication succeeds or fails. The configure method of the final configure-AuthenticationManagerBuilder is used to configure the settings related to authentication.

  • Annotation
  • @EnableWebSecurity → Enable Spring Security
  • @Autowired → Automatically store objects
  • @Bean → Register in DI container
  • @Override → indicates an override

  • Signature of each method
  • configure-WebSecurity → Web-wide security settings
  • configure-HttpSecurity → Security setting for each URL
  • configure-AuthenticationManagerBuilder → Authentication related settings

WebSecurityConfig.java


package com.app.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

import com.app.service.UserDetailsServiceImpl;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private UserDetailsServiceImpl userDetailsService;

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

// It can handle CSS, Javascript, etc. and external image files
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(
"/images/**",
"/css/**",
"/js/**"
);
}

@Override
protected void configure(HttpSecurity http) throws Exception{
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login") // login page URL
.loginProcessingUrl("/login")
.usernameParameter("username")
.passwordParameter("password")
.defaultSuccessUrl("/index", true) // URL that transitions due to successful authentication
.failureUrl("/login?error") // URL transitioned due to authentication failure
.permitAll() // all users can connect
.and()
.logout()
.logoutUrl("/logout") // URL of the logout page
.logoutSuccessUrl("/login?logout") // URL after successful logout
.permitAll();
}
It's a sequel.
@Autowired
    public void configure(AuthenticationManagerBuilder auth) throws Exception{
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
}
LoginController.java

When the URL is “/login”, login.html is displayed.

LoginController.java


package com.app.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class LoginController {
@GetMapping("/login")
public String getSignUp(Model model) {
return "login";
}
}
LoginUser.java
  • Annotation
  • @Entity → Assign to entity class
  • @Table(name = "(table name)") → specify the table name of the database to access
  • @Column(name = "column name") → specify the column name of the table
  • @Id → Primary key (Integer type this time)

LoginUser.java


package com.app.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "user")
public class LoginUser {

    @Column(name = "user_id")
    @Id
    private Long userId;

    @Column(name = "user_name")
    private String userName;

    @Column(name = "password")
    private String password;

    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 Long getUserId() {
return userId;
}

public void setUserId(Long userId) {
this.userId = userId;
}
}
LoginUserDao.java

Access the database with the findUser method and return the user object to which the user name applies.

  • @Repository → Assign to the class that accesses DB

LoginUserDao.java


package com.app.repository;

import javax.persistence.EntityManager;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.app.entity.LoginUser;

@Repository
public class LoginUserDao {

    @Autowired
    EntityManager em;

    public LoginUser findUser(String userName) {

        String query = "";
        query += "SELECT * ";
        query += "FROM user ";
        query += "WHERE user_name = :userName ";

        return (LoginUser)em.createNativeQuery(query, LoginUser.class).setParameter("userName", userName).getSingleResult();
    }
}
UserRepository.java

UserRepository.java


package com.app.repository;

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

import com.app.entity.LoginUser;

/*
 * <Entity class, ID type>
 */
@Repository
public interface UserRepository extends JpaRepository<LoginUser, Integer>{}
UserDetailsServiceImpl.java
  • @Service → Assign to the class that performs business logic

UserDetailsServiceImpl.java


package com.app.service;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import com.app.repository.LoginUserDao;
import com.app.entity.LoginUser;

@Service
public class UserDetailsServiceImpl implements UserDetailsService{

    @Autowired
    private LoginUserDao userDao;

    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {

        LoginUser user = userDao.findUser(userName);
        if (user == null) throw new UsernameNotFoundException(userName + "does not exist in the database.");

        List<GrantedAuthority> grantList = new ArrayList<GrantedAuthority>();
        GrantedAuthority authority = new SimpleGrantedAuthority("USER");
        grantList.add(authority);
        
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        UserDetails userDetails = (UserDetails)new User(user.getUserName(), encoder.encode(user.getPassword()),grantList);

        return userDetails;
    }
}
index.html

Place the following at the bottom of the “class=”contains flexbox”” div tag.

index.html


<form method="post" id="logout" th:action="@{/logout}">
<button type="submit">Logout</button>
</form>
login.html

login.html


<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<meta charset="UTF-8">
<title>LoginPage</title>
</head>
<body>
<div class="main_container">
<p th:if="${param.error}" class="message"> *The user name or password is incorrect.</p>
<p th:if="${param.logout}" class="message"> *Logged out</p>
<div class="login_container">
<form th:action="@{/login}" method="post">
<div class="buttons username">
<i class="fas fa-users"></i> <input class="texts" type="text" name="username"
placeholder="username" />
</div>
<div class="buttons pass">
<i class="fas fa-lock"></i> <input class="texts" type="password" name="password"
placeholder="password" />
</div>
<div class="submitButton">
<input class="submit" type="submit" value="login" />
</div>
</form>
</div>
</div>
</body>
</html>
application.properties

application.properties


spring.datasource.url=jdbc:mysql://localhost:3306/manageBook
spring.datasource.username=(mysql username)
spring.datasource.password=(mysql password)
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.database=MYSQL
spring.jpa.hibernate.ddl-auto=update
pom.xml

Add the following.

pom.xml


<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>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

Database constructionExecute the following command to the installed MySQL.

terminal


$ mysql -u (mysql username) -p
Enter password: (mysql password)
mysql> create database manageBook;
mysql> use manageBook
mysql> create table user(user_id int auto_increment,user_name varchar(256),password varchar(256),PRIMARY KEY(user_id));
mysql> insert user value(1,"(favorite name)","(favorite password)");
mysql> select * from user;

If you check the contents of the table with the last “select * from user;”, you will see the following. After confirming, log out with the quit command.

terminal


mysql> select * from user;
+---------+-----------+---------------+
| user_id | user_name | password |
+---------+-----------+---------------+
| 1 | (favorite name) | (favorite password) |
+---------+-----------+---------------+
1 row in set (0.04 sec)

mysql> quit;
Bye

Execution confirmation

After running the Spring application, access http://localhost:8080/login and check. Maybe you can log in.

At the end

This time I’ll end here. Next time, at the end, we will implement the transition from the home screen and the functions related to DB access. Continue to next time (part3) >Posting soon

Reference site

Primary mistake that Spring Boot could not scan the component | Engineer 2 Nensei no Kuroku

Implementing authentication function with Spring Security ①-Qiita

Memo of trying Spring Boot-Qiita

Collection of Spring Boot annotations

How to define Bean using Configuration class in Spring Boot-Reasonable Code

Spring Security Usage Note Basics/Mechanisms-Qiita

Set Web Security with Spring Boot (1/2): CodeZine

JPA (Java Persistence API) Annotations