Spring Boot にユーザー認証機能を追加する手順書
この手順書では、Spring Boot アプリケーションに「ユーザー登録」「ログイン」「ログイン状態の取得」機能を追加する方法を初心者向けに説明します。
1. 目的
- ユーザーが登録できるようにする
- ログインしてセッション管理できるようにする
- ログインしているユーザーの情報を取得できるようにする
2. 依存関係の追加
build.gradle に Spring Security などの依存関係を追加します。
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security' ←これを追加する
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
…
}
3. ユーザーエンティティの作成・拡張
User.java に UserDetails を実装して、Spring Security に渡すユーザー情報をカスタマイズします。
package com.example.simple_crud_spring.model;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import jakarta.persistence.*;
import java.util.Collection;
import java.util.List;
@Entity
@Table(name = "users")
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String email;
@Column(nullable = false)
private String password;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of(() -> "ROLE_USER");
}
@Override
public String getUsername() {
return email;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
4. リポジトリ作成
メールアドレスでユーザーを取得できるように、UserRepository を定義します。
package com.example.simple_crud_spring.repository;
import com.example.simple_crud_spring.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
}
5. サービスの作成
UserDetailsService を実装することで、Spring Security がログイン時にユーザーを見つけてくれます。
package com.example.simple_crud_spring.service;
import com.example.simple_crud_spring.model.User;
import com.example.simple_crud_spring.repository.UserRepository;
import org.springframework.security.core.userdetails.*;
import org.springframework.stereotype.Service;
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
return userRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
}
}
6. セキュリティ設定
CORS とログインの設定を行い、フロントエンドからアクセスできるようにします。
package com.example.simple_crud_spring.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.security.config.Customizer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("http://localhost:3000")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true);
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors(Customizer.withDefaults()) // ← CORS許可を有効にする
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.anyRequest().permitAll())
.formLogin(form -> form
.loginProcessingUrl("/login")
.successHandler((req, res, auth) -> res.setStatus(200))
.failureHandler((req, res, ex) -> res.setStatus(401)))
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessHandler((req, res, auth) -> res.setStatus(200)));
return http.build();
}
}
7. パスワードエンコーダーの定義
BCrypt を使うためのエンコーダーを Bean 定義します。
package com.example.simple_crud_spring.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 AppConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
8. ユーザー API の作成
ユーザー登録と、ログイン中のユーザー情報取得を行うコントローラーです。
package com.example.simple_crud_spring.controller;
import com.example.simple_crud_spring.model.User;
import com.example.simple_crud_spring.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
@Autowired
public UserController(UserRepository userRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
// ① ユーザー作成
@PostMapping("/register")
public User register(@RequestBody User user) {
user.setPassword(passwordEncoder.encode(user.getPassword())); // パスワードをハッシュ化
return userRepository.save(user);
}
// ② ログインは Spring Security の機能(/login)に任せるので特別なコードは不要
// ③ ログインしているユーザー情報の取得
@GetMapping("/me")
public Object getCurrentUser(Authentication authentication) {
if (authentication == null || !authentication.isAuthenticated()) {
return null;
}
return (User) authentication.getPrincipal();
}
}
9. PostController の変更(必要に応じて)
投稿者を特定するような箇所では Authentication からログインユーザーを取得できます。
package com.example.simple_crud_spring.controller;
import com.example.simple_crud_spring.model.Post;
import com.example.simple_crud_spring.repository.PostRepository;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/posts")
public class PostController {
private final PostRepository postRepository;
public PostController(PostRepository postRepository) {
this.postRepository = postRepository;
}
@GetMapping("/all")
public List<Post> getAll() {
System.out.println("取得開始");
List<Post> allPosts;
try {
allPosts = postRepository.findAll();
System.out.println("取得成功: " + allPosts.size() + " 件");
} catch (Exception e) {
System.out.println("エラー発生: " + e.getMessage());
e.printStackTrace(); // これが一番重要です♡
return List.of(); // 空リスト返して回避
}
return allPosts;
}
@PostMapping
public Post create(@RequestBody Post post) {
return postRepository.save(post);
}
@GetMapping("/{id}")
public Post getById(@PathVariable Long id) {
return postRepository.findById(id).orElse(null);
}
@PutMapping("/{id}")
public Post update(@PathVariable Long id, @RequestBody Post updatedPost) {
return postRepository.findById(id)
.map(post -> {
post.setTitle(updatedPost.getTitle());
post.setContent(updatedPost.getContent());
post.setUserId(updatedPost.getUserId());
return postRepository.save(post);
})
.orElse(null);
}
@DeleteMapping("/{id}")
public void delete(@PathVariable Long id) {
postRepository.deleteById(id);
}
}
10. 動作確認
1. POST /api/users/register で新規登録
2. POST /login でログイン(Content-Type: application/x-www-form-urlencoded)
3. GET /api/users/me でログイン中のユーザー確認
4. Cookie にセッション ID がついていることを確認
以上が、Spring Boot 側のユーザー認証機能追加の手順です。