[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でなんか作るかー。