0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

I tried creating a web application with springboot [Internal management system]

Last updated at Posted at 2023-12-28

1. Create a project

Project: Maven
Language: Java
SpringBoot: 3.2.1

Group: com.example
Artifact: SampleWeb
Name : SampleWeb
Description: Demo project for Spring Boot
Package name: com.example.demo
Dependencies

Spring Boot DevTools
Spring Web
Lombok
Thymeleaf
PostgreSQL Driver
Validation

2. Display login screen

(1) Understand the processing steps required to display the screen
① Access the page “http://~/login” 
② The controller in charge of “http://~/login” accepts (Java file)
③ Returns a file that defines screen display information (html file)
④ Display content of html file in browser

(2) Create a controller
① Create a controller folder and LoginController file in src/main/java/com.example.demo

src/main/java/com.example.demo.controller/LoginController.java
package com.example.demo.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;

import com.example.demo.form.LoginForm;
import com.example.demo.service.LoginService;

import lombok.RequiredArgsConstructor;

@Controller
@RequiredArgsConstructor
public class LoginController {

    private final LoginService service;

    @GetMapping("/login")
    public String view(Model model, LoginForm form) {
        return "login";
    }

    @PostMapping("/login")
    public String login(Model model, LoginForm form) {
        var userInfo = service.searchUserById(form.getLoginId());
        var isCorrectUserAuth = userInfo.isPresent()
                && form.getPassword().equals(userInfo.get().getPassword());
        if (isCorrectUserAuth) {
            return "redirect:/menu";
        } else {
            model.addAttribute("errorMsq", "The combination of login ID and password is incorrect");
            return "login";
        }
    }
}

② Create Login.html file in src/main/resources/templates

③ Create a form folder class to LoginForm file in src/main/java/com.example.demo
※ Form class is a class for passing data between html and Controller.

src/main/java/com.example.demo/form/LoginForm.java
package com.example.demo.form;

import lombok.Data;

@Data
public class LoginForm {

    private String loginId;
    private String password;

    public String getLoginId() {
        return this.loginId;
    }
}

※ THYMELEAF is a template engine that can be described as HTML and can be displayed as it is. You can also create dynamic content by linking with Java code.
※ A template engine is a software for generating a web page by combining static content such as HTML and CSS with a dynamic content obtained from a database or API.

④ Add thymeleaf to login.html

login.html
package com.example.demo.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;

import com.example.demo.form.LoginForm;

@Controller
public class LoginController {

    @GetMapping("/login")
    public String view(Model model, LoginForm form) {
        return "login";
    }

    @PostMapping("/login")
    public void login(LoginForm form) {
        System.out.println(form);
    }
}

(3) Implement processing for successful and failed logins!

① Create menu.html in src/main/resources/templates

src/main/resources/templates/menu.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>TOPmenu</title>
  </head>
  <body></body>
</html>

② Create a controller folder and MenuController file

controller/MenuController.java
package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class MenuController {

    @GetMapping(("/menu"))
    public String view() {
        return "menu";
    }

}

(4)Access the database using Spring Data JPA!

① Write the description to connect to the DB in application.properties

resources/application.properties
spring.jpa.database=POSTGRESQL
spring.datasource.url=jdbc:postgresql://localhost:5432/SpringTestDB
spring.datasource.username=username
spring.datasource.password=password

② Create code to add to spring data jpa in pom.xml
※ pom.xml is a file that defines additional libraries required for implementation

pom.xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

③ Create repository package and UserInfoRepository interface in src/main/java/com.example.demo

src/main/java/com.example.demo/UserInfo.java
package com.example.demo.entity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;

@Entity
@Table(name = "user_info")
@Data
public class UserInfo {

    @Id
    @Column(name = "login_id")
    private String loginId;
    private String password;
}

④ Create service package and LoginService class in src/main/java/com.example.demo

service/LoginService.java
package com.example.demo.service;

import java.util.Optional;

import org.springframework.stereotype.Service;

import com.example.demo.entity.UserInfo;
import com.example.demo.repository.UserInfoRepository;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class LoginService {

    private UserInfoRepository repository;

    public Optional<UserInfo> searchUserById(String loginId) {
        return repository.findById(loginId);
    }
}

⑤ Modify LoginController.java to call service class

LoginController.java
package com.example.demo.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;

import com.example.demo.form.LoginForm;
import com.example.demo.service.LoginService;

import lombok.RequiredArgsConstructor;

@Controller
@RequiredArgsConstructor
public class LoginController {

    private final LoginService service;

    @GetMapping("/login")
    public String view(Model model, LoginForm form) {
        return "login";
    }

    @PostMapping("/login")
    public String login(Model model, LoginForm form) {
        var userInfo = service.searchUserById(form.getLoginId());
        var isCorrectUserAuth = userInfo.isPresent()
                && form.getPassword().equals(userInfo.get().getPassword());
        if (isCorrectUserAuth) {
            return "redirect:/menu";
        } else {
            model.addAttribute("errorMsq", "The combination of login ID and password is incorrect");
            return "login";
        }
    }
}

What is Spring Data JPA?

  • Libraries (functions) for using databases
  • With basic syntax such as select and update using primary keys, you can execute SQL without writing a query.

Example of automatic SQL conversion by method name

findByAge(argument)
=> select * from table name where age = [Argument];

findByAge(argument 1, argument 2)
=> select * from table name where age = [Argument 1] and live = [Argument 2];

findByAgeIsNull()
=> select * from table name where age IS NULL;

About Controller,Service,Repository

Controller

  • Receive screen input
  • Call the Service class
  • Call the destination screen

Service

  • Implement business logic such as check processing and DV access (calling Repository class)

Repository

  • Perform DB access

(5)Implement hashed password comparison processing!
① Create config package and BeanDefine class in src/main/java/com.example.demo

config/BeanDefine.java
package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class BeanDefine {

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

② Modify LoginController.java

controller/LoginController.java
package com.example.demo.controller;

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.example.demo.form.LoginForm;
import com.example.demo.service.LoginService;

import lombok.RequiredArgsConstructor;

//LoginScreenController
@Controller
@RequiredArgsConstructor
public class LoginController {

    // LoginService
    private final LoginService service;
    // passwordEncoder
    private final PasswordEncoder passwordEncoder;

    // Initial display
    // @Param modelモデル
    // @Param form入力情報
    // return 表示画面

    @GetMapping("/login")
    public String view(Model model, LoginForm form) {
        return "login";
    }

    // Login
    // @Param modelモデル
    // @Param form入力情報
    // return 表示画面

    @PostMapping("/login")
    public String login(Model model, LoginForm form) {
        var userInfo = service.searchUserById(form.getLoginId());
        var isCorrectUserAuth = userInfo.isPresent()
                && passwordEncoder.matches(form.getPassword(), userInfo.get().getPassword());
        if (isCorrectUserAuth) {
            return "redirect:/menu";
        } else {
            model.addAttribute("errorMsq", "The combination of login ID and password is incorrect");
            return "login";
        }
    }
}

(6) Manage error messages with property files

① Create messages.properties in the resources file

resources/messages.properties
login.wrongInput=The combination of login ID and password is incorrect.
signup.existedLoginId=This is the login ID that you have already registered.
User registration completed.

② Create util package and AppUtil class in src/main/java/com.example.demo

util/AppUtil.java
package com.example.demo.util;

import java.util.Locale;

import org.springframework.context.MessageSource;

//Application common class
public class AppUtil {

    public static String getMessage(MessageSource messageSource, String key, Object... params) {
        return messageSource.getMessage(key, params, Locale.ENGLISH);
    }

}

③ Modify LoginController.java

LoginController.java
package com.example.demo.controller;

import org.springframework.context.MessageSource;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.example.demo.form.LoginForm;
import com.example.demo.service.LoginService;
import com.example.demo.util.AppUtil;

import lombok.RequiredArgsConstructor;

//LoginScreenController
@Controller
@RequiredArgsConstructor
public class LoginController {

    // LoginService
    private final LoginService service;
    // passwordEncoder
    private final PasswordEncoder passwordEncoder;

    private final MessageSource messageSource;

    // Initial display
    // @Param modelモデル
    // @Param form入力情報
    // return 表示画面

    @GetMapping("/login")
    public String view(Model model, LoginForm form) {
        return "login";
    }

    // Login
    // @Param modelモデル
    // @Param form入力情報
    // return 表示画面

    @PostMapping("/login")
    public String login(Model model, LoginForm form) {
        var userInfo = service.searchUserById(form.getLoginId());
        var isCorrectUserAuth = userInfo.isPresent()
                && passwordEncoder.matches(form.getPassword(), userInfo.get().getPassword());
        if (isCorrectUserAuth) {
            return "redirect:/menu";
        } else {
            var errorMsg = AppUtil.getMessage(messageSource, ErrorMessageConst.LOGIN_WRONG_INPUT);
            model.addAttribute("errorMsq", errorMsg);
            return "login";
        }
    }
}

④ Create constant package and ErrorMeddageConst class in src/main/java/com.example.demo

constant/ErrorMeddageConst.java
package com.example.demo.constant;

//Error message ID constant class
public class ErrorMeddageConst {
    // Incorrect input on login screen
    public static final String LOGIN_WRONG_INPUT = "login.wrongInput";
}

(7)Implement the process to register in the DB user information table!

① Create signup.html in template

template/signup.html
<!DOCTYPE html>
<html xmlns:th="http://www.themeleaf.org" lang="en" xml:lang="en">//Add thymeleaf
  <head>
    <meta charset="utf-8" />
    <title>User registration screen</title>
  </head>
  <body>
    <2>Login screen</h2>
      <span th:text=${message}></span>
      <form th:action="@{/signup}" method="post" th:object="${signForm}">
        <div>
        <label>Login ID</label>
        <input type="text" th:field="*{signId}">
        </div>
        <div>
          <label>Password</label>
          <input type="password" th:field="*{password}">
        </div>
        <div>
          <input type="submit" value="login">
        </div>
      </form>
  </body>
</html>

② SignupController file in controller folder

controller/SignupController.java
package com.example.demo.controller;

import org.springframework.context.MessageSource;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.example.demo.form.LoginForm;
import com.example.demo.service.LoginService;
import com.example.demo.service.SignupService;

import lombok.RequiredArgsConstructor;

//User registration screen Controller
@Controller
@RequiredArgsConstructor
public class SignupController {

    // User registration screen Service
    private final SignupService service;

    // Initial display
    // @Param modelモデル
    // @Param form入力情報
    // return 表示画面

    @GetMapping("/signup")
    public String view(Model model, SignupForm form) {
        return "signup";
    }

    // User registration screen
    // @Param modelモデル
    // @Param form入力情報
    // return 表示画面

    @PostMapping("/signup")
    public void signup(Model model, SignupForm form) {

    }
}

③ Create a Signup file in the form folder

form/Signup.java
package com.example.demo.form;

import lombok.Data;

//User registration screen form
@Data
public class Signup {

    // LoginId
    private String loginId;
    // Password
    private String password;

}

④ Prepare to display in login.html

template/login.html
<!DOCTYPE html>
<html xmlns:th="http://www.themeleaf.org" lang="en" xml:lang="en">//Add thymeleaf
  <head>
    <meta charset="utf-8" />
    <title>Login screen</title>
  </head>
  <body>
    <2>Login screen</h2>
      <span th:text=${errorMsg}></span>
      <form th:action="@{/login}" method="post" th:object="${loginForm}">
        <div>
        <label>Login ID</label>
        <input type="text" th:field="*{loginId}">
        </div>
        <div>
          <label>Password</label>
          <input type="password" th:field="*{password}">
        </div>
        <div>
          <input type="submit" value="login">
        </div>
      </form>
      <a th:href="@{/signup}"">user registration</a>
  </body>
</html>

⑤ Create a SignupService class in service

service/SignupService.java

```import org.springframework.stereotype.Service;

import com.example.demo.entity.UserInfo;
import com.example.demo.form.SignupForm;
import com.example.demo.repository.UserInfoRepository;

import lombok.RequiredArgsConstructor;

//User registration screen

public class SignupService {

    private final UserInfoRepository repository;
    // Dozer Mapper
    private final Mapper mapper;
    // passwordEncoder
    private final PasswordEncoder passwordEncoder;

    public UserInfo resistUserById(SignupForm form) {
        var userInfo = mapper.map(form, UserInfo.class);

        var encodedPassword = passwordEncoder.encode(form.getPassword());
        userInfo.setPassword(encodedPassword);

        return repository.save(userInfo);
    }
}

⑥ Add Dozar Mapper to Project

pom.xml
<dependency>
    <groupId>net.sf.dozar</groupId>
    <artifactId>dozar-mapper</artifactId>
    <version>5.6.0</version>
</dependency>

(8) Implement key duplication check before registering table data!

① Edit service/SignupService.java

service/SignupService.java
package com.example.demo.service;

import dozer.DozerBeanMapper;

import java.util.Optional;

import org.apache.catalina.mapper.Mapper;
import org.springframework.context.MessageSource;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import com.example.demo.entity.UserInfo;
import com.example.demo.form.SignupForm;
import com.example.demo.repository.UserInfoRepository;

import lombok.RequiredArgsConstructor;

//User registration screen
@Service
@RequiredArgsConstructor
public class SignupService {

    private final UserInfoRepository repository;
    // Dozer Mapper
    private final Mapper mapper;
    // passwordEncoder
    private final PasswordEncoder passwordEncoder;

    public Optinal<UserInfo> resistUserById(SignupForm form) {
        var userInfoExistedOpt = repository.findAll(form.getLoginId());
        if (userInfoExistedOpt.isPresent()) {
            return Optional.empty();
        }

        var userInfo = mapper.map(form, UserInfo.class);
        var encodedPassword = passwordEncoder.encode(form.getPassword());
        userInfo.setPassword(encodedPassword);

        return Optional.of(repository.save(userInfo));
    }
}

② Edit controller/SignupController.java

controller/SignupController.java
package com.example.demo.controller;

import org.springframework.context.MessageSource;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.example.demo.constant.MessageConst;
import com.example.demo.form.LoginForm;
import com.example.demo.service.LoginService;
import com.example.demo.service.SignupService;
import com.example.demo.util.AppUtil;

import lombok.RequiredArgsConstructor;

//User registration screen Controller
@Controller
@RequiredArgsConstructor
public class SignupController {

    // User registration screen Service
    private final SignupService service;

    // MessageSource
    private final MessageSource messageSource;

    // Initial displa
    // @Param modelモデル
    // @Param form入力情報
    // return 表示画面

    @GetMapping("/signup")
    public String view(Model model, SignupForm form) {
        return "signup";
    }

    // User registration screen
    // @Param modelモデル
    // @Param form入力情報
    // return 表示画面

    @PostMapping("/signup")
    public void signup(Model model, SignupForm form) {
        var userInfoOpt = service.resistUserInfo(form);
        var message = AppUtil.getMessage(messageSource, judgeMessagekey(userInfoOpt));
        model.addAttribute("message", message);

    }

    private String judgeMessagekey(Optinal<UserInfo> userInfoOpt) {
        if (userInfoOpt.isEmpty()) {
            return MessageConst.SIGNUP_EXISTED_LOGIN_ID;
        } else {
            return MessageConst.SIGNUP_RESIST_SUCCEED;
        }
    }
}

③ Edit constant/ErrorMessageConst.java

constant/ErrorMessageConst.java
package com.example.demo.constant;

//Error message ID constant class
public class MessageConst {
    // Incorrect input on login screen
    public static final String LOGIN_WRONG_INPUT = "login.wrongInput";
    // User registration screen: Already registered login ID
    public static final String SIGNUP_EXISTED_LOGIN_ID = "signup.extendLoginId";
    // User registration screen:User registration completed
    public static final String SIGNUP_RESIST_SUCCEED = "signup.resistSucceed";
}

④ constant/ErrorMessageConst.java ⇨ Change to constant/MessageConst.java

⑤ Edit controller/SignupController.java

controller/SignupController.java
package com.example.demo.controller;

import org.springframework.context.MessageSource;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.example.demo.constant.MessageConst;
import com.example.demo.form.LoginForm;
import com.example.demo.service.LoginService;
import com.example.demo.service.SignupService;
import com.example.demo.util.AppUtil;

import lombok.RequiredArgsConstructor;

//User registration screen Controller
@Controller
@RequiredArgsConstructor
public class SignupController {

    // User registration screen Service
    private final SignupService service;

    // MessageSource
    private final MessageSource messageSource;

    // Initial displa
    // @Param modelモデル
    // @Param form入力情報
    // return 表示画面

    @GetMapping("/signup")
    public String view(Model model, SignupForm form) {
        return "signup";
    }

    // User registration screen
    // @Param modelモデル
    // @Param form入力情報
    // return 表示画面

    @PostMapping("/signup")
    public void signup(Model model, SignupForm form) {
        var userInfoOpt = service.resistUserInfo(form);
        var message = AppUtil.getMessage(messageSource, judgeMessagekey(userInfoOpt));
        model.addAttribute("message", message);

    }

    private String judgeMessagekey(Optinal<UserInfo> userInfoOpt) {
        if (userInfoOpt.isEmpty()) {
            return MessageConst.SIGNUP_EXISTED_LOGIN_ID;
        } else {
            return MessageConst.SIGNUP_RESIST_SUCCEED;
        }
    }
}

(9) Arrange the screen layout using Bootstrap
Bootstrap - org.webjars
① Introducing Bootstrap to pom.xml

pom.xml
<!-- https://mvnrepository.com/artifact/org.webjars/jquery -->
       <dependency>
      <groupId>org.webjars</groupId>
       <artifactId>jquery</artifactId>
       <version>3.7.1</version>
    </dependency>

		<!-- https://mvnrepository.com/artifact/org.webjars/bootstrap -->
        <dependency>
         <groupId>org.webjars</groupId>
         <artifactId>bootstrap</artifactId>
        <version>5.3.2</version>
        </dependency>

② Add Boostrap code to login.html

login.html


Reference site

【Spring Bootで作るWebアプリ | part3】プログラムの実行環境を作成しよう!【初学者向け】
【Spring Bootで作るWebアプリ | part4】画面を表示してみよう!【初学者向け】
【Spring Bootで作るWebアプリ | part4 補足】GetMappingの別実装方法について【初学者向け】
【Spring Bootで作るWebアプリ | part5】ログイン画面の入力値を受け取る!【初学者向け】
【Spring Bootで作るWebアプリ | part6】ログイン成功時、失敗時の処理を実装する!【初学者向け】
【Spring Bootで作るWebアプリ | part7】開発用PCにデータベースを構築する!【初学者向け】
【Spring Bootで作るWebアプリ | part9】ソースコードにコメントを書いてみよう!【初学者向け】
【Spring Bootで作るWebアプリ | part10】ハッシュ化したパスワードの比較処理を実装する!【初学者向け】
【Spring Bootで作るWebアプリ | part11】エラーメッセージをプロパティファイルで管理する【初学者向け】
【Spring Bootで作るWebアプリ | part12】DBのユーザー情報テーブルへ登録を行う処理を実装する!【初学者向け】
【Spring Bootで作るWebアプリ | part13】テーブルデータ登録前のキー重複チェックを実装する!【初学者向け】
【Spring Bootで作るWebアプリ | part14】Bootstrapを使って画面レイアウトを整えよう!【初学者向け】

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?