403エラーを直したい
Q&A
Closed
SpringSecurityでユーザー登録ボタンを押すと403エラーが出る
意見交換
Java
spring-security
springframework
SpringBoot
登録ボタンを押したら403エラーが出るところを解決したい
今年初めてjavaに触れてChatGPTを使いながらやりくりしていたのですがとうとう困ってしまいました。
有識者の方や分かるかたいたら助けてほしいです。
例)
卒業研究で作成中のWebポータルアプリがSpringsecurityを使ってかあらエラーばかりで今は、ユーザー追加で困っています。
解決方法を教えて下さい。
発生している問題・エラー
エラーが発生しました
予期しないエラーが発生しました。もう一度お試しください。
ステータス: 403
または、問題・エラーが起きている画像をここにドラッグアンドドロップ
該当するソースコード
AdduserController.java
package com.example.demo.controller;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
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.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.example.demo.service.AddUserService;
@Controller
public class AddUserController {
private final AddUserService userAddService;
public AddUserController(AddUserService userAddService) {
this.userAddService = userAddService;
}
// ログインユーザーの役職に基づいてビューを表示
@GetMapping("/adduser")
public String view(Model model, Authentication authentication) {
if (authentication != null && authentication.isAuthenticated()) {
User user = (User) authentication.getPrincipal();
boolean isAdmin = user.getAuthorities().stream()
.anyMatch(authority -> authority.getAuthority().equals("ROLE_管理者"));
model.addAttribute("isAdmin", isAdmin);
}
return "adduser";
}
// ユーザー追加処理
@PostMapping("/adduser")
public String submitUser(
@RequestParam @NotNull Long userId,
@RequestParam @NotBlank String name,
@RequestParam @Email String email,
@RequestParam @NotNull String password,
@RequestParam @NotNull String role,
Authentication authentication,
RedirectAttributes redirectAttributes) {
if (authentication == null || !authentication.isAuthenticated()) {
redirectAttributes.addFlashAttribute("error", "ログインしていません。");
return "redirect:/login"; // 認証されていない場合のリダイレクト
}
// ログインユーザーが管理者か確認
User user = (User) authentication.getPrincipal();
boolean isAdmin = user.getAuthorities().stream()
.anyMatch(authority -> authority.getAuthority().equals("ROLE_管理者"));
// 管理者でない場合、アクセスを拒否
if (!isAdmin) {
redirectAttributes.addFlashAttribute("error", "管理者権限が必要です。");
return "redirect:/login"; // リダイレクト先が適切か確認
}
try {
// ユーザー情報を保存
userAddService.addUser(userId, name, email, password, role);
// フラッシュスコープに成功メッセージを追加
redirectAttributes.addFlashAttribute("message", "ユーザーを追加しました!");
return "redirect:/userinfo"; // リダイレクト先が正しいか確認
} catch (IllegalArgumentException e) {
redirectAttributes.addFlashAttribute("error", "不正な入力です。再度確認してください。");
return "redirect:/adduser"; // 再度フォームページにリダイレクト
} catch (Exception e) {
redirectAttributes.addFlashAttribute("error", "ユーザーの追加に失敗しました。もう一度お試しください。");
return "redirect:/adduser"; // 再度フォームページにリダイレクト
}
}
}
SecurityConfig.java
package com.example.demo.config;
import java.io.IOException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import com.example.demo.constant.UrlConst;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final String USERNAME_PARAMETER = "loginId";
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize ->
authorize
// ログインページや認証なしページを公開
.requestMatchers(UrlConst.NO_AUTHENTICATION).permitAll()
// 管理者専用のページを制限
.requestMatchers(UrlConst.AUTHENTICATION_REQUIRED).hasAuthority("ROLE_管理者")
// /adduserへのPOSTリクエストは管理者のみアクセス許可
.requestMatchers(HttpMethod.POST, UrlConst.ADD_USER).hasAuthority("ROLE_管理者") // POST は管理者専用
.anyRequest().authenticated() // 他のリクエストには認証を要求
)
.formLogin(login -> login
.loginPage(UrlConst.LOGIN)
.usernameParameter(USERNAME_PARAMETER)
.defaultSuccessUrl(UrlConst.HOME, true) // ログイン成功後のリダイレクト先
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
// ロール情報を確認して出力
authentication.getAuthorities().forEach(authority -> {
System.out.println("Role from Security: " + authority.getAuthority());
});
// ロールが管理者かどうかを確認
boolean isAdmin = authentication.getAuthorities().stream()
.anyMatch(authority -> authority.getAuthority().equals("ROLE_管理者"));
// ログを追加して確認
if (isAdmin) {
System.out.println("User has ROLE_管理者, redirecting to admin page.");
response.sendRedirect(UrlConst.ADMIN); // 管理者専用ページにリダイレクト
} else {
System.out.println("User does not have ROLE_管理者, redirecting to home.");
response.sendRedirect(UrlConst.HOME); // 一般ユーザー用ホームページにリダイレクト
}
}
})
);
return http.build();
}
}
UrlConst.java
package com.example.demo.constant;
public class UrlConst {
public static final String LOGIN = "/login";
public static final String HOME = "/home";
public static final String ADMIN = "/admin";
public static final String ADD_USER = "/adduser"; // ここで定義
public static final String[] NO_AUTHENTICATION = {LOGIN};
public static final String[] AUTHENTICATION_REQUIRED = {"/admin", "/adduser", "/post-announcement", "/addcompany"};
}
adduser.html
管理者ユーザー管理画面<!-- Bootstrap CSS -->
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
<style>
/* ヒーローセクション */
.hero-section {
background: rgb(147, 0, 73);
color: white;
padding: 80px 0 30px;
text-align: center;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
.hero-section h1 {
font-size: 3.5rem;
font-weight: 700;
margin-bottom: 15px;
}
.hero-section p {
font-size: 1.5rem;
margin-bottom: 30px;
}
footer {
background-color: rgb(147, 0, 73);
color: white;
padding: 25px 0;
text-align: center;
margin-top: 50px; /* フッターの余白を増加 */
}
/* 管理者・ユーザー登録フォーム */
.form-section {
margin-top: 50px;
}
.form-section h2 {
font-size: 2rem;
font-weight: 600;
margin-bottom: 30px;
}
.form-group {
margin-bottom: 25px;
}
.form-control {
border-radius: 0.375rem;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);
}
.form-control:focus {
border-color: rgb(147, 0, 73);
box-shadow: 0 0 5px rgba(147, 0, 73, 0.7);
}
/* ドラッグアンドドロップエリア */
.drop-area {
width: 100%;
height: 200px;
border: 2px dashed #007bff;
text-align: center;
line-height: 180px;
color: #007bff;
margin-bottom: 30px;
border-radius: 8px;
background-color: #f9f9f9;
transition: background-color 0.3s ease;
}
.drop-area:hover {
background-color: #e6f7ff;
cursor: pointer;
}
.drop-area.drag-over {
background-color: #d1e7ff;
}
.btn {
border-radius: 0.375rem;
font-weight: 600;
padding: 12px 20px;
}
/* レスポンシブ対応 */
@media (max-width: 768px) {
.hero-section h1 {
font-size: 2.5rem;
}
.form-section h2 {
font-size: 1.5rem;
}
.btn {
width: 100%;
}
}
</style>
<!-- ヒーローセクション -->
<div class="hero-section">
<h1>管理者 - ユーザー管理</h1>
<p>全ユーザーの情報を登録することができます。</p>
</div>
<!-- メインコンテンツ -->
<div class="container">
<!-- メッセージ表示 -->
<div th:if="${message}" class="alert alert-success">
<p th:text="${message}"></p>
</div>
<div th:if="${error}" class="alert alert-danger">
<p th:text="${error}"></p>
</div>
<!-- ユーザー登録フォーム -->
<div class="form-section">
<h2>ユーザー登録</h2>
<form id="admin-form" action="/adduser" method="post">
<div class="form-group">
<label for="userId">ログインID</label>
<input type="text" class="form-control"id="userId" th:name="userId" placeholder="ログインID" required>
</div>
<div class="form-group">
<label for="name">名前</label>
<input type="text" class="form-control" id="name" th:name="name" placeholder="ユーザーの名前" required>
</div>
<div class="form-group">
<label for="email">メールアドレス</label>
<input type="email" class="form-control" id="email" th:name="email" placeholder="ユーザーのメールアドレス(任意)">
</div>
<div class="form-group">
<label for="password">パスワード</label>
<input type="password" class="form-control" id="password"th:name="password" placeholder="ユーザーのパスワード" required>
</div>
<div class="form-group">
<label for="role">役割</label>
<div>
<label><input type="radio" name="role" value="管理者" required> 管理者</label>
<label class="ml-3"><input type="radio" name="role" value="一般" required> 一般ユーザー</label>
</div>
</div>
<button type="submit" class="btn btn-primary" th:if="${isAdmin}" th:text="'登録'">登録</button>
</form>
</div>
<!-- Excelファイルのアップロード -->
<div class="form-section">
<h2>ユーザー登録(Excelファイルのドラッグアンドドロップ)</h2>
<div id="drag-area" class="drop-area">
<p>Excelファイルをここにドラッグアンドドロップしてください。</p>
<p>または、クリックしてファイルを選択</p>
<input type="file" id="file-input" style="display:none;">
</div>
<button id="upload-button" class="btn btn-success mt-3" disabled>アップロード</button>
</div>
</div>
<!-- フッター -->
<footer>
<p>© 2024 ポータルサイト. All rights reserved.</p>
</footer>
<!-- スクリプト -->
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.2/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<script>
// ドラッグアンドドロップ処理
var dropArea = document.getElementById('drag-area');
dropArea.addEventListener('dragover', function(event) {
event.preventDefault();
dropArea.classList.add('drag-over');
});
dropArea.addEventListener('dragleave', function() {
dropArea.classList.remove('drag-over');
});
dropArea.addEventListener('drop', function(event) {
event.preventDefault();
dropArea.classList.remove('drag-over');
var files = event.dataTransfer.files;
if (files.length > 0) {
document.getElementById('file-input').files = files;
document.getElementById('upload-button').disabled = false;
}
});
// ファイル選択ダイアログを表示
document.getElementById('drag-area').addEventListener('click', function() {
document.getElementById('file-input').click();
});
// ファイル選択時の処理
document.getElementById('file-input').addEventListener('change', function(event) {
if (event.target.files.length > 0) {
document.getElementById('upload-button').disabled = false;
}
});
// アップロード処理
document.getElementById('upload-button').addEventListener('click', function() {
var file = document.getElementById('file-input').files[0];
if (file) {
var formData = new FormData();
formData.append('file', file);
fetch('/upload', {
method: 'POST',
body: formData
})
.then(response => {
if (!response.ok) throw new Error('ファイルアップロードに失敗しました');
return response.json();
})
.then(data => alert('ファイルがアップロードされました'))
.catch(error => alert(error.message));
}
});
</script>