스프링 시큐리티 4부터 달라진 설정
pom.xml에서 스프링 시큐리티 버전 변경
pom.xml
<properties> <spring.version>5.3.33</spring.version> <spring.security.version>5.8.10</spring.security.version> <jdk.version>11</jdk.version> </properties>
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>${spring.security.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>${spring.security.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>${spring.security.version}</version> </dependency>
src/main/webapp/WEB-INF/lib에 있는 라이브러리를 모두 지운다.
pom.xml 파일이 있는 루트 디렉터리로 이동해 다음을 수행한다.
mvn clean compile war:inplace
톰캣을 재실행한다.
로그인 화면으로 이동하여 로그인을 시도하면 빈 화면을 만나게 된다.
에러 메시지도 로그에 없다.
원인은 스프링 시큐리티 4의 CSRF 방지 기능이 작동하고 있기 때문이다.
스프링 시큐리티 4부터는 이 기능이 디폴트로 작동한다.
따라서 PATCH, POST, PUT, DELETE 요청에 CSRF 토큰을 포함해야 한다.
/users/login.jsp 파일을 열고 폼에 다음을 추가한다.
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
스프링 폼 태그를 사용하는 폼(일반적으로 <form:form>, 우리의 프로젝트에서는 <sf:form>의 모양이다)
은 자동으로 토큰 파라미터가 추가되므로 위 코드가 필요 없다.
다시 로그인을 시도한다.
이번에는 /j_spring_security_check를 찾을 수 없다는 404 에러를 만나게 된다.
스프링 시큐리티 4에서는 http 자식요소 form-login의 속성 중 기본값이 변경된 속성
<form-login>에서 login-processing-url 속성의 기본값은 /j_spring_security_check에서 POST /login으로,
username-parameter 속성의 기본값은 j_username에서 username으로,
password-parameter 속성의 기본값은 J_password에서 password로,
authentication-failure-url 속성의 기본값은 /login?error=1으로 변경되었다.
스프링 시큐리티 설정 파일을 열고 강조된 부분을 참고하여 수정한다.
security.xml
<form-login login-page="/users/login" authentication-failure-url="/users/login?error=1" default-target-url="/bbs/list?boardCd=chat&page=1" />
login-page 속성의 기본값은 /login, authentication-failure-url 속성의 기본값은 /login?error=1이다.
설정 파일에서 속성을 생략한다면 이 값이 적용된다.
사용자 로그인 페이지(/users/login)를 사용하고 로그인 실패시 다시 로그인 페이지로 이동하게 하려면, login-page 속성 뿐만 아니라 authentication-failure-url 속성을 명시해야 하며
아래와 같은 추가적인 설정이 필요하다.(이미 되어 있다.)
<http use-expressions="true"> <intercept-url pattern="/users/login" access="permitAll" />
http의 use-expressions의 속성 기본값이 false에서 true로 변경되었으므로 생략할 수 있다.
<http> <intercept-url pattern="/users/login" access="permitAll" />
로그인 페이지 수정
명시하지 않은 설정은 디폴트 값이 적용됨을 고려하면서 /users/login.jsp를 수정한다.
/users/login.jsp
<c:if test="${param.error != null }"> <h2>Username/Password not corrrect</h2> </c:if> <c:url var="loginUrl" value="/login" /> <form action="${loginUrl }" method="post"> <p style="margin:0; padding: 0;"> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /> </p> <table> <tr> <td style="width: 200px;"><spring:message code="user.email" /></td> <td style="width: 390px"><input type="text" name="username" style="width: 99%;" /></td> </tr> <tr> <td><spring:message code="user.password" /></td> <td><input type="password" name="password" style="width: 99%;" /></td> </tr> </table>
설정 파일일 변경되었으니 톰캣을 재실행하고 로그인을 테스트한다.
로그인이 성공했다면 로그아웃을 실행한다.
다시 빈 화면을 만나게 될 것이다.
이는 logout의 logout-url 속성의 기본값이 /j_spring_security_logout에서 POST /logout으로 변경되었기 때문이다.
POST /logout 요청을 해야하기에 모든 화면이 포함하는 header.jsp를 수정하여 로그아웃을 수정해 보자.
/inc/header.jsp파일을 열고 <head>와 </head>사이에 다음을 추가한다.
<input type="button" value="<spring:message code="user.logout" />" id="logout" />
header.jsp 가장 아래에 다음을 추가한다.
<form id="logoutForm" action="/logout" method="post" style="display:none"> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /> </form> <script> $(document).ready(function() { $('#logout').click(function() { $('#logoutForm').submit(); return false; }); }); </script>
다시 로그아웃을 테스트한다.
header.jsp에 jQuery를 사용하고 있으므로 화면을 보여주는 페이지는 모두 jQuery를 임포트하도록 다음 코드를 추가한다.
<script type="text/javascript" src="/js/jquery-3.2.1.min.js"></script>
로그아웃까지 테스트했다면 다시 로그인하고 새글 쓰기를 시도한다.
새글 쓰기 처리에서 다시 빈 화면을 만나게 된다.
첨부 파일의 경우 <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />이 아닌 쿼리 스프링으로 CSRF 토큰을 전달해야 한다.
이는 스프링 폼 태그를 사용하고 있다 하더라도 마찬가지다.
write.jsp와 modify.jsp 파일을 열고 <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />이 있다면 지우고,
아래와 같이 폼의 action 속성을 수정한다.
write.jsp의 action 속성
<sf:form action="write?${_csrf.parameterName}=${_csrf.token}" method="post" ...
modify.jsp action 속성
<sf:form action="modify?${_csrf.parameterName}=${_csrf.token}" method="post" ...
하지만 이 방법은 쿼리 파라미터가 노출될 수 있다.
민감한 데이터를 노출되지 않도록 바디나 헤더에 두는 것이 좀 더 낫다.
이에 관한 정보는 아래 참고에 링크해 둔다.