스프링

 

 

테스트를 위해 security-context.xml 에서 페이지 권한 설정을 permitAll 로 한다.

<http use-expressions="true">
		<intercept-url pattern="/sample/**" access="permitAll"/>
		<intercept-url pattern="/ajaxTest/**" access="permitAll"/>	
		<intercept-url pattern="/login/loginForm.do" access="permitAll"/>  	
		<intercept-url pattern="/admin/**" access="hasRole('ADMIN')"/> 
		<intercept-url pattern="/login" access="hasRole('USER')" />
		<intercept-url pattern="/" access="permitAll"/>
		<intercept-url pattern="/user/**" access="hasAnyRole('USER, ADMIN')" />
		<intercept-url pattern="/passwordEncoderTest" access="permitAll"/>
		
		<form-login login-page="/login/loginForm.do" 
					default-target-url="/"
					authentication-failure-url="/login/loginForm.do?error"
					username-parameter="id"
					password-parameter="password" />		

		<logout logout-url="/logout"    
				logout-success-url="/" />
				
		<access-denied-handler error-page="/login/accessDenied.do"/>	
		<remember-me key="REMEMBER_ME_KEY"/>		
	</http>

 

 

 

SecurityAjaxController

컨트롤 영역이다.  ajax 를 데이터를 받는 것으로 특별한것이 없다.

/**
* <pre>
* 1. 패키지명 : net.macaronics.web.controller
* 2. 타입명 : SecurityAjaxController.java
* 3. 작성일 : 2017. 12. 1. 오후 6:12:29
* 4. 저자 : 최준호
* 
* </pre>
*
*/
package net.macaronics.web.controller;

import java.io.PrintWriter;

import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import net.macaronics.web.domain.UserVO;

@Controller
@RequestMapping("/ajaxTest")
public class SecurityAjaxController {

	private static Logger logger =LoggerFactory.getLogger(SecurityAjaxController.class);
	
	@RequestMapping(value="/ajaxCsrfForm",  method=RequestMethod.GET )
	public String ajaxCsrfTestForm() throws Exception{
		
		return "ajax/csrfTest";
	}
	
	@RequestMapping(value="/ajaxCsrf", method=RequestMethod.POST)
	public void ajaxCsrfTest(HttpServletResponse response  ,String username, String password, String hobby) throws Exception{
		
		logger.info("넘겨온 값  {} , {} , {} " , username, password, hobby);
		
		PrintWriter out =response.getWriter();
		out.println("success");
		
	}
	
	
	
}

 

 

스프링 시큐리티에서 제공하는   csrf 를 view 영역의 meta 의 content 에 el 태그 _csrf.token 과 _csrf.headerName 를 사용해서 데이터를 뿌려주자.

<meta id="_csrf" name="_csrf" content="${_csrf.token}"/>
<!-- default header name is X-CSRF-TOKEN -->
<meta id="_csrf_header" name="_csrf_header" content="${_csrf.headerName}"/>

 

html 소스를 보면 다음과 같은 형식으로 출력된다.

<meta id="_csrf" name="_csrf" content="b3309bf5-37b8-47bf-a7c3-e3b4ee97ccfc">
<!-- default header name is X-CSRF-TOKEN -->
<meta id="_csrf_header" name="_csrf_header" content="X-CSRF-TOKEN">

 

메타 태그에 있는 csrf 토그과 header 를 다음과 같은 방법으로 javascript 의 변수에 저장하자.

var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
 

 

ajax 사용시 beforSend 를 사용해서 header 와 csrf 토크값을 던져 주면 된다. 

            beforeSend : function(xhr){
                xhr.setRequestHeader(header, token);
            },

 

전체 소스는 다음과 같다.

 

csrfTest.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta id="_csrf" name="_csrf" content="${_csrf.token}"/>
<!-- default header name is X-CSRF-TOKEN -->
<meta id="_csrf_header" name="_csrf_header" content="${_csrf.headerName}"/>


<script src="//code.jquery.com/jquery.min.js"></script>
<script src="//code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script>

<title>ajax csrf Test</title>
</head>
<body>


<form id="form1">
 <input type="text" name="username">
 <input type="text" name="password">
 <input type="text" name="hobby">
 <button id="btnAjax" type="button" >전송</button>
</form>


<div id="result">
</div>


<script>
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
 
$(function() {
	
	$("#btnAjax").click(function(){		
		var params=jQuery("#form1").serialize();	
		$.ajax({	
			url:"/ajaxTest/ajaxCsrf",
			type:"post",
			data:params,
			beforeSend : function(xhr){
	            xhr.setRequestHeader(header, token);
	        },
			success:function(result){
				
				if($.trim(result)=='success'){
					$("#result").html(params);
				}
			},
			error:function(result){
				alert("실패");	
			}
		});
		
	});

	
});
</script>




</body>
</html>

 

 

테스트 성공시 화면

 

 

 

스프링 시큐리티에서 csrf 토큰을 제거한 후 전송해 보자.

	$("#btnAjax").click(function(){		
		var params=jQuery("#form1").serialize();	
		$.ajax({	
			url:"/ajaxTest/ajaxCsrf",
			type:"post",
			data:params,
	/* 		beforeSend : function(xhr){
	            xhr.setRequestHeader(header, token);
	        }, */
			success:function(result){
				
				if($.trim(result)=='success'){
					$("#result").html(params);
				}
			},
			error:function(result){
				alert("실패");	
			}
		});
		
	});

 

csrf 토큰을 보내는 beforeSend 를 제거하고 전송하면  콘솔 창에 다음과 같은 에러 메시지가 나타난다.

*******  Macaroncis AOP Annotation.beforeTargetMethod executed. => 
INFO : config.aop.LogAdvice -  MVC 영역 - Controller 	 :  ,  Type - net.macaronics.web.controller.SecurityAjaxController 
INFO : config.aop.LogAdvice - 클래스 명 - SecurityAjaxController, 메서드 명 - ajaxCsrfTestForm 
INFO : config.aop.LogAdvice - 파라미터 값 : []
ERROR: config.error.CommonException - commonErrorMessage() , Request method 'POST' not supported
ERROR: config.error.CommonException - @ExceptionHandler(Exception.class) --  

★ ★ ★     에러 메시지 시작 ★★★  

getMessage() : Request method 'POST' not supported

★ 에러 시작 클래스 이름 className : org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping
★ 에러 시작 메서드 이름(methodName) : handleNoMatch, line : 225

STACKTRACE - className : org.springframework.web.servlet.handler.AbstractHandlerMethodMapping, 메서드 이름(methodName) : lookupHandlerMethod, line : 368
STACKTRACE - className : org.springframework.web.servlet.handler.AbstractHandlerMethodMapping, 메서드 이름(methodName) : getHandlerInternal, line : 308
STACKTRACE - className : org.springframework.web.servlet.handler.AbstractHandlerMethodMapping, 메서드 이름(methodName) : getHandlerInternal, line : 61
STACKTRACE - className : org.springframework.web.servlet.handler.AbstractHandlerMapping, 메서드 이름(methodName) : getHandler, line : 351
STACKTRACE - className : org.springframework.web.servlet.DispatcherServlet, 메서드 이름(methodName) : getHandler, line : 1131
STACKTRACE - className : org.springframework.web.servlet.DispatcherServlet, 메서드 이름(methodName) : doDispatch, line : 936
STACKTRACE - className : org.springframework.web.servlet.DispatcherServlet, 메서드 이름(methodName) : doService, line : 897
STACKTRACE - className : org.springframework.web.servlet.FrameworkServlet, 메서드 이름(methodName) : processRequest, line : 970
STACKTRACE - className : org.springframework.web.servlet.FrameworkServlet, 메서드 이름(methodName) : doPost, line : 872
STACKTRACE - className : javax.servlet.http.HttpServlet, 메서드 이름(methodName) : service, line : 648
STACKTRACE - className : org.springframework.web.servlet.FrameworkServlet, 메서드 이름(methodName) : service, line : 846
STACKTRACE - className : javax.servlet.http.HttpServlet, 메서드 이름(methodName) : service, line : 729
STACKTRACE - className : org.apache.catalina.core.ApplicationFilterChain, 메서드 이름(methodName) : internalDoFilter, line : 292
STACKTRACE - className : org.apache.catalina.core.ApplicationFilterChain, 메서드 이름(methodName) : doFilter, line : 207
STACKTRACE - className : org.apache.tomcat.websocket.server.WsFilter, 메서드 이름(methodName) : doFilter, line : 52
STACKTRACE - className : org.apache.catalina.core.ApplicationFilterChain, 메서드 이름(methodName) : internalDoFilter, line : 240
STACKTRACE - className : org.apache.catalina.core.ApplicationFilterChain, 메서드 이름(methodName) : doFilter, line : 207
STACKTRACE - className : org.apache.catalina.core.ApplicationDispatcher, 메서드 이름(methodName) : invoke, line : 716
STACKTRACE - className : org.apache.catalina.core.ApplicationDispatcher, 메서드 이름(methodName) : processRequest, line : 466
STACKTRACE - className : org.apache.catalina.core.ApplicationDispatcher, 메서드 이름(methodName) : doForward, line : 391
STACKTRACE - className : org.apache.catalina.core.ApplicationDispatcher, 메서드 이름(methodName) : forward, line : 318
STACKTRACE - className : org.springframework.security.web.firewall.RequestWrapper$FirewalledRequestAwareRequestDispatcher, 메서드 이름(methodName) : forward, line : 154

 

 

 

 

 

 

 

소스 :  https://github.com/braverokmc79/spring_simple_blog

 

 

 

 

about author

PHRASE

Level 60  라이트

만약 한쪽의 말만 듣는다면, 친한 사이가 갑자기 떨어짐을 볼 것이다. -명심보감

댓글 ( 4)

댓글 남기기

작성