본문으로 바로가기

로그인 화면 커스터마이징을 했지만 부족한 부분들이 있다. 그래서 조금 더 로그인스럽게 보이기 위해 몇 가지 더 작업을 할 것이다. 지금 할 작업들은 에러 메시지 작업과 Access Denied 페이지 작업이다. 


현재 로그인 페이지 상태에서 잘못된 로그인 정보를 입력해보자. 로그인에 실패했지만 아무런 에러 메시지를 보지 못하고 로그인 페이지만 보일 것이다. 로그인이 실패했으면 왜 실패했는 지 에러 메시지를 띄워주어야 한다. 스프링에서 제공하는 기본 로그인 화면을 사용했을 때에는 잘못된 로그인 정보를 입력하면 에러 메시지가 보였다. 그렇기에 우리도 잘못된 로그인 정보를 받으면 에러 메시지를 띄워주도록 하자.


Spring Security에서 예외가 발생(로그인 실패)하게 되면 해당 예외에 대한 객체를 만들어서 세션에 저장한다. 해당 세션을 저장할 때 사용되는 Key의 이름은 SPRING_SECURITY_LAST_EXCEPTION 이다. 세션에서 해당 키를 이용하여 message 변수 값을 읽어와 화면에 출력하면 된다.


사실 세션을 통해서 에러 메시지를 출력하는 것은 좋지 못하다. 예를 들어 해킹을 통해 인증 실패가 계속 발생한다고 해보자. 그럼 세션에 예외 객체를 계속 저장할 것이다. 이렇게 여러번 계속 예외를 던지게 되면 WAS 메모리가 가득차서 Full CG가 발생하게 되어 웹 페이지 응답이 느려질 것이다. 그래서 예외가 발생할 경우에는 세션에 저장하지 않도록 해주는 것이 좋다. 지금은 어떠한 컨트롤러도 핸들러도 사용하지 않기 때문에 나중에 만들어가면서 에러 메시지를 세션에 이용하지 않게 할 것이다.


LoginPage.jsp

1
2
3
4
5
6
7
<c:if test="${not empty SPRING_SECURITY_LAST_EXCEPTION}">
    <font color="red">
        <p>Your login attempt was not successful due to <br/>
            ${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}</p>
        <c:remove var="SPRING_SECURITY_LAST_EXCEPTION" scope="session"/>
    </font>
</c:if>
cs


인증에 실패했을 경우 기본 속성값(/login?error)으로 인하여, 파라미터 error를 셋팅하여 로그인 페이지에 보여준다. 이때 세션에 저장된 SPRING_SECURIRY_LAST_EXCEPTION 키 값도 같이 오게 된다. 따라서 <c:if> 태그를 사용하여 해당 키가 비어있지 않으면(인증에 실패했으면) 세션을 통해 저장되어 있는 에러 메시지를 보여준다.



위의 코드를 삽입하고 실행해보자. 위의 사진에 빨간 글씨가 인증에 실패했을 때 나오는 에러 문구인데, Bad credentials 라고 적힌 부분이 ${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message 로 세션에 저장되어 온 에러 메시지이다. 이 메세지들 또한 원하는 메시지로 출력할 수 있다. 여기서 끝내도 되지만 나는 한글로 에러 메시지를 띄우고 원하는 문구를 삽입할 것이다.


해당 메시지들은 어디에 저장되어 있을까? 메이븐의 라이브러리(Libraries)에 들어가서 spring-security-core-버전.jar 에 들어가면 각 나라 언어별로 메시지 프로퍼티 파일들이 있다. message.properties 파일을 보면 key 값에 따라 메세지 내용들이 담겨 있는 것을 확인할 수 있다. 위의 에러 메시지는 key가 AbstractUserDetailsAuthenticationProvider.bacCredentials 인 것을 읽어 온 것이다. message 프로퍼티 파일들의 key 값을 동일하게 해준다면, 우리가 원하는 에러 메시지를 사용하여 새로운 message 프로퍼티 파일들을 만들 수 있다.


security_message.properties

1
2
3
4
5
AbstractUserDetailsAuthenticationProvider.badCredentials=아이디나 비밀번호가 맞지 않습니다. 다시 확인해주세요.
AbstractUserDetailsAuthenticationProvider.credentialsExpired=비밀번호 유효기간이 만료 되었습니다. 관리자에게 문의하세요.
AbstractUserDetailsAuthenticationProvider.disabled=계정이 비활성화되었습니다. 관리자에게 문의하세요.
AbstractUserDetailsAuthenticationProvider.expired=계정이 만료되었습니다. 관리자에게 문의하세요.
AbstractUserDetailsAuthenticationProvider.locked=계정이 잠겨있습니다. 관리자에게 문의하세요.
cs


WEB-INF 폴더에 message 폴더를 만들고 security_message.properties 파일을 생성하고 다음의 내용을 넣었다. message.properties를 그대로 복사해서 수정해도 되지만 난 일단 간단하게 사용할 것만 수정하였다. 이렇게 원하는 문구로 수정을 했으면 메세지 파일을 사용할 수 있게끔 설정을 해야 한다. 혹시 유니코드 문자로 나오는 게 싫으면 이클립스에 propedit 플러그인을 설치하면 된다.


context-root.xml

1
2
3
4
5
6
7
8
9
    <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basenames">
            <list>
                <value>/WEB-INF/message/security_message</value>
            </list>
        </property>
        <property name="defaultEncoding" value="UTF-8"/>
        <property name="cacheSeconds" value="5"/>
    </bean>
cs


root-context 설정 파일에 넣었다. servlet-context 설정 파일에 넣으면 spring security가 messageSource에 접근할 수 없다. 따라서 root-context 설정 파일에 해당 설정을 해야 한다. 여기까지 설정을 했다면 실행을 해보자. 그러면 인증을 실패했을 때 한글로 에러메시지가 커스터마이징 된 것을 볼 수 있다.




이제는 접근 불가 페이지를 커스터마이징 해보자. 인증 후에 권한에 따라 접근 경로가 달라지는데 접근 권한이 없는 경로에 진입하게 되면 Access Denied 403 에러 페이지가 뜬다. 어느 사이트에 가도 이런 식의 에러 페이지는 절대 뜨지 않는다. 그렇기 때문에 커스터 마이징을 할 것이다. 거창한 것없이 그냥 접근 불가 페이지를 보여준다. 참고로 이것도 에러 메시지 세션으로 존재하여 원하는 접근 불가 메시지를 출력 할 수 있다.


context-security.xml

1
2
3
<access-denied-handler
    error-page="/access_denied_page"
/>
cs


403 페이지가 뜨지 않고 지정한 페이지로 이동하게 설정한다. ref 속성을 사용한다면 핸들러를 이용할 수 있다.


LoginController.java

1
2
3
4
    @RequestMapping(value="/access_denied_page")
    public String accessDeniedPage() throws Exception {
        return "/access_denied_page";
    }
cs


access_denied_page.jsp

1
<h6>권한이 없어 접근이 불가합니다.<br>관리자에게 문의하세요.</h6>
cs


그냥 페이지를 보여주는 것이기때문에 정말 별거 없다. 접근 불가 페이지 설정하는 context-security 부분만 잘 알아두면 된다. 이렇게 접근 불가 페이지를 보여주는 것이 아니라 알람창과 함께 메인 페이지로 가는 방법도 있다. 그건 GitHub에 있으니 그 방법을 쓰고 싶다면 참고해도 된다.



이번 글은 정말 개인 취향이기때문에 속성 설정부분만 유의하면 된다. GitHub


댓글을 달아 주세요

  1. 익명 2018.11.26 10:48

    비밀댓글입니다

  2. 호랑말코 2019.01.06 14:32

    굿뜨잡 잘보고갑니다!!

  3. 질문맨 2019.08.20 12:19

    깃에서 전체 소스를 다운받아서 적용시키고 DB에 데이터를 넣어서 로그인해보니까 비밀번호 유효기간이 만료 되었습니다. 관리자에게 문의하세요. 라는 에러가 뜨는데 DB에 들어가는 내용이 어떤게 추가되었을까요?(카운트, 엑세스데이트는 추가)

    • BlogIcon #에게 2019.08.20 12:44 신고

      해당 메세지를 처리하려면 UserDetails 에서 isCredentialsNonExpired 메소드가 활성화되어야 합니다. DB에 칼럼 하나를 추가하고 해당 메소드를 활용하시면 될 듯 해요!

  4. 질문맨 2019.08.21 09:09

    isCredentialsNonExpired 메소드(CREDEXPI)의 기본 값은 어떻게 잡아주나요?
    0~6까지 넣어봤는데 다 오류 메시지를 불러 오던데 로그인이 통과되는 값이 궁금합니다.
    소스를 뒤적여봐도 잘 안보이네요 ㅠㅠ

  5. BlogIcon 차람쥐코드 2019.12.05 17:14 신고

    늦은감이 있지만 질문하나만 하겠습니다.
    시큐리티 예외를 SPRING_SECURITY_LAST_EXCEPTION" 세션키로 받으면(클라이언트단)
    해커가 해킹을 시도했을시에 계속 인증시도를 하면서 예외 던지게 되면 was에 메모리가 가득차서
    fullCG가 발생하면서 응답속도가 느려진다고 말씀하셨는데 여기서 궁금한게 fullCG가 혹시 GC 가비지컬렉션
    말씀하시는건가요? 만약에 fullGC가 맞다고하면 카비지컬렉션이 힙메모리 영역을 클리어하는 건데 계속해서 클리어
    하게되면서 응답속도가 느려지는 원리가 맞는건가요? ㅎㅎ

  6. 띠로리 2020.01.06 14:59

    안녕하세요, 우선 전자정부기반으로 시큐리티환경설정 잡으면서 정말 큰도움이되고있어 감사의 인사먼저드리겠습니다.

    댓글을 다는 이유가 다름이아니라. 해당 message.properties와 denined jsp를 나타내는부분이 이상하게 전혀 안되네요.

    처음에는 제 환경설정이 root-context.xml 파일을 따로 안만들어서 빈이 등록이안되서 한글로된 메세지가 안나오나 해서 따로 web.xml 파일에 root-context파일 경로를 추가하고 블러거님처럼 다시 예제를 해보았는데도 안되고있어.. 뭔가 빼먹은부분이 있나 궁금해서 댓글을 남깁니다.

    다시한번 잘 정리된 글 덕분에 환경설정을 잘하고있습니다. 감사합니다.

    • BlogIcon #에게 2020.01.06 15:17 신고

      안녕하세요! 어떤 부분이 안보이거나 안되는 지 확인해봐야 할 거 같아요. Access Denied 페이지가 안띄워지는 건가요? 아니면 message properties 의 메세지들이 보이지 않은 건가요?

    • 띠로리 2020.01.06 15:24

      네, 맞습니다. 현재 빨간색으로 나오는 영어문구 밑에 한글로 나오는부분이 저는 계속 영어만나오고있어요.

      제가 환경설정이 좀달라서그런진모르겠는데.. 설명중에 시큐리티가 xml파일을 못읽게할수도있다고한부분이 마음에걸리네요
      >> 해당부분은 도무지 영문을 못찾겠어서 pc 재부팅해보니 되네요..;;

      빠른답변감사합니다.!


      그리고 access Denide페이지 또한 안끄고있어서.. 계속 403 페이지가 나오는데 이부분은 security-context.xml 파일을 비교해서 봐도 틀린부분이없는데 이상하게 페이지호출이안되네요..
      >> 해당부분도 해결하였습니다.

      전자정부프레임워크같은경우는 요청URL 마지막에 .do를 붙이는데 이것을 안붙여서 일어난 해프닝이엿습니다.

    • BlogIcon #에게 2020.01.06 15:42 신고

      그러면 https://github.com/todyDev/Spring-Security/blob/master/securityPrj/src/main/resources/spring/context-root.xml 여기 코드에서 18-24번까지 설정하시고 https://github.com/todyDev/Spring-Security/blob/master/securityPrj/src/main/java/tody/common/util/MessageUtils.java 해당 유틸파일을 만들어서 해보시겠어요??

    • 띠로리 2020.01.06 15:51

      제가 답글남기시기전에 남기셨군요. 해당 부분은 모두 다 해결하였습니다.

      때로는 재부팅후 바람한번 쐬고오는게 좋은거 같네요 ㅠㅠ

    • BlogIcon #에게 2020.01.06 15:52 신고

      ㅠㅠ 이놈의 이클립스!!! 좋은 하루되세요

  7. 안녕하세요 2020.05.24 01:49

    LoginPage.jsp 에서 <c:if test="${not empty SPRING_SECURITY_LAST_EXCEPTION}"> 부분에서 test 변수는 어디서 나오는건가요 ??

    • BlogIcon #에게 2020.09.27 11:43 신고

      SPRING_SECURITY_LAST_EXCEPTION 요게 어디서 오시는 지 궁금하신 걸까요? 이건 security 에서 제공해주는 key 입니다

  8. 익명 2022.05.17 17:40

    비밀댓글입니다