見出し画像

ReactからSpring Bootにアクセス確認する時にSpring Securityにつまづいた話

要約

この記事では、前回作成した環境を用いて、フロントエンド(React)からバックエンド(Spring Boot)にアクセスする際に発生した問題と、その解決方法について説明します。特に、Spring Securityによる認証に関する問題とCORS設定の方法を詳細に記載します。

概要

前回作成した環境で、フロントエンド(React)からバックエンド(Spring Boot)にアクセスしようとした際に、Spring Securityに阻まれてしまいました。この記事は、その解決方法を備忘録として記載したものです。

環境

・react: 18.3.1

・react-router-dom: 6.24.1
・springboot: 3.3.1
・Spring Security: 6.3.1

やりたいこと

・CORSの設定
・フロントエンドからバックエンドへのアクセス

つまったこと

・Spring Securityの認証を対象外にする方法

コード例

バックエンド
DemoApplication.java

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@SpringBootApplication
public class DemoApplication {

  public static void main(String[] args) { 
    SpringApplication.run(DemoApplication.class, args);
  }  

  // この箇所でCORSの設定
  @Bean 
  public WebMvcConfigurer corsConfigurer() {
     return new WebMvcConfigurer() {
         @Override
         public void addCorsMappings(CorsRegistry registry) {
             registry.addMapping("/**").allowedOrigins("http://localhost:3000");
         }
     };
  }
}

HelloController.java

package com.example.demo;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
  @GetMapping("/hello")
  public Message getMessage() {
    return new Message("Hello, World!");
}

  public static class Message {
    private String message;

    public Message(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
  }
}

フロントエンド
App.tsx

import React, { useEffect, useState } from "react";
import "./App.css";

const App: React.FC = () => {
  const [data, setData] = useState<{ message: string } | null>(null);

  useEffect(() => {
    fetch("http://localhost:8080/hello")
      .then((response) => response.json())
      .then((data) => setData(data))
      .catch((error) => console.error("Error fetching data:", error));
  }, []);

  return (
    <div className="App">
      <header className="App-header">
        <h1>Data from Backend</h1>
        {data ? <pre>{JSON.stringify(data, null, 2)}</pre> : <p>Loading...</p>}
      </header>
    </div>
  );
};

export default App;

期待値

http://localhost:8080/hello をフェッチすると、以下のようなJSONレスポンスが返されることを期待していました。

{"message":"Hello, World!"}

しかし、現状はリクエストに失敗しており下記のERRORがコンソール上に出力されていました。

問題

実際にリクエストを送ると、コンソールに以下のエラーメッセージが表示されました。

Failed to load resource: the server responded with a status of 401 ()

これは、Spring Securityによる認証ができていない事が原因でした。
(Backendにブラウザからリクエストして認証すればリクエスト可能であることを確かめた)

解決方法

参考サイト
問題解決のために、以下のサイトを参考にしました。

Spring Security 5.4〜6.0でセキュリティ設定の書き方が大幅に変わる件

上記でSpriing Securityの書き方が大きく変わっていることを知り、
ブログ内に記載されていた新しいバージョンのSecurityConfig.javaを参考に変更しました。

package com.example.demo;

import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.formLogin(login -> login
                .loginProcessingUrl("/login")
                .loginPage("/login")
                .defaultSuccessUrl("/")
                .failureUrl("/login?error")
                .permitAll()
        ).logout(logout -> logout
                .logoutSuccessUrl("/")
        ).authorizeHttpRequests(authz -> authz
                .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
                .requestMatchers("/","/hello").permitAll() // ここに認証しないリストを追加
                .requestMatchers("/general").hasRole("GENERAL")
                .requestMatchers("/admin").hasRole("ADMIN")
                .anyRequest().authenticated()
        );
        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

上記の // ここに認証しないリストを追加 の部分に /hello を追加すると、バックエンドへのアクセスが可能になりました。

期待通りに画面が表示され、CORSの設定も問題なく完了しました。

まとめ

ReactからSpring Bootにアクセスする際、Spring Securityによる認証が障害となる場合があります。本記事では、その問題を解決するための手順を示しました。特に、CORS設定とSpring Securityの設定を適切に行うことで、フロントエンドとバックエンドのスムーズな連携を実現することができます。同様の問題に直面した際には、この記事を参考にして、適切な設定を行ってください。

んー!ファイト!!!!


この記事が気に入ったらサポートをしてみませんか?