[SpringBoot2.4.3] DB認証のログイン処理(※2回目)

### Entity
com.example.demo.account

package com.example.demo.account;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;

import javax.persistence.*;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.GrantedAuthority;

@Entity
@Table(name="accounts")
public class Account implements UserDetails{
	
	private static final long serialVersionUID = 1L;
	
	public enum Authority {ROLE_USER, ROLE_MANAGER, ROLE_ADMIN}
	
	@Id
	@Column(nullable = false, unique = true)
	private String username;
	
	@Column(nullable = false)
	private String password;
	
	@Column(nullable = false, unique = true)
	private String mailAddress;
	
	@Column(nullable = false)
	private boolean mailAddressVerified;
	
	@Column(nullable = false)
	private boolean enabled;
	
	@Temporal(TemporalType.TIMESTAMP)
	private Date createdAt;
	
	@ElementCollection(fetch = FetchType.EAGER)
	@Enumerated(EnumType.STRING)
	@Column(nullable = false)
	private Set<Authority> authorities;
	
	protected Account() {}
	
	public Account(String username, String password, String mailAddress) {
		this.username = username;
		this.password = password;
		this.mailAddress = mailAddress;
		this.mailAddressVerified = false;
		this.enabled = true;
		this.authorities = EnumSet.of(Authority.ROLE_USER);
	}
	
	@PrePersist
	public void prePersist() {
		this.createdAt = new Date();
	}
	
	public boolean isAdmin() {
		return this.authorities.contains(Authority.ROLE_ADMIN);
	}
	
	public void setAdmin(boolean isAdmin) {
		if(isAdmin) {
			this.authorities.add(Authority.ROLE_MANAGER);
			this.authorities.add(Authority.ROLE_ADMIN);
		} else {
			this.authorities.remove(Authority.ROLE_ADMIN);
		}
	}
	
	public boolean isManager() {
		return this.authorities.contains(Authority.ROLE_MANAGER);
	}
	
	public void setManager(boolean isManager) {
		if(isManager) {
			this.authorities.add(Authority.ROLE_MANAGER);
		} else {
			this.authorities.remove(Authority.ROLE_MANAGER);
			this.authorities.remove(Authority.ROLE_ADMIN);
		}
	}

	@Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (Authority authority : this.authorities) {
            authorities.add(new SimpleGrantedAuthority(authority.toString()));
        }
        return authorities;
    }
    
    @Override
    public boolean isAccountNonExpired() {
    	return true;
    }
    
    @Override
    public boolean isAccountNonLocked() {
    	return true;
    }
    
    @Override
    public boolean isCredentialsNonExpired() {
    	return true;
    }
    
    @Override
    public String getUsername() {
    	return username;
    }
    
    public void setUsername(String username) {
    	this.username = username;
    }
    
    @Override
    public String getPassword() {
    	return password;
    }
    
    public void setPassword(String password) {
    	this.password = password;
    }
    
    @Override
    public boolean isEnabled() {
    	return this.enabled;
    }
    
    public void setEnabled(boolean enabled) {
    	this.enabled = enabled;
    }
    
    public String getMailAddress() {
    	return mailAddress;
    }
    public void setMailAddress(String mailAddress) {
    	this.mailAddress = mailAddress;
    }
    public boolean isMailAddressVerified() {
    	return mailAddressVerified;
    }
    public void setMailAddressVerified(boolean mailAddressVerified) {
    	this.mailAddressVerified = mailAddressVerified;
    }
    public Date getCreatedAt() {
    	return createdAt;
    }	
}

Repository

package com.example.demo.account;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface AccountRepository extends CrudRepository<Account, Long>{
	public Account findByUsername(String username);
}

Service

package com.example.demo.account;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class AccountService implements UserDetailsService {
	
	@Autowired
	private AccountRepository repository;
	
	@Autowired
	private PasswordEncoder passwordEncoder;
	
	@Override
	public Account loadUserByUsername(String username) throws UsernameNotFoundException {
		if(username == null || "".equals(username)) {
			throw new UsernameNotFoundException("Username is empty");
		}
		
		Account user = repository.findByUsername(username);
		if(user == null) {
			throw new UsernameNotFoundException("User not found: " + username);
		}
		return user;
	}
	
	@Transactional
	public void registerAdmin(String username, String password, String mailAddress) {
		Account user = new Account(username, passwordEncoder.encode(password), mailAddress);
		user.setAdmin(true);
		repository.save(user);
	}
	
	@Transactional
	public void registerManager(String username, String password, String mailAddress) {
		Account user = new Account(username, passwordEncoder.encode(password), mailAddress);
		user.setManager(true);
		repository.save(user);
	}
	
	@Transactional
	public void registerUser(String username, String password, String mailAddress) {
		Account user = new Account(username, passwordEncoder.encode(password), mailAddress);
		repository.save(user);
	}
}

AuthoController.java

package com.example.demo.account;

import org.springframework.beans.factory.annotation.Autowired;
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 org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class AuthController {
	
	@RequestMapping("/")
	public String index() {
		return "redirect:/top";
	}
	
	@GetMapping("/login")
	public String login() {
		return "login";
	}
	
	@PostMapping("/login")
	public String loginPost() {
		return "redirect:/login-error";
	}
	
	@GetMapping("/login-error")
	public String loginError(Model model) {
		model.addAttribute("loginError", true);
		return "login";
	}
	
	@RequestMapping("/top")
	public String top() {
		return "/top";
	}
}

WebSecurityConfig.java

package com.example.demo;

import com.example.demo.account.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	private AccountService userService;
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
				.authorizeRequests()
				.antMatchers("/login", "/login-error").permitAll()
				.antMatchers("/**").hasRole("USER")
				.and()
				.formLogin()
				.loginPage("/login").failureUrl("/login-error");
		
	}
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth
				.userDetailsService(userService)
				.passwordEncoder(passwordEncoder());
		userService.registerAdmin("admin", "secret", "admin@localhost");
	}
	
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
}

login.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="utf-8">
	<title>Login page</title>
    <style>
.alert-danger{color:red;}
</style>
</head>
<body>
	<h2>ログイン画面</h2>
	<form th:action="@{/login}" method="post">
		<div th:if="${session['SPRING_SECURITY_LAST_EXCEPTION']} != null" class="alert-danger">
		<span th:text="ユーザ名またはパスワードに誤りがあります"></span>
		</div>
		<div style="width:160px;"><label for="username">ユーザ名:</label></div>
		<input type="text" name="username" autofocus="autofocus">
		<br>
		<div style="width:160px;"><label for="password">パスワード:</label></div>
		<input type="password" name="password" />
		<br>
		<p><input type="submit" value="ログイン"></p>
</body>
</html>

top.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
	<meta charset="UTF-8" />
	<title>メニュー画面</title>
</head>
<body>
	<h2>ログイン画面</h2>
	<div th:fragment="logout" sec:authorize="isAuthenticated()">
		<p>こんにちは:
			<span sec:authentication="name"></span>さん</p>
		<p>mail:
			<span sec:authentication="principal.mailAddress"></span></p>
		<p>権限:
			<span sec:authentication="principal.authorities"></span></p>
		<form action="#" th:action="@{/logout}" method="post">
			<input type="submit" value="ログアウト">
		</form>
	</div>
</body>
</html>

application.properties

spring.jpa.database=POSTGRESQL
spring.datasource.url=jdbc:postgresql://localhost:5432/test
spring.datasource.username=root
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

hibernate.properties

hibernate.jdbc.lob.non_contextual_creation = true

test=> select * from accounts;
username | created_at | enabled | mail_address | mail_address_verified | password
———-+————+———+—————–+———————–+————————————————————–
admin | | t | admin@localhost | f | $2a$10$19UBN8MoIHBihXXDs2uyreHU4DlAow7jNfUwtChyAw6pp5THYM8N.
(1 row)

あああああああああああ
AccountService.javaで登録してんのか。

@Transactional
public void registerAdmin(String username, String password, String mailAddress) {
Account user = new Account(username, passwordEncoder.encode(password), mailAddress);
user.setAdmin(true);
repository.save(user);
}

なるほど、
spring.jpa.hibernate.ddl-auto=update で、自動でテーブルが作成されんのか。。

insert into accounts (username, enabled, mail_address, mail_address_verified, password) values
(‘user1′,TRUE,’user1@gmail.com’, FALSE,’$2a$10$BuoyNf/vQZB5cS.eCEtW7uHtFTMq0SwLKXblcOeGCIJCpqeCnQH2S’),
(‘user2′,TRUE,’user2@gmail.com’, FALSE,’$2a$10$BuoyNf/vQZB5cS.eCEtW7uHtFTMq0SwLKXblcOeGCIJCpqeCnQH2S’);

insert into account_authorities (account_username, authorities) values
(‘user1′,’ROLE_USER’);


やべえええええええええええええ
Spring Securityできたやんか🔥🔥🔥🔥
他にやりたい事もあるけど、Javaでなんか作るかー。

[SpringBoot2.4.3] DB認証によるログイン処理を実装したい

WebSecurityConfig.java

package com.example.demo;

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.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
	
	@Bean
	PasswordEncoder passwordEncoder() {
		return NoOpPasswordEncoder.getInstance();
	}
	
	@Override
	public void configure(WebSecurity web) throws Exception {
		web.ignoring().antMatchers("/css/**", "/resources/**");
	}
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			.authorizeRequests()
				.antMatchers("/", "/home").permitAll()
				.anyRequest().authenticated()
				.and()
			.formLogin()
				.loginPage("/login")
				.loginProcessingUrl("/login")
				.usernameParameter("username")
				.passwordParameter("password")
				.successForwardUrl("/home")
				.failureUrl("/login?error")
				.permitAll()
				.and()
			.logout()
				.logoutUrl("/logout")
				.permitAll()
				.logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
	}
}

DemoController.java
L UserModelは後から作ります

package com.example.demo;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.security.core.Authentication;

@Controller
public class DemoController {
	@RequestMapping(value = {"/", "/home"})
	public String home() {
		return "home";
	}
	
	@RequestMapping(value = "/hello")
	public String hello(Authentication authentication, Model model) {
		UserModel userModel = (UserModel)authentication.getPrincipal();
		model.addAttribute("name", userModel.getUsername());
		
		return "hello";
	}
}

pom.xml
L jdbcとpostgres追加

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jdbc</artifactId>
		</dependency>
		
		<dependency>
			<groupId>org.postgresql</groupId>
			<artifactId>postgresql</artifactId>
			<scope>runtime</scope>
		</dependency>

application.properties
L jdbc, postgres追加

spring.jpa.database=POSTGRESQL
spring.datasource.url=jdbc:postgresql://localhost:5432/test
spring.datasource.username=root
spring.datasource.password=

$ psql -U root test
test=> \d

create table t_user (
	id serial primary key,
	name varchar(255),
	password varchar(255),
	enabled boolean DEFAULT true
);

insert into t_user (name, password) values
('user1','password'),
('user2','password');

select * from t_user;

UserModel.java

package com.example.demo;

import java.util.Collection;

import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.GrantedAuthority;

@NoArgsConstructor
@AllArgsConstructor
@Data
public class UserModel implements UserDetails{

	private String id;
	private String name;
	private String password;
	private boolean enabled;
	
	@Override
	public Collection<? extends GrantedAuthority> getAuthorities(){
		return null;
	}
	
	@Override
	public String getPassword() {
		return this.password;
	}
	
	@Override
	public String getUsername() {
		return this.name;
	}
	
	@Override
	public boolean isAccountNonExpired() {
		return true;
	}
	
	@Override
	public boolean isAccountNonLocked() {
		return true;
	}
	
	@Override
	public boolean isCredentialsNonExpired() {
		return true;
	}
	
	@Override
	public boolean isEnabled() {
		return this.enabled;
	}
}

UserRepository.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.UserRepository">
	<select id="selectByUser" parameterType="com.example.demo.UserRepository" resultType="com.example.demo.UserModel">
		SELECT id, name, password, enabled FROM t_user where id = #{id};
	</select>
</mapper>

UserRepository.java

package com.example.demo;

import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserRepository {
	public UserModel selectByUser(String username);
}

UserDetailsServiceImpl.java

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

@Component
public class UserDetailsServiceImpl implements UserDetailsService {
	
	@Autowired
	private UserRepository userRepository;
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		if(StringUtils.isEmpty(username)) throw new UsernameNotFoundException("");
		
		UserModel userModel = userRepository.selectByUser(username);
		
		if(userModel == null) throw new UsernameNotFoundException("");
		if(!userModel.isAccountNonExpired() || !userModel.isAccountNonLocked() || 
				!userModel.isCredentialsNonExpired() || !userModel.isEnabled())
			throw new UsernameNotFoundException("");
		return userModel;
	}

}

Description:

Field userRepository in com.example.demo.UserDetailsServiceImpl required a bean of type ‘com.example.demo.UserRepository’ that could not be found.

なんでや。。。なんでや。。。。
もう一回やるか、次ラストで。

[SpringBoot2.4.3] ログイン後のリダイレクト先指定

WebSecurityConfig.java
L .defaultSuccessUrl(“/test1/index”)でログイン後のURLを指定する

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			.authorizeRequests()
				.antMatchers("/","/home").permitAll()
				// 上記以外は認証が必要
				.anyRequest().authenticated()
				.and()
			.formLogin()
				.loginPage("/login")
				.permitAll()
				.defaultSuccessUrl("/test1/index")
				.and()
			.logout()
				.permitAll();
	}
	@Bean
	@Override
	public UserDetailsService userDetailsService() {
		UserDetails user =
				User.withDefaultPasswordEncoder()
					.username("user")
					.password("pass")
					.roles("USER")
					.build();
		return new InMemoryUserDetailsManager(user);
	}	
}

なるほど。
ログイン認証をpostgresでやりたいな。

CakePHPのControllerやRouteの書き方

/src/Model/Table/PostsTable.php

namespace App\Model\Table;

use Cake\ORM\Table;

class PostsTable extends Table
{
	public function initialize(array $config) : void {
		$this->addBehavior('Timestamp');
	}
}

/templates/Posts/index.php
L 拡張子はctpではなくphpにする
https://book.cakephp.org/4/ja/views.html

<h1>Blog Posts</h1>

<ul>
	<?php foreach($posts as $post) : ?>
		<li><?= h($post->title); ?></li>
	<?php endforeach;?>
</ul>

/src/Controller/PostsController.php

// /posts/index
// /(controller)/(action)/(options)

namespace App\Controller;

class PostsController extends AppController{

	public function index(){
		$posts = $this->Posts->find('all');
		$this->set('posts', $posts);
	}
}

config/routes.php

    $builder->connect('/', ['controller' => 'Posts', 'action' => 'index']);

controller

$posts = $this->Posts->find('all')->order(['title'=>'DESC'])->limit(2)->where(['title like'=> '%3']);

3系と4系だと、大分書き方が変わってるな。特にctpからphpに変わってるのは驚きだな。

CakePHPの環境構築

$ php -i | grep intl
$ sudo yum -y install php-intl
$ sudo vi /etc/php.d/intl.ini

extension=intl.so

$ php -i | grep intl
PHP Warning: Module ‘intl’ already loaded in Unknown on line 0
/etc/php.d/20-intl.ini,
/etc/php.d/intl.ini
intl
intl.default_locale => no value => no value
intl.error_level => 0 => 0
intl.use_exceptions => 0 => 0

### mysql
create database cakephp;
use cakephp;
create table posts (
id int unsigned auto_increment primary key,
title varchar(255),
body text,
created datetime default null,
modified datetime default null
);

insert into posts (title, body, created) values
(‘title 1’, ‘body 1’, now()),
(‘title 2’, ‘body 2’, now()),
(‘title 3’, ‘body 3’, now());

select * from posts;

### cake install
$ composer create-project –prefer-dist cakephp/app myapp

config/app_local.php

            'username' => 'root',
            'password' => 'hoge',

            'database' => 'cakephp',

config/app.php

        'defaultLocale' => env('APP_DEFAULT_LOCALE', 'ja_JP'),
        'defaultTimezone' => env('APP_DEFAULT_TIMEZONE', 'Asia/Tokyo'),

$ bin/cake bake all posts
$ bin/cake server -H 192.168.33.10 -p 8000
http://192.168.33.10:8000/posts

あああああああああ、ちょっと思い出してきたー

[MySQLデータ移行]エクスポートとインポート

$ uname -a
Linux localhost 4.14.214-160.339.amzn2.x86_64 #1 SMP Sun Jan 10 05:53:05 UTC 2021 x86_64 x86_64 x86_64 GNU/Linu
$ mysql –version
mysql Ver 8.0.23 for Linux on x86_64 (MySQL Community Server – GPL)

$ mysql -u root -p
mysql> show databases;
mysql> use nonemail
mysql> show tables;
+————————+
| Tables_in_nonemail |
+————————+
| failed_jobs |
| migrations |
| password_resets |
| personal_access_tokens |
| sessions |
| users |
+————————+
mysql> select * from users;
-> usersテーブルに4件データが入っています。

### mysqlのデータエクスポート
$ mysqldump -u root -p –opt nonemail > database_name.sql
$ ls
database_name.sql

dumpファイルの中身
-> sql文が入っている

### mysqlのdumpファイルからデータインポート
$ ls
database_name.sql
$ mysql -u root -p newdatabase < database_name.sql Enter password: ERROR 1049 (42000): Unknown database 'newdatabase' -> ん? dbを作ってからでないとダメ??

mysql> create database newdatabase;
Query OK, 1 row affected (0.01 sec)
$ mysql -u root -p newdatabase < database_name.sql Enter password: mysql> use newdatabase;
mysql> show tables;
+————————+
| Tables_in_newdatabase |
+————————+
| failed_jobs |
| migrations |
| password_resets |
| personal_access_tokens |
| sessions |
| users |
+————————+
6 rows in set (0.00 sec)
mysql> select * from users;

ちゃんとデータも入ってます^^

なるほど、mysqlのデータ移行の方法は理解した。

[SpringBoot2.4.3] uploadを任意の場所にファイルを保存

Controller.java
L new FileOutputStream(${path})で、保存場所を指定する

	@RequestMapping(value="upload", method=RequestMethod.POST)
    public void handle(
            HttpServletResponse response,
            @RequestParam MultipartFile file
            ) {
        if(file.isEmpty()) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }
        try {
            BufferedInputStream in = new BufferedInputStream(file.getInputStream());
            BufferedOutputStream out = new BufferedOutputStream(
                    new FileOutputStream("./target/" + file.getOriginalFilename()));
        } catch (IOException e) {
            throw new RuntimeException("Error uploading file.", e);
        }
    }

なるほど、resourcesのstaticの中に置くこともできますね。

new FileOutputStream(“./src/main/resources/static/file/” + file.getOriginalFilename())

### filenameをreturn
res.getWriter().write(file.getOriginalFilename());

OK、後は基本機能としてはログインのみなんだよなー

[SpringBoot2.4.3] ファイルアップロードのcontroller

import javax.servlet.http.HttpServletResponse;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

@Controller
@RequestMapping("/contract")
public class ContractController {

	@GetMapping("input")
	public String contract() {
		return "contract/input";
	}
	
	@RequestMapping(value="upload", method=RequestMethod.POST)
    public void handle(
            HttpServletResponse response,
            @RequestParam MultipartFile file
            ) {
        if(file.isEmpty()) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }
        try {
            BufferedInputStream in = new BufferedInputStream(file.getInputStream());
            BufferedOutputStream out = new BufferedOutputStream(
                    new FileOutputStream(file.getOriginalFilename()));
        } catch (IOException e) {
            throw new RuntimeException("Error uploading file.", e);
        }
    }
}

html

<form action="/contract/upload" method="post" enctype="multipart/form-data">
    <div class="form-group">
    <div class="custom-file">
      <input type="file" name="file" class="custom-file-input" id="inputFile">
    </div>
  </div>
  <br>
<button type="submit" class="btn btn-primary" id="upload" value="upload">登録</button>	
</form>

BufferedInputStreamとBufferedOutputStreamの使い方がイマイチよくわからんな。。

[SpringBoot2.4.3] ファイルアップロード機能を作る

まず、webpackで簡単にフロントを作成します

続いて、そのまま、src/main/resources/templates/contract/input.html に流し込みます。

<form action="/upload" enctype="multipart/form-data">
    <div class="form-group">
    <div class="custom-file">
      <input type="file" name="file" class="custom-file-input" id="inputFile">
      <!-- <label class="custom-file-label" for="inputFile"></label> -->
    </div>
  </div>
  <br>
<button type="button" class="btn btn-primary">登録</button>	
</form>

続いてControllerを作ります。適当にContractController.javaとします。

package com.example.demo;

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

@Controller
@RequestMapping("/contract")
public class ContractController {

	@GetMapping("input")
	public String contract() {
		return "contract/input";
	}
}

ここからfile保存を実装したい。。。

[SpringBoot2.4.3] Service

package com.example.demo;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

import org.springframework.stereotype.Service;

@Service
public class MyDataService {
	
	@PersistenceContext
	private EntityManager entityManager;
	
	@SuppressWarnings("unchecked")
	public List<MyData> getAll(){
		return (List<MyData>) entityManager
				.createQuery("from MyData").getResultList();
	}
	
	public MyData get(int num) {
		return (MyData)entityManager
				.createQuery("from MyData where id = " + num)
				.getSingleResult();
	}
	
	public List<MyData> find(String fstr){
		CriteriaBuilder builder = entityManager.getCriteriaBuilder();
		CriteriaQuery<MyData> query = builder.createQuery(MyData.class);
		Root<MyData> root = query.from(MyData.class);
		query.select(root).where(builder.equal(root.get("name"), fstr));
		List<MyData> list = null;
		list = (List<MyData>) entityManager.createQuery(query).getResultList();
		return list;
	}
}