반응형
지난 번에 spring boot에 csrf를 적용하는 포스팅을 올렸었는데 그냥 spring과 적용하는 방법이 달라 또 포스팅을 올려본다!
- web.xml에 dispactcher servlet을 등록한다.
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
- servlet-context.xml에 csrf hidden이 form에 자동으로 추가해주는 class를 빈(bean)으로 등록한다
<beans:bean id="requestDataValueProcessor" class="com.xxxx.framework.processor.CSRFRequestDataValueProcessor"/>
- CSRFRequestDataValueProcessor.java
- csrf hidden 데이터를 자동 추가해주는 클래스, RequestDataValueProcessor를 implements해야 한다.
public class CSRFRequestDataValueProcessor implements RequestDataValueProcessor{
@Override
public Map<String, String> getExtraHiddenFields(HttpServletRequest request) {
Map<String, String> hiddenFields = new HashMap<String, String>();
HttpSession session = request.getSession(false);
if(session == null) {
return hiddenFields;
}
String token = UUID.randomUUID().toString().replaceAll("-", "");
session.setAttribute(FrameworkConstants.CSRF_TOKEN_NAME, token);
hiddenFields.put(FrameworkConstants.CSRF_PARAM_NAME, token);
return hiddenFields;
}
@Override
public String processAction(HttpServletRequest arg0, String arg1) {
return null;
}
@Override
public String processFormFieldValue(HttpServletRequest arg0, String arg1, String arg2, String arg3) {
return null;
}
@Override
public String processUrl(HttpServletRequest arg0, String arg1) {
return null;
}
}
-
servlet-context.xml 에 CSRF 체크 interceptor 등록
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**/*.do"/>
<beans:bean class="com.xxx.framework.interceptor.CSRFInterceptor">
<beans:property name="excludeURLs">
<beans:array value-type="java.lang.String">
<beans:value>login.do</beans:value>
<beans:value>logout.do</beans:value>
<beans:value>view.do</beans:value>
</beans:array>
</beans:property>
</beans:bean >
</mvc:interceptor>
</mvc:interceptors>
- login.do, logout.do, view.do는 csrf를 체크하지 않음!
- csrf를 체크하지 않을 url 혹은 패턴을 등록할 수 있다. 그리고 csrf 인터셉터에서 해당 url은 체크하지 않도록 로직을 짜면 됨!
- CSRFInterceptor.java
public class CSRFInterceptor extends HandlerInterceptorAdapter{
Logger log = Logger.getLogger(this.getClass().getName());
private String excludeURL ;
private String[] excludeURLs;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
boolean result = true;
if(request.getMethod() == null) {
return result;
}
if(!request.getMethod().equalsIgnoreCase("post")) {
return result;
}else {
if(!isExcludeURL(request.getRequestURI())) {
if(!this.verifyCSRFToken(request)) {
this.log.error("Incorrect CSRF value");
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Bad Request");
result = false;
}
}
}
return result;
}
private boolean verifyCSRFToken(HttpServletRequest request) {
boolean result = true;
if(request.getSession().getAttribute(FrameworkConstants.CSRF_TOKEN_NAME) != null) {
String sessionToken = (String)request.getSession().getAttribute(FrameworkConstants.CSRF_TOKEN_NAME);
String paramToken = request.getParameter(FrameworkConstants.CSRF_PARAM_NAME);
if(paramToken == null || !sessionToken.equals(paramToken)) {
this.log.debug("Incorrect CSRF value");
result = false;
}
}
return result;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
super.afterCompletion(request, response, handler, ex);
}
private boolean isExcludeURL(String url) {
boolean result = false;
if(url == null) {
return true;
}
url = url.toLowerCase();
for (int i = 0; i < this.excludeURLs.length; i++) {
if (url.indexOf( this.excludeURLs[i].toLowerCase() ) < 0) {
continue;
}
result = true;
break;
}
return result;
}
public String getExcludeURL() {
return excludeURL;
}
public void setExcludeURL(String excludeURL) {
this.excludeURL = excludeURL;
}
public String[] getExcludeURLs() {
return excludeURLs.clone();
}
public void setExcludeURLs(String[] excludeURLs) {
if (excludeURLs == null) {
this.excludeURLs = new String[0];
} else {
this.excludeURLs = Arrays.copyOf(excludeURLs, excludeURLs.length);
}
}
}
확실히 spring boot가 훨씬 간단하다.. 너무 편해!
728x90
반응형
'개발 > Web' 카테고리의 다른 글
[Web] PRG 패턴 (Post - Redirect - Get) (2) | 2022.01.07 |
---|---|
[JBoss] 로깅 문제, 자체적으로 사용하는 로그 라이브러리 제외하기 (0) | 2021.12.31 |
하나의 tomcat에 같은 프로젝트 두개 이상 띄우기 : 'webapp.root' 에러 (0) | 2021.12.13 |
[Spring Boot] spring security - CSRF 적용 , +) ajax csrf 적용 (0) | 2021.12.07 |
[Spring Boot] mybatis 적용하기 (0) | 2021.12.06 |
댓글