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?

16.SpringBootのユーザー認証の作り方

Posted at

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 側のユーザー認証機能追加の手順です。

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?