본문으로 바로가기

스프링에서 제공하는 기본 로그인 화면을 커스터마이징을 하지 않고 그대로 사용하기에는 부족하다. 물론 로그인 기능은 충실히 한다. 아이디와 비밀번호를 받아와 로그인을 하게 만든다는 것. 하지만 로그인에서 이 기능만 수행할 순 없다. 아이디 기억, 검증 로직 등등 로그인 외에도 다른 기능들이 추가적으로 있는 로그인 화면을 만드는 게 목표일 것이다. 물론 디자인적으로 분명히 고쳐야 한다. 이런 기본 로그인 화면만으로는 다른 어떤 것을 추가할 수 없다. 그렇기 때문에 우리는 내가 원하는 로그인 화면을 Spring Security 로그인에 맞게 커스터마이징해야 한다.


먼저 기본 로그인 화면부터 보자. 그래야 Spring Security 로그인에 맞게 커스터마이징을 할 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body onload="document.f.username.focus();">
    <h3>Login with Username and Password</h3>
    <form name="f" action="/login" method="POST">
        <table>
            <tbody>
                <tr>
                    <td>User:</td>
                    <td><input type="text" name="username" value=""></td>
                </tr>
                <tr>
                    <td>Password:</td>
                    <td><input type="password" name="password"></td>
                </tr>
                <tr>
                    <td colspan="2"><input name="submit" type="submit" value="Login"></td>
                </tr>
                <input name="${_csrf.parameterName}" type="hidden" value="${_csrf.token}">
            </tbody>
        </table>
    </form>
</body>
cs


별거 없어 보이는 HTML이지만 중요한 것들이 있다. 빠져서는 안되는 부분이며 속성값들의 기본값을 알아둬야 한다.


- form 태그의 action 속성값은 POST /login 이다.

- 아이디 입력 input 태그의 name 속성값은 username 이다.

- 비밀번호 입력 input 태그의 name 속성값은 password 이다.

- CSRF 토큰을 같이 전송한다.


로그인 화면을 커스터마이징할 때 반드시 알아야하고 존재해야하는 부분이다. 왜냐하면 우리는 이 속성값들을 원하는 대로 변경할 수 있기 때문이다. username 이나 password 속성값이 마음에 들지 않는다면 login_id 나 login_pw 로 변경해도 된다. 속성값들을 변경했을 경우 반드시 Spring Security 설정을 변경해야 한다. 이 부분은 나중에 설명을 할 것이다.


속성값을 설정한 뒤에, 로그인 페이지를 만들고 Spring Security 설정을 해야 한다. 로그인 페이지를 만들지 않고 그냥 메인 페이지에 조그맣게 로그인 폼을 만들면 되지 않냐, 라고 생각할 수도 있다. 그럼 만약에 인증하지 않고 인증해야 들어갈 수 있는 페이지에 접근하려고 하면, 로그인 유도를 어떻게 할 것인가. 로그인 화면을 따로 지정하여 이동시켜서 로그인 화면을 보여줘야 한다. 그리고 어느 사이트를 가도 인증을 해야 하는 페이지에 접근한다면 메인 화면에 있는 로그인 폼이 아닌 로그인 페이지가 따로 뜰 것이다. 따라서 위의 규정에 따라 로그인 폼을 작성하여 Spring Security 로그인을 만들더라도, 로그인 화면 페이지 자체는 만들어둬야 한다. 그럼 시작해보자.



1. 로그인 페이지

나는 부트스트랩을 이용하여 로그인 페이지를 만들었다. 디자인은 각자 구상한대로 만들면 된다. 규정만 있으면 된다.


loginPage.jsp

1
2
3
4
5
6
7
8
9
<form class="px-4 py-3" action="/login" method="post">
    ...
    <input type="text" class="form-control" name="loginId" placeholder="example">
    ...
    <input type="password" class="form-control" name="loginPwd" placeholder="Password">
    ...
    <input name="${_csrf.parameterName}" type="hidden" value="${_csrf.token}"/>
    <button type="submit" class="btn btn-primary">Sign in</button>
</form>
cs


action 속성은 그대로 두고 name 속성값들만 변경했다. 각각 loginId와 loginPwd로 변경했다. action 속성값을 변경하지 않는다고 생략하면 안된다. 속성값의 기본값을 적어주어야 한다. 마찬가지로 name 속성값들을 변경하지 않으려면 username과 password를 써주어야 한다.



2. Security 설정

커스텀한 로그인 화면에서 속성값을 셋팅했다면 그에 맞는 Spring Security 설정을 해야 한다. form 방식의 로그인을 하기 위해서는 <http/> 태그 안의 <form-login/>에 속성값들을 설정해야 한다. <form-login /> 태그 안에는 여러가지 속성들이 존재하는데 본인한테 필요한 속성들을 사용하면 된다. 나는 일단 지금 필요한 것들만 추가했다. 반드시 커스텀한 로그인 화면에서의 속성값과 일치해야한다.


context-security.xml

1
2
3
4
5
6
7
8
9
10
<http>
    <!-- 권한 설정 -->    
    <form-login
        username-parameter="loginId"
        password-parameter="loginPwd"
        login-processing-url="/login"
        login-page="/secu/loginPage"
        default-target-url="/"
    />
</http>
cs

- 04행: 아이디 입력 input 태그의 name 속성값을 변경했기 때문에 추가해주었다. 기본값은 username이다.

- 05행: 비밀번호 입력 input 태그의 name 속성값을 변경했기 때문에 추가해주었다. 기본값은 password이다.

- 06행: form 태그의 action 속상값의 기본값이다. 기본값을 설정했을 때는 생략해도 된다.

- 07행: 로그인 페이지의 url을 뜻한다.

- 08행: 로그인 성공시, 가게 될 페이지를 뜻한다. 나는 index 페이지로 가게 해뒀다.


 표현식

 설명

 username-parameter

 input 태그의 아이디 혹은 이메일 name 속성 값 (default: username)

 password-parameter

 input 태그의 비밀번호 name 속성 값 (default: password)

 login-page

 새로운 로그인 화면 url (default: 스프링 기본 로그인 화면)

 login-processing-url

 form 태그의 action 속성 (default: POST /login)

 default-target-url

 로그인 성공시 호출할 url

 authentication-failure-url

 로그인 실패시 호출할 url (default: /login?error=1)

 authentication-success-handler-ref

 로그인 성공시 default-target-url이 아닌 핸들러로 리다이렉트

 authentication-failure-handler-ref

 로그인 실패시 authentication-failure-url이 아닌 핸들러로 리다이렉트

 always-use-default-target

 무조건 핸들러가 지정한 페이지로 이동 (default: false)



3. Controller

로그인 페이지를 가기위해선 컨트롤러가 필요하다. 


LoginController.java

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

- 01행: security 설정에서의 새로운 로그인 화면의 주소를 뜻한다.



4. 페이지 수정

인증을 하지않고 접근 권한이 없는 페이지에 들어갈 때 로그인 페이지를 띄우는 것 외에, 이용자가 스스로 인증(로그인)을 하고 싶을 때 로그인 버튼을 눌러서 로그인 페이지로 들어가기 위해 로그인 버튼을 생성한다.


index.jsp

1
2
3
4
5
6
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
 
<sec:authorize access="isAnonymous()">
    <h5><a href='<c:url value="/secu/loginPage"/>'>LOGIN</a> 로그인 해주세요.</h5>
</sec:authorize>
cs

- 02행: spring security 기능을 jsp 페이지에서 사용해야 될 때 제공하는 tag 이다.

- 04행: 인증을 하지 않은 이용자에게만 보이도록 권한 조건을 걸었다.

- 05행: security 설정에서의 새로운 로그인 화면의 주소를 뜻한다.



5. 실행

메인 페이지에 들어가게되면 로그인 버튼도 이렇게 보일 것이다. 지금은 인증을 하지 않은 상태(isAnonymous())이기 때문에 로그인 버튼이 보인다. 로그인 버튼을 누르거나 접근 권한이 존재하는 페이지의 버튼을 누르면 로그인 페이지가 뜰 것이다. 하지만 무엇을 눌러서 로그인을 하느냐에 따라서 로그인에 성공하고 보이는 페이지가 다를 것이다. 우선 user 버튼(권한이 필요한 페이지)을 눌러보자.



인증을 하지 않았기 때문에 로그인 페이지가 뜰 것이다. 해당 권한에 맞는 이용자의 정보로 로그인을 해보자. user 버튼(권한이 필요한 페이지)을 눌러서 로그인 페이지에 들어왔다. 그럼 로그인에 성공하면 어느 페이지로 가게 될까?



user 페이지로 바로 들어가게 된다. user 페이지를 보고 싶어서 user 버튼을 눌렀지만, 인증이 되지 않았기에 로그인 페이지로 넘어갔다. 그렇기에 로그인에 성공한다면 가려고 했던 user 페이지에 가게 된다. 그럼 그냥 메인페이지에서 로그인버튼을 눌러서 로그인 페이지에 들어갔다면, 로그인에 성공한다면 어디로 가게 될까? 바로 메인페이지가 보일 것이다. 만약에 로그인 페이지에서 로그인에 성공한 후 가게 될 페이지를 무조건 고정적으로 하고 싶다면, security 설정에서 awlays-default-target을 설정하면 된다. 그럼 로그인 성공 후 default-target-url 로 지정한 곳으로 무조건 가게 된다. 궁금하면 테스트해보면 된다!




그럼 로그인한 상태로 메인페이지에 가보자. 인증을 한 이용자이기때문에 로그인 버튼이 보이지 않는다. 그리고 권한에 맞게 페이지에 접근이 되는 지 확인까지 해본다면 기본적인 로그인 화면 커스터마이징은 끝이 났다. 이젠 자신이 원하는 로그인 폼에 맞게 커스터마이징하면 된다.


GitHub


댓글을 달아 주세요

  1. 초보자 2019.02.13 19:33

    안녕하세요 좋은 글 덕분에 열심히 따라하고 있습니다.
    그런데, 한 가지 벽에 막혀서 진행을 못하여 이렇게 글을 남겨 봅니다

    로그인 페이지에서 로그인을 하면 성공 화면으로 넘어가지 않아서 ..
    몇 시간째 살펴보니, 주소창 뒤에 [login?error] 이런게 붙어있더군요

    그리고 다시 블로그 글을 살펴보니까, 이게 로그인 실패시 요청의 기본 값인걸 알고
    시큐리티 설정파일에 로그인 실패시 넘어가는 페이지를 만들어보니

    성공 페이지로는 못가고 계속 실패페이지만 나옵니다.. 어떻게 해결 해야 할 까요 ?

    • BlogIcon #에게 2019.02.13 19:47 신고

      안녕하세요~! 콘솔에도 어떠한 에러 로그가 뜨지 않았다면 인증에 실패한 경우인 거 같아요. 실패 페이지말고 실패 핸들러로 어떤 에러인지 보셔야할 거 같아요!! 핸들러에서 왜 인증에 실패했는 지 알 수 있거등요!

    • 초보자 2019.02.14 16:07

      감사합니다 ! 덕분에 문제 해결했습니다 ㅠㅠ

    • BlogIcon #에게 2019.02.14 16:29 신고

      오 다행이네요!

  2. 초보.. 2019.03.13 16:41

    안녕하세요 공부하면서 사소한 질문이있는데 위에 기본 로그인 html태그는 그냥 콘솔에있는내용 복사하신건가요? 파일이있다면 어느 경로에 있는지 알수있을까요?

  3. BlogIcon 사랑해 2020.05.24 23:10

    안녕하세요. 1년이 지난 지금도 잘 보고 배우고있습니다.

    1년이 지나서인지 오류가 하나 발생합니다.

    Login.page 에서, csrf 토큰을 입력하는 input 태그 안에, 적혀진 name, type, value가 있습니다.

    블로그에 입력한대로 입력할 경우, 스프링에서

    Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.

    라고 답할 경우 이렇게 바꿔보세요.


    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>

    type 과 name의 위치를 바꾸고 나서 잘 됩니다.


    근데 이유는 모르겠어요