본문으로 바로가기

아주 기본적인 기초 셋팅만으로 프로젝트에 써먹을 수 없다. 아니 기초셋팅만으로 적용하면 안된다. 마이너스 요소다!! 스프링 시큐리티를 사용하게 되면 커스터마이징이 필수다. 무작정 바꾸는 것보다는 어떤식으로 이루어지는 지 파악부터 하자. 그래야 어렵지 않게 따라올 수 있다. 스프링 시큐리티를 전부 다 아는 건 아니지만, 아는 선 안에서 최대한의 흐름을 설명하고자 한다. 나는 스프링 시큐리티를 공부하면서 많이 헤맸었다ㅠㅠ 어떤 분은 이걸 쓰고, 어떤 분은 이걸 안쓰고.. 그래서 써야 되는 건지, 말아야 되는 건지 ㅠㅠ 근데 차츰 차츰 하다보니까 말 그대로 커스터마이징이기에 자신이 필요한 기능이 있으면 쓰고 없으면 안쓰는 거다. 나는 그렇게 결론을 내렸다. 다시 차근 차근 해봐야지.


일단 공통적으로 커스터마이징을 해야 하는 부분이 있다.


1. 원하는 스타일의 로그인 화면으로 교체

2. 로그인 에러 메세지를 원하는대로 변경

3. 원하는 스타일의 접근 권한 에러 화면 변경

4. DB를 이용한 사용자 인증 절차

5. 패스워드 암호화

6. DB를 통해 접근 경로 권한 관리

7. 권한 체크 시 메모리에서 체크 


위의 7가지는 스프링 시큐리티를 쓴다면 꼭 커스터마이징해야 하는 부분이다. 로그인 화면은 로그인 전용 화면이 있을 수도 있고 팝업으로 띄워줄 수도 있고 메인에 조그맣게 달려있는 경우도 있다. 그렇기에 로그인 화면 커스터마이징에 대해서 잘 알아두면 원하는 대로 사용할 수 있다. 마찬가지로 에러에 관한 것도 자신이 원하는 스타일 대로 만들 수 있다. xml 태그만으로 사용자 인증과 접근 경로를 관리 할 수 없기 때문에 DB를 이용해서 관리하도록 만드는 게 중요하다. 



먼저, 기초 셋팅을 하면 나오는 로그인 화면 페이지가 있다. 정말 안이쁘다(...) 원하는 스타일로 화면을 바꾸기 위해선 스프링 내부에서 제공하는 로그인 페이지의 소스를 이해해야 한다.


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="${_crsf.token}">
            </tbody>
        </table>
    </form>
</body>
cs


스프링 내부에서 제공하는 로그인 화면 html이다. 초 간단 그자체이다. 별 볼 것 없어도 여기엔 우리가 꼭 알아야할 것들이 있다.


- form 태그의 action 속성값이 /login 이다. (POST방식)

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

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


이 세가지를 눈여겨 봐야한다. 로그인 화면을 새로 만들었다면 action 속성값과 input 태그의 name 값들을 그대로 적용시켜줘야 하기 때문이다. 물론 마음에 안들면 로그인 커스터마이징을 할 때 변경할 수 있다. 그리고 action 속성에 /securityPrj/login 이라고 되어있는데 /login 이라고 말한 이유는 /securityPrj/ 는 내 프로젝트의 이름이기 때문에 사용자마다 이름이 다를 것이다. 따라서 스프링 시큐리티의 form 태그의 속성값은 /login 이다.


참고로 하나 더 알려주자면, spring security 3.x 버전과 4.x 버전이 조금 달라졌다. 3.x버전의 action 속성값은 /j_spring_security_check 이고, 아이디 name은 j_username 이며 비밀번호 name은 j_password 이다. 공부는 3.x 버전으로 했는데 4.x버전으로 올리고 보니, 이 부분이 바뀌어 있었다. 달라지는 건 없다. 3가지를 규정을 따라야 한다는 것과 그것을 원하는대로 변경할 수 있다는 게 핵심이다.



위의 규칙을 알았다면, 본격적으로 기초 셋팅을 실행했을 때 사용한 스프링 내부에 있는 클래스를 우리가 직접 커스터마이징하면 된다. 하나 하나 만든다고 생각하면 된다. 먼저 spring security 설정을 한다. 로그인 폼을 설정하는데, 위의 규정을 바꾸거나 경로 설정 등등을 하게 된다. 설정을 다 하고 나면 인증 절차를 직접 상속받아 구현하게 된다.


org.springframework.security.core.userdetails.UserDetails

org.springframework.security.core.userdetails.UserDetailsService

org.springframework.security.core.authentication.AuthenticationProvider


우리는 위의 3가지의 인터페이스를 상속받아서 구현해야 한다. 위의 인터페이스들로 어떻게 인증 절차가 이루어지는 지 흐름을 확인해보자. 인증 절차가 시작되면 AuthenticationProvider 인터페이스가 실행되는데, 여기에서 DB에 있는 이용자의 정보와 화면에서 입력한 로그인 정보를 비교하게 된다. AuthenticationProvider 인터페이스에서는 authenticatie() 메소드를 오버라이드 하게 되는데, 이 메소드의 파라미터인 Authentication 으로 화면에서 입력한 로그인 정보를 가져올 수 있다. AuthenticationProvider 인터페이스에서 DB에 있는 이용자의 정보를 가져오려면, UserDetailsService 인터페이스를 사용한다. UserDetailsService 인터페이스는 화면에서 입력한 이용자의 이름(username)을 가지고 loadUserByUsername() 메소드를 호출하여 DB에 있는 이용자의 정보를 UserDetails 형으로 가져온다. 만약 이용자가 존재하지 않으면 예외를 던진다. 이렇게 DB에서 가져온 이용자의 정보와 화면에서 입력한 로그인 정보를 비교하게 되고, 일치하면 Authentication 참조를 리턴하고, 일치 하지 않으면 예외를 던진다.


이제 이 인터페이스를 원하는 대로 적당히 구현하면 된다. 3개 다 만들지 않아도 된다. AuthenticationProvider 인터페이스를 구현하지 않고 UserDetailsService 인터페이스로 시큐리티를 만들 수도 있다. 로그인 처리까지 직접 비즈니스로 구현하고 싶으면 AuthenticationProvider 인터페이스를 구현하면 된다. 인터페이스의 기능을 보고 원하는 것을 구현하면 된다. 나는 3가지 모두 구현할 것이다. 그리고 쿼리를 사용하기 위해 DAO도 만들고 Controller 또한 만든다.



스프링 시큐리티를 공부하는데 너무 복잡했고 어려워서 미칠뻔 했다. 커스터마이징이니까 정말 자신들이 원하는 대로 구현해서 더 그렇게 느낀 것 같다. 그리고 공부할 때의 버전이랑 지금 작성하면서 새로 만드는 버전이 달라서 사실 걱정도 된다. 내가 4.x 버전의 스프링 시큐리티를 무사히 끝낼 수 있을까.. 화이팅


GitHub