πPOST 403 (Forbidden)
403 Forbidden μ€λ₯λ μμ²μ΄ μλ²μ μν΄ κ±°λΆλμμμ λνλ λλ€. μ΄κ²μ μ£Όλ‘ κΆνμ΄ μλ κ²½μ° λ°μν©λλ€.
μ΄λ¬ν κ²½μ°κ° λ°μνλ μ΄μ λ μ¬λ¬ κ°μ§κ° μμ μ μμ§λ§, μ£Όλ‘ λ€μκ³Ό κ°μ μ΄μ κ° μμ΅λλ€:
- CSRF(Cross-Site Request Forgery) ν ν°μ΄ λλ½λμκ±°λ μλͺ»λ κ²½μ°: μλ²μμ CSRF 보νΈλ₯Ό μν΄ μμ² ν€λμ CSRF ν ν°μ ν¬ν¨ν΄μΌ νλ κ²½μ°κ° μμ΅λλ€. AJAX μμ²μ λ³΄λΌ λ μ΄ ν ν°μ ν¨κ» 보λ΄λ κ²μ νμΈνμΈμ.
- μ¬μ©μμ κΆνμ΄ μλ κ²½μ°: μλ²κ° μμ²μ μ²λ¦¬ν κΆνμ΄ μλ κ²½μ° λ°μν μ μμ΅λλ€. λ‘κ·ΈμΈμ΄ νμν μλΉμ€μΈ κ²½μ°, μ¬μ©μκ° μ¬λ°λ₯΄κ² μΈμ¦λμλμ§ νμΈνμΈμ.
- μλ²μμ μμ²μ κ±°λΆνλ κ·μΉ λλ μ μ± μ΄ μλ κ²½μ°: μλ² μΈ‘μμ μμ²μ κ±°λΆνλ κ·μΉ λλ μ μ± μ΄ μλ κ²½μ° 403 Forbidden μ€λ₯κ° λ°μν μ μμ΅λλ€. μ΄ κ²½μ° μλ² λ‘κ·Έ λλ API λ¬Έμλ₯Ό νμΈνμ¬ λ μμΈν μ 보λ₯Ό μ»μ μ μμ΅λλ€.
403 Forbidden μ€λ₯κ° λ°μν κ²½μ°, λ¨Όμ μλ² μΈ‘μ μ€μ μ νμΈνκ³ λ¬Έμ λ₯Ό ν΄κ²°ν΄μΌ ν©λλ€. μμ²μ΄ μ¬λ°λ₯΄κ² ꡬμ±λμλμ§, νμν μΈμ¦μ΄ μ 곡λμλμ§, μλ² μΈ‘μ 보μ μ μ± μ μ€μνλμ§ λ±μ νμΈν΄μΌ ν©λλ€.
β ν΄κ²° λ°©λ²
<aside> π‘ API ꡬν ν λλ§λ€ Spring Securityλ₯Ό μ λλ‘ νμ§ μκ³ CRUD κΈ°λ₯μ ꡬννλ€ λ³΄λ κ³μ CSRF μ€λ₯κ° λ°μνμλ€.
μ²μμλ csrf λ¬Έμ μΈκ±΄ μκ² λλ° μ΄λ»κ² ν΄κ²°ν΄μΌ ν μ§ λͺ°λΌμ νλ‘μ νΈ μ§ν μλμ μν₯μ λΌμ³€λ€.
λͺ λ² λκ°μ μ€λ₯λ₯Ό κ³μ κ²½ννλ€ λ³΄λ μ νν μ리λ μμ§ μ λͺ¨λ₯΄μ§λ§ μ΄λ»κ² ν΄μΌ ν΄κ²° ν μ μμμ§λ μκ² λμλ€.
</aside>
μ°μ λλ html → js (ajax ν΅μ _ jqueryλ₯Ό μ¬μ©) → controller νλ¦μΌλ‘ κ°λ°μ μ§ννμλ€.
1. μ°μ JS νμΌμ CSRF ν ν°μ κ°μ Έμ¨λ€.
(μ΄κ±΄ κ°λ°μλ§λ€ λ€λ₯΄μ§λ§ λλ μ΄μ°¨νΌ html μμ js κ·Έλ¦¬κ³ css μ°κ²°μ μ§μ΄ λμ μνμ΄κΈ° λλ¬Έμ jsμμ λΆλ₯΄κΈ°λ‘ κ²°μ νλ€. * htmlμμλ λΆλ₯Ό μ μλ€.)
// CSRF ν ν° κ°μ Έμ€κΈ°
function getCsrfToken() { return ***document***.querySelector('meta[name="_csrf"]').getAttribute('content');}
2. CSRF ν ν°μ κ°μ Έμ¨λ€.
var csrfToken = getCsrfToken(); // CSRF ν ν° κ°μ Έμ€κΈ°
κ·Έλ¦¬κ³ κ°μ Έμ¨ ν ν°μ …
3.csrf ν ν°μ ν€λ μ€μ - 보λ΄κΈ°
beforeSend: function(xhr) { // CSRF ν ν°μ ν€λμ μ€μ xhr.setRequestHeader('X-CSRF-TOKEN', csrfToken); },
λ₯Ό μΆκ°νλ€. μ΄λ ajax λ₯Ό ν΅νμ¬ μλ²λ‘ λ°μ΄ν°λ₯Ό μ μ‘νλλ° κ·Έλ csrf ν ν°μ ν€λμ λ£μ΄μ κ°μ΄ 보λ΄λ²λ¦°λ€.
κ·Έλ κ² νλ©΄ csrf κ΄λ ¨ 403 μ€λ₯λ₯Ό ν΄κ²° ν μ μλ€.
$.ajax({
beforeSend: function(xhr) {
// CSRF ν ν°μ ν€λμ μ€μ
xhr.setRequestHeader('X-CSRF-TOKEN', csrfToken);
});
β μ 체 μ½λ.js
// CSRF ν ν° κ°μ Έμ€κΈ°
function getCsrfToken() {
return document.querySelector('meta[name="_csrf"]').getAttribute('content');
}
function deletePost(postId) {
var csrfToken = getCsrfToken(); // CSRF ν ν° κ°μ Έμ€κΈ°
$.ajax({
type: 'DELETE',
url: '/posts/' + postId, // μλ²μ μλν¬μΈνΈ κ²½λ‘
beforeSend: function(xhr) {
xhr.setRequestHeader('X-CSRF-TOKEN', csrfToken);
},
success: function(response) {
alert("κ²μλ¬Όμ΄ μμ λμμ΅λλ€.");
// μμ²μ΄ μ±κ³΅νλ©΄ λͺ©λ‘ νμ΄μ§λ‘ μ΄λν©λλ€.
window.location.href = "/list-posts";
},
error: function(error) {
console.error('Error deleting post:', error);
alert("κ²μλ¬Ό μμ μ€ μ€λ₯κ° λ°μνμ΅λλ€.");
// νμν κ²½μ° μ€λ₯ μ²λ¦¬ λμ μν
}
});