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
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.
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
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
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>TOPmenu</title>
</head>
<body></body>
</html>
② Create a controller folder and MenuController file
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
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
<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
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
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
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
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
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
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
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
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
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
<!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
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
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
<!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
```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
<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
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
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
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
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
<!-- 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
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を使って画面レイアウトを整えよう!【初学者向け】