스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

105

Post on 22-Jul-2016

312 views

Category:

Documents


17 download

DESCRIPTION

피터 뮬라리엔 지음 | 유윤선 옮김 | 오픈소스 & 웹 시리즈 _ 026 | ISBN: 9788992939645 | 25,000원 | 2010년 12월 15일 발행 | 424쪽

TRANSCRIPT

Page 1: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션
Page 2: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션
Page 3: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

스프링

시큐리티 3

Page 4: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

iv

•목 차•

01장 보안에 취약한 애플리케이션의 해부 1보안 감사 .............................................................................................................2

예제 애플리케이션 소개 ....................................................................................2

JBCP 애완동물 상점 애플리케이션의 아키텍처 ......................................3

애플리케이션 개발도구 ..............................................................................4

보안 감사 결과 검토 ...........................................................................................5

인증 .............................................................................................................7

권한부여 .......................................................................................................8

데이터베이스 크리덴셜 보안 .....................................................................9

민감한 정보 ..................................................................................................9

전송 레벨 보안 .............................................................................................9

보안 문제 해결을 위한 스프링 시큐리티 3 사용 .......................................... 10

왜 스프링 시큐리티인가? .........................................................................10

요약 ................................................................................................................... 11

02장 스프링 시큐리티 시작하기 13핵심 보안 개념 ................................................................................................. 14

인증 ...........................................................................................................14

권한부여 .....................................................................................................15

간단한 절차를 통한 애플리케이션 보안 적용 .............................................. 18

스프링 시큐리티 XML 설정 파일 구현 ...................................................18

web.xml 파일 수정 및 스프링 DelegatingFilterProxy 추가 ..................20

Page 5: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

v

web.xml 파일 수정 및 스프링 시큐리티 XML 파일 레퍼런스 추가 .....21

부족한 부분들에 대한 고려사항 ...............................................................23

보안은 복잡한 주제다–보호된 웹 요청의 아키텍처 .................................... 25

요청은 어떻게 처리될까? ..........................................................................25

auto-con�g가 내부적으로 하는 작업들 ..................................................29

사용자들은 어떻게 인증받을까? ..............................................................29

요청은 어떻게 권한부여를 받을까? .........................................................37

요약 ................................................................................................................... 45

03장 사용자 경험 개선 47로그인 페이지 커스터마이징 ......................................................................... 47

커스텀 로그인 페이지 구현.......................................................................49

로그아웃 기능의 이해 ..................................................................................... 54

사이트 헤더의 Log Out 링크 추가 ...........................................................54

로그아웃 동작 원리 ...................................................................................55

remember me .................................................................................................. 58

remember me 옵션 구현 ...........................................................................58

remember me 기능의 동작 원리 ..............................................................59

remember me 기능은 과연 안전할까? ....................................................64

비밀번호 수정 기능 구현 ................................................................................ 71

비밀번호 변경 기능을 위한 인 메모리 저장소의 확장 ...........................72

요약 ................................................................................................................... 77

Page 6: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

vi

04장 크리덴셜 안전하게 저장하기 79스프링 시큐리티에서 데이터베이스를 사용한 인증 적용 ........................... 80

데이터베이스 인증 저장소 설정 ...............................................................80

데이터베이스 기반 인증의 동작 원리 ......................................................82

커스텀 JDBC UserDetailsService 구현 ...................................................84

기본으로 제공되는 JDBC 기반 사용자 관리 ...........................................85

JdbcDaoImpl에 대한 고급 설정 .................................................................... 87

그룹 기반 권한부여 설정 ..........................................................................88

데이터베이스 인증 시 레거시 또는 커스텀 스키마의 사용 ...................90

보안 비밀번호 설정 ......................................................................................... 93

비밀번호 인코딩 설정 ...............................................................................96

비밀번호에 소금을 조금 치는 것은 어떨까? ...........................................98

솔트 비밀번호 설정 ....................................................................................... 101

비밀번호 변경 기능 개선 ........................................................................103

커스텀 솔트 소스 설정 ............................................................................104

remember me 기능의 데이터베이스로의 이전 .......................................... 108

데이터베이스에 상주하는 remember me 토큰 설정 ...........................108

데이터베이스에 저장된 토큰은 더 안전할까? ......................................109

SSL을 사용한 사이트 보안 ........................................................................... 110

SSL 사용을 위한 아파치 톰캣 설정 ........................................................110

사이트 영역 자동 보호 ............................................................................112

요약 ................................................................................................................. 115

05장 미세 접근 제어 117애플리케이션 기능과 보안에 대해 다시 생각하기..................................... 118

애플리케이션 보안에 대한 기획 .............................................................118

사용자 역할 기획 .....................................................................................118

페이지 레벨 보안 기획 ............................................................................119

Page 7: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

vii

다양한 미세 권한부여 방법 .......................................................................... 121

스프링 시큐리티 태그 라이브러리를 활용한 조건적 콘텐츠 렌더링 .122

컨트롤러 로직을 사용한 조건적 콘텐츠 렌더링 ...................................125

페이지 내 권한부여를 설정하는 가장 좋은 방법은 무엇일까? ............126

비즈니스 티어 보호하기 ............................................................................... 128

비즈니스 메서드 보안의 기본 .................................................................129

메서드 보안의 여러 가지 방식 ................................................................131

메서드 보안의 동작 원리 ........................................................................135

고급 메서드 보안 ........................................................................................... 138

빈 데코레이터를 사용한 메서드 보안 규칙 ...........................................139

메서드 매개변수를 포함하는 메서드 보안 규칙 ...................................141

메서드 매개변수 바인딩의 동작 원리 ....................................................141

역할 기반 필터링을 통한 메서드 데이터 보호 ........................................... 143

메서드 보안에 대한 합리적인 경고 ........................................................148

요약 ................................................................................................................. 148

06장 고급 설정과 확장 151커스텀 보안 필터 작성하기 .......................................................................... 152

서블릿 필터 레벨에서의 IP 필터링 ........................................................152

커스텀 AuthenticationProvider 작성 ......................................................... 156

AuthenticationProvider를 사용한 간단한 싱글 사인 온 구현 ............156

여러 AuthenticationProvider의 결합 ....................................................162

요청 헤더를 사용한 싱글 사인 온 시뮬레이션 ......................................163

커스텀 AuthenticationProvider의 사용 시점에 대한 고려사항 .........164

세션 관리와 동시성 ....................................................................................... 165

세션 고정 보호 설정 ................................................................................165

동시 세션 제어를 통한 사용자 보안 강화 ..............................................170

동시 세션 제어의 다른 장점들 ................................................................173

예외 처리에 대한 이해와 설정 ..................................................................... 176

Page 8: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

viii

“접근 거부” 처리 설정 .............................................................................178

AccessDeniedException을 일으키는 원인 ...........................................180

AuthenticationEntryPoint의 중요성.....................................................181

스프링 시큐리티 인프라스트럭처 빈의 수동 설정..................................... 181

스프링 시큐리티 빈 의존 관계의 전체적인 설명 ..................................182

웹 애플리케이션 재설정 .........................................................................183

최소 스프링 시큐리티 환경 설정 ............................................................184

고급 스프링 시큐리티 빈 기반 설정 ............................................................ 190

세션 라이프사이클과 연관된 요소들의 조정 ........................................190

다른 서비스들에 대한 수동 설정 ............................................................191

SpEL 표현식 핸들러와 보터를 사용한 명시적 설정 .............................196

메서드 보안에 대한 빈 기반 설정 ...........................................................197

명시적인 설정의 마무리 .........................................................................197

어떤 방식의 설정을 택해야 할까? ..........................................................198

인증 이벤트 처리 ........................................................................................... 199

인증 이벤트 리스너 설정 ........................................................................200

수많은 애플리케이션 이벤트들 ..............................................................203

SpEL 표현식 핸들러의 커스텀 구현체 개발 ............................................... 203

요약 ................................................................................................................. 205

07장 접근 제어 목록 209접근 제어 목록을 활용한 비즈니스 객체 보안 ........................................... 210

스프링 시큐리티에서의 접근 제어 목록 ................................................211

스프링 시큐리티 ACL 지원 기능을 위한 기본 설정 .................................. 213

간단한 시나리오에 대한 가정 .................................................................213

HSQL 데이터베이스의 ACL 테이블 추가 ............................................214

접근 결정 관리자 설정 ............................................................................217

ACL 지원 빈 설정 ....................................................................................218

간단한 ACL 엔트리 생성 ........................................................................223

Page 9: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

ix

고급 ACL 주제들 ........................................................................................... 225

퍼미션의 동작 원리 .................................................................................225

커스텀 ACL 퍼미션 선언 ........................................................................228

스프링 시큐리티 JSP 태그 라이브러리를 활용한 ACL 활성화 ...........232

ACL을 지원하는 스프링 표현식 언어 ...................................................232

수정 가능 ACL과 권한부여 ....................................................................234

Ehcache ACL 캐싱 ..................................................................................238

일반적인 ACL 적용에 대한 고려 ................................................................. 240

ACL 규모의 확장성과 성능 모델링 .......................................................241

커스텀 개발에 들어가는 비용을 간과하지 말자 ...................................243

스프링 시큐리티 ACL을 꼭 사용해야 할까? .........................................244

요약 ................................................................................................................. 245

08장 OpenID에 대한 개방 247OpenID라는 약속의 땅 ................................................................................ 248

OpenID 가입 ...........................................................................................249

스프링 시큐리티에서의 OpenID 인증 사용 ............................................... 250

OpenID 로그인 폼 작성 ..........................................................................250

스프링 시큐리티에서의 OpenID 지원 기능 설정 ................................251

OpenID 사용자 추가 ...............................................................................251

OpenID 사용자 등록 문제 ........................................................................... 253

OpenID 식별자가 해석되는 과정 ..........................................................253

OpenID를 이용한 사용자 등록 구현 .....................................................256

어트리뷰트 교환 ............................................................................................ 261

스프링 시큐리티 OpenID에서의 AX 사용 ...........................................263

현실에서의 AX 지원과 제약 ...................................................................264

구글 OpenID 지원 ...................................................................................265

OpenID는 안전할까? ................................................................................... 266

요약 ................................................................................................................. 267

Page 10: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

x

09장 LDAP 디렉터리 서비스 269LDAP 이해 ..................................................................................................... 270

LDAP ........................................................................................................270

자주 사용되는 LDAP 어트리뷰트명 ......................................................271

임베디드 LDAP 서버 실행 .....................................................................273

기본 LDAP 연동 설정 ................................................................................... 273

LDAP 서버 레퍼런스 설정 .....................................................................273

LDAP AuthenticationProvider의 사용 .................................................274

임베디드 LDAP 문제 해결 .....................................................................275

스프링 LDAP 인증 방식 이해 ...................................................................... 276

사용자 크리덴셜 인증 .............................................................................276

사용자 역할 멤버십 판단 ........................................................................277

UserDetails의 추가 어트리뷰트 매핑 ....................................................280

고급 LDAP 설정 ............................................................................................ 281

예제 JBCP LDAP 사용자 ........................................................................281

비밀번호 비교 vs. 바인딩 인증 ...............................................................282

UserDetailsContextMapper 설정 ..........................................................285

또 다른 비밀번호 어트리뷰트의 사용 ....................................................287

UserDetailsService로서의 LDAP 활용 .................................................288

외부 LDAP 서버와의 연동 ........................................................................... 290

명시적 LDAP 빈 설정 ................................................................................... 291

외부 LDAP 서버 레퍼런스 설정 .............................................................291

LdapAuthenticationProvider 설정 ........................................................291

LDAP을 통한 마이크로소프트 액티브 디렉터리와의 연동 ................293

UserDetailsService에 대한 역할 검색 위임 ..........................................295

요약 ................................................................................................................. 296

Page 11: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

xi

10장 CAS를 활용한 싱글 사인 온 299CAS 소개 ........................................................................................................ 299

고수준 CAS 인증 흐름 ............................................................................300

스프링 시큐리티와 CAS .........................................................................301

CAS 설치와 설정 .....................................................................................302

CAS 기본 연동 설정 ...................................................................................... 303

CasAuthenticationEntryPoint 추가 ......................................................304

CAS 티켓 검증 사용 ................................................................................305

CasAuthenticationProvider를 활용한 진위성 증명 ............................307

고급 CAS 설정 ............................................................................................... 309

CAS 단언으로부터의 어트리뷰트 조회 .................................................309

어트리뷰트 조회가 왜 유용할까? ...........................................................320

추가적인 CAS의 기능들 .........................................................................321

요약 ................................................................................................................. 321

11장 클라이언트 인증서 인증 325클라이언트 인증서 인증의 동작 원리 ......................................................... 325

클라이언트 인증서 인증 인프라스트럭처 설정 ......................................... 327

공개 키 인프라스트럭처의 용도 이해 ....................................................328

클라이언트 인증서 키 쌍 생성하기 ........................................................328

톰캣 신뢰 저장소 설정 ............................................................................329

브라우저로 인증서 키 쌍 불러오기 ........................................................331

테스트 마무리 ..........................................................................................332

클라이언트 인증서 인증 문제 해결 ........................................................333

스프링 시큐리티에서의 클라이언트 인증서 인증 설정 ............................. 334

security 네임스페이스를 사용한 클라이언트 인증서 인증 설정 ........335

Page 12: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

xii

스프링 시큐리티가 인증서 정보를 사용하는 원리 ...............................335

스프링 시큐리티 인증서 인증의 동작 원리 ...........................................336

나머지 부족한 부분들 .............................................................................338

병행 인증 지원 .........................................................................................339

스프링 빈을 사용한 클라이언트 인증서 설정 ............................................ 341

빈 기반 설정의 추가 기능 .......................................................................342

클라이언트 인증서 인증 구현 시 고려 사항 ............................................... 343

요약 ................................................................................................................. 344

12장 스프링 시큐리티 확장 프로젝트 347스프링 시큐리티 확장 프로젝트 .................................................................. 348

커베로스와 SPNEGO 인증에 대한 기초 지식 ............................................ 348

스프링 시큐리티에서의 커베로스 인증 ...................................................... 351

전체적인 커베로스 스프링 시큐리티 인증 흐름 ...................................351

준비 작업 ..................................................................................................353

커베로스 관련 스프링 빈 설정 ................................................................355

SPNEGO 빈의 security 네임스페이스 설정 .........................................357

커베로스 영역에 대한 애플리케이션 서버 장비 추가 ..........................359

파이어폭스 사용자들에 대한 고려 사항 ................................................359

문제 해결 ..................................................................................................360

커베로스와의 연동을 위한 LDAP UserDetailsService 설정 .................... 362

커베로스와 폼 로그인의 병행 ...................................................................... 363

요약 ................................................................................................................. 365

Page 13: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

xiii

13장 스프링 시큐리티 3 마이그레이션 369스프링 시큐리티 2에서 마이그레이션하기 ................................................ 369

스프링 시큐리티 3의 개선 사항 ................................................................... 370

스프링 시큐리티 3에서의 설정 변화 ........................................................... 371

AuthenticationManager 설정 수정 .......................................................371

세션 관리 옵션을 위한 새로운 설정 구문 ..............................................373

커스텀 필터 설정에 대한 변경사항 ........................................................373

CustomA�erInvocationProvider에 대한 변경 사항 ...........................375

그 밖의 설정 변경 사항 ...........................................................................375

패키지와 클래스 관련 변경 사항 ................................................................. 376

요약 ................................................................................................................. 378

부록 추가 참고 자료 381JBCP Pets 예제 코드 시작하기 .................................................................... 381

사용 가능한 애플리케이션 이벤트 .............................................................. 383

스프링 시큐리티 가상 URL .......................................................................... 384

메서드 보안을 위한 명시적인 빈 설정 ........................................................ 385

논리적인 필터명의 마이그레이션 참고 사항 ............................................. 388

찾아보기..............................................................................390

Page 14: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

xiv

옮긴이 글

처음 위키북스에서 이 책의 번역 의뢰를 받았을 때는 손사래를 쳤다. 역자보다 스프링이나 스프링

시큐리티를 잘 알고 있는 개발자 분들이 많을뿐더러 스프링 시큐리티 프레임워크를 전문적으로

다루는 첫 기술 서적인데 역자 때문에 개발자들에게 누가 되지는 않을까 하는 생각이었다. 하지만

우여곡절 끝에 결국 스프링 시큐리티 번역에 착수했고 그게 벌써 4개월 전의 일이다.

결론부터 얘기하자면 이 책은 웹 보안에 대해 고민하는 모든 사람들에게 추천하는 책이다. 저자

인 피터 뮬라리엔은 스프링 프레임워크 포럼에서 고정 패널로 활동하고 있으며 오랜 시간 동안 많

은 개발자들이 올린 질문에 답하고 의견을 나누었고 개발자들이 정말 궁금한 게 뭔지 알고 있는

뛰어난 개발자다. 저자는 이 책을 읽는 독자들이 스프링 시큐리티를 최대한 쉽게 이해하게끔 각

종 다이어그램을 사용해 전체적인 흐름을 보여주고 한 주제를 한 번에 하나씩 찬찬히 설명한다. 저

자의 오랜 경험에서 나온 혜안은 스프링 시큐리티를 공부할 때 독자들이 피부로 느끼는 궁금증을

해소하기에 부족함이 없으며 저자가 중간 중간 주는 팁은 보안 담당 개발자라면 항상 염두에 둬야

할 내용으로 가득 차 있다.

이 책은 크게 스프링 시큐리티 프레임워크의 기본 지식을 다루는 전반부(1~7장), 외부 시스템

연동을 다루는 후반부(8~12장), 마이그레이션을 다루는 13장으로 구성된다. 이 책은 레시피 형태

의 책은 아니고 처음부터 하나씩 내용을 익히다 보면 어느새 기본 지식을 튼튼히 다질 수 있게 해

주는 책이다.

역자는 스프링 시큐리티를 이루는 두 축인 인증(authentication)과 권한부여(authorization)에 대

한 설명이 이 책의 핵심이라고 생각한다. 우선 이 두 가지에 대한 설명을 이해하고 나면 이후의 설

명은 이러한 인증, 권한부여를 어디까지 미세하게 적용하고, 어느 부분과 연동할 것인가에 관한 상

세 설명으로 볼 수 있다. 역자는 이 책의 인증과 권한부여에 대한 도입부 설명이 마치 아리아드네

의 실(Ariadne’s thread)과 같다고 느꼈다. 그리스 로마 신화에서 테세우스가 미노타우로스의 미로

에서 아리아드네의 실을 붙잡고 미로를 무사히 빠져나올 수 있었던 것처럼 독자들도 인증과 권한

부여에 대한 두 주요 주제에 대한 이 책의 설명을 이해하기만 하면 아무리 복잡한 권한 적용도 스

프링 시큐리티를 통해 손쉽게 할 수 있을 거라 확신한다.

Page 15: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

xv

스프링 시큐리티는 말 그대로 스프링 기반의 보안 프레임워크다. 이 책에서는 스프링에 대한 기

본 지식을 전제하지만 스프링에 대한 최소한의 지식(주로 스프링 웹 MVC)을 갖춘 자바 개발자라

면 누구든지 이 책을 볼 수 있다. 스프링 시큐리티는 스프링의 CoC 원칙을 그대로 계승한 프레임

워크다. 복잡한 설정이 싫은 개발자라면 관례를 따라 개발하면 되고, 프로젝트에서 복잡한 보안

규칙을 적용해야 한다면 단계적인 보안 선언을 통해 원하는 수준의 보안을 적용할 수 있다. CoC

원칙으로 인해 초기 기본 설정에 대한 진입 장벽은 매우 낮으며 이후 프로젝트와 관련한 보안 규

칙을 원하는 대로 커스터마이징하거나 오버라이드할 수 있다. 이 책이 웹 보안으로 고민하는 많은

분들에게 아리아드네의 실 같은 책이 되기를 바란다.

감사의 글

먼저 이 책의 번역을 저에게 의뢰해 주신 위키북스 박찬규 사장님, 김윤래 편집장님께 감사하다.

이 분들이 없었다면 이렇게 좋은 책이 아직 빛을 못 보고 있었을 것이다. 또 투박한 원고를 읽기 좋

은 문장으로 교정해 주신 이대엽 님께도 감사를 드린다. 사실상 공역이라고 해도 무방할 만큼 많

은 표현을 다듬어 주셨고 고쳐 주셨다. 또 이 책에 베타리더로 참여해 준 성종천 님에게도 감사하

다. 성종천 님은 항상 역자의 책에 베타리더로 참여해 주시는 고마운 분이다. 그리고 좋은 책을 집

필해 준 저자 피터 뮬라리엔에게도 감사하고 스프링 시큐리티 프레임워크의 모태를 만든 벤 알렉

스에게도 감사하다. 벤은 좋은 프레임워크를 만들었고, 피터는 그 프레임워크에 대한 좋은 책(이

책)을 썼다. 마지막으로 사랑하는 가족, 그리고 하나님께 감사하다.

- 유윤선

Page 16: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

xvi

추천의 글

스프링 시큐리티는 2003년 벤 알렉스에 의해 시작됐고 당시에는 “스프링을 위한 아시지 시큐리티

시스템(Acegi Security System for Spring)” 또는 “아시지 시큐리티(Acegi Security)”라고 불렀다. 시간

이 흐르면서 벤의 리더십하에 아시지 시큐리티는 보다 정교한 인증 및 접근 제어 시스템으로 발전

했고 스프링 프레임워크를 기반으로 하는 애플리케이션의 표준 보안 솔루션으로 폭넓게 활용됐

다. 개발 초기에는 소스에 공헌하는 개발자들이 전 세계 각지에 흩어져 있었고 그 중에는 개발에

참여했다가 이내 나가는 사람들도 있었지만, 초기에 참여했던 사람들 중 일부는 현재까지도 개발

에 공헌하고 있다. 나는 이 프로젝트에 2004년에 처음으로 참여하기 시작했다. 프로젝트에 대한

논의는 개발자들의 시차로 인해 하루 중 다소 엉뚱한 시간대에 주로 진행됐고, 내가 벤을 직접 만

날 수 있었던 건 벤이 고향인 호주를 떠나 유럽으로 여행을 온 시점으로, 내가 프로젝트를 시작한

지 1~2년 후의 일이었다. 현재 나와 벤은 모두 스프링소스(SpringSource)와 스프링 시큐리티 개발

을 전업으로 하고 있으며, 현재 이 두 프레임워크는 전 세계 모든 주요 애플리케이션에서 사용되고

있다.

스프링 시큐리티는 항상 배우기 어렵다는 꼬리표를 달고 있다. 스프링 시큐리티는 기본 옵션에

서 벗어난 요구 사항들을 충족시키기 위해 코드를 커스터마이징하거나 확장할 수 있는 "현장 확장

형" 프레임워크다. 스프링 시큐리티를 사용하면 대부분의 것들이 가능하지만 때로는 특정 요구 조

건을 충족시키기 위해 내부 로직을 깊이 있게 이해해야 할 필요도 있다. 이러한 깊이 있는 이해는

주로 경험을 통해서만 얻을 수 있다. 스프링 시큐리티 2.0에서 도입된 XML 네임스페이스 설정 옵

션은 사용자들이 간단한 사용 사례를 바로 적용할 수 있게 해 주지만 스프링 시큐리티 프레임워크

에서 제공하는 모든 기능을 제대로 활용하려면 상당한 학습이 필요하다. 본래 보안이라는 주제가

익숙하지 않은 다양한 개념들이 사용되는 복잡한 주제이기도 하지만 애플리케이션의 보안을 책

임지는 소프트웨어 개발자는 대부분의 개발자들이 잘 알지 못하는 기술과 더불어 프로토콜에 대

해서도 해박한 지식을 갖고 있어야 한다는 점에서 이러한 어려움은 더 크게 느껴지기도 한다. 하지

만 일정에 쫓기는 개발자에게 최신 지식도 함께 갖출 것을 기대하는 데에는 무리가 있다.

이 책은 스프링 시큐리티를 전문적으로 다루는 첫 번째 책이면서 동시에 스프링 시큐리티 입

문을 돕기 위해 다양한 실전 예제와 함께 스프링 시큐리티의 내부 구조, 커스터마이징, CAS,

OpenID, 커베로스(Kerberos) 등과 같은 싱글 사인 온 시스템과의 통합 등 다양한 분야를 심도 있

게 다루고 있다. 때마침 프레임워크의 주요 부분이 재설계된 스프링 시큐리티 3.0이 출시되면서 스

Page 17: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

xvii

프링 시큐리티 3.0을 다루는 이 책이 출시된 것을 다행으로 생각한다.

피터는 스프링과 스프링 시큐리티 프레임워크와 관련해 다양한 경험을 가지고 있는 개발자이며

스프링 시큐리티 포럼에서 고정 패널로 활동하면서 수백 명의 사용자들이 올린 질문에 답하고 초

보자들이 느끼는 어려움을 풀어주는 데 자신의 경험에서 나온 조언을 아끼지 않는다. 피터의 오랜

경험에서 우러나온 그의 혜안 중 상당 부분은 이 책에도 그대로 녹아 있다. 많은 경우 사람들이

느끼는 어려움은 스프링 시큐리티와 직접적으로 관련된 것이라기보다는 외부 기술과 연관된 문

제다. 개발자들이 원하는 것은 SSL 설정, LDAP 디렉터리, 서로 다른 OpenID 프로바이더를 사용

하는 실질적인 측면에 대해 잘 모르더라도 또는 이런 문제들을 어떤 주제로 분류하든 상관없이 실

제 프로젝트에서 이런 문제를 가능한 한 빨리 해결하는 것이다. 바로 이 부분이 다른 프로젝트 레

퍼런스 매뉴얼과 이 책이 차별성을 갖는 부분이다. 피터는 이와 관련한 다양한 주제에 대해 설명하

고 어떤 일이 일어나고 여러분이 어떤 작업을 해야 하는지를 충실히 전달해 준다.

이 책을 집필하기까지 많은 노력이 있었다. 나는 이 책이 스프링 시큐리티를 처음 시작하는 사용

자뿐 아니라 스프링 시큐리티에 대한 기존 지식을 더 넓히고 싶은 독자에게 더할 나위 없이 좋은

자료가 되리라 생각한다. 이 책에서 배우는 내용들은 훗날 여러분에게 큰 도움이 될 것이다. 스프

링 시큐리티는 오픈소스 스프링 포트폴리오의 주요 프레임워크이며 앞으로도 꾸준히 스프링 개

발자들이 사용해야 할 프레임워크다. 앞으로 스프링 시큐리티의 미래는 어떻게 될까? 스프링 시

큐리티는 앞으로 (벤의 요즘 주요 관심사인 스프링 루 1 와 같은) 기존 스프링 프로젝트와 통합됨은

물론 프로젝트 코어와 함께 스프링 시큐리티 확장 프로젝트(12장에서 자세히 살펴본다)를 통해

새로운 기술들을 혁신적으로 지원하게 될 것이다. 물론 이 과정에서 여러분은 직접 개발에 참여하

거나, 패치를 보내거나 새로운 확장 프로젝트를 제안함으로써 프로젝트 개발에 참여할 수 있다.

프로젝트에 행운이 함께하길 바라며 스프링 시큐리티와 함께 즐거운 시간을 보내길 바란다.

- 루크 테일러, 스프링 시큐리티 프로젝트 리더

1   (옮긴이) 실제로 스프링 시큐리티 프레임워크를 만든 벤 알렉스는 스프링 루 프레임워크를 만든 장본인이기도 하다.

Page 18: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

xviii

지은이 소개

피터 뮬라리엔(Peter Mularien)

피터 뮬라리엔은 현재 중급 규모의 엔터프라이즈 소프트웨어 벤더의 설계자이자 수석 개발자이

다. 피터 뮬라리엔은 웹 기반 애플리케이션 개발에 종사했으며 다양한 분야의 기술 관련 컨설턴트

로 일하면서 10년 이상 주요 기업과 소형 기업을 대상으로 컨설팅했다. 자바와 스프링을 다루고 있

는 그의 블로그인 “It’s Only So�ware”는 복잡한 기술들에 대해 개발자들이 쉽게 이해할 수 있는

내용과 튜토리얼로 많은 인기를 얻고 있다. 피터는 스프링 커뮤니티 포럼의 유명 패널 중 한 명이며

전체 게시물을 올린 사람들 중 상위 30위 안에 랭크돼 있다.

밤 늦은 시간과 주말에도 함께 시간을 보내지 못해 미안한 나의 아내와 아이들에게 사랑과 감사

의 인사를 전한다.

Page 19: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

xix

리뷰어 소개

스콧 바타글리아(Scott Battaglia)

스콧 바타글리아는 러트거스(Rutgers) 대학에서 컴퓨터 공학 학사와 석사 학위를 받았으며 현재

러트거스 대학의 MBA 과정과 University of Medicine and Dentistry에서 공중 보건학 석사 과

정을 마치는 중이다. 그는 지난 6년간 러트거스 대학에서 소프트웨어 개발자로 일했으며 초기에

는 설계-엔지니어링 팀에서 비즈니스 애플리케이션, 설계, 개발 프로세스에 참여했고 현재는 IDM

팀에서 스프링 시큐리티, CAS, OpenRegistry와 같은 오픈소스 프로젝트에 참여하면서 러트거

스의 IDM 재설계 프로젝트에도 참여하고 있다. 스콧은 Jasig, Educause 같은 다양한 컨퍼런스에

서 스프링 시큐리티, 모범 개발 방식, CAS, IDM, 자바 개발 등 다양한 주제에 대해 발표했다. 스

콧은 Internet2의 ACAMP 프로그램 운영 위원회 위원과 Jasig 컨퍼런스의 운영 위원으로도 활동

했다. 현재는 프로젝트의 수석 설계자로서 Jasig CAS 운영 위원회의 의장을 맡고 있다. 스콧은 여

가 시간이 생기면 마라톤을 뛰거나 하이킹, 사진 찍기, 새로운 라이브러리와 도구 학습, 뉴 저지의

Make-A-Wish 재단에서 봉사 활동으로 시간을 보낸다.

카를로스 산체스(Carlos Sanchez)

카를로스 산체스는 10년 가까이 오픈소스 개발에 참여했다. 카를로스는 특히 e-커머스, 금융 서

비스, 통신, 소프트웨어 개발 등 다양한 산업 분야의 비즈니스 모델과 관련한 문제를 해결하는 것

을 전문으로 하고 있다. 카를로스는 아파치 메이븐 프로젝트 관리 위원회(PMC), 아파치 소프트웨

어 재단, 이클립스 재단의 회원이며, 스프링 시큐리티 프로젝트의 소스 커미터이자 로스엔젤레스

의 G2iX에서 수석 솔루션 설계자로 일하고 있다.

카를로스는 스페인 코루냐 대학에서 컴퓨터 공학 학위를 취득했으며 여행을 하면서 새로운 장소

를 찾아내는 것을 좋아한다.

카를로스는 메이븐2에 대한 자신의 첫 번째 책인 “Better Builds with Maven”을 공저했으며 이

프로젝트와 관련한 다른 여러 책의 리뷰어로 참여한 바 있다.

Page 20: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

xx

서 문

스프링 시큐리티 3의 세계에 온 것을 환영한다! 스프링 시큐리티라는 주제를 위해 전면을 할애

한 이 책이 여러분의 손에 쥐어져 있다는 사실에 필자는 한없이 기쁨을 느끼고 이 책이 보안이라

는 흥미로운 주제와 관련해 여러분의 궁금증을 모두 풀어줄 수 있는 책이 되기를 바란다. 여기서

는 앞으로 우리가 살펴볼 내용을 소개하고 그 과정에서 여러분에게 도움을 줄 사항들도 조언해

주겠다.

이 책을 다 읽을 때쯤이면 여러분은 스프링 시큐리티의 아키텍처와 스프링 시큐리티를 웹 기반

애플리케이션과 연동하는 방법, 스프링 시큐리티를 외부 인증 및 권한 시스템과 연동하는 방법에

익숙해질 것이다.

이 책은 크게 두 부분으로 나뉜다. 전반부에서는 (1장부터 7장) 웹 애플리케이션 관점에서 스프

링 시큐리티를 처음부터 끝까지 (아주 기본적인 초기 설정부터 시작해 고급 주제인 접근 제어 목

록 설정에 이르기까지) 다룬다. 후반부에서는 (8장부터 12장) 더 큰 생태계에서 본 스프링 시큐리

티를 다루면서 OpenID, 마이크로소프트 액티브 디렉터리, LDAP 등의 기본적인 외부 시스템과

의 연동 방법을 보여준다. 마지막 장에서는 스프링 시큐리티 2에서 스프링 시큐리티 3로 옮겨가면

서 발생하는 마이그레이션 이슈를 소개한다.

이 책에서는 기본적인 스프링 웹 MVC 기반 애플리케이션을 가지고 이 책에 담긴 개념들을 설

명한다. 이 애플리케이션은 아주 단순하고 쉬운 애플리케이션으로, 일부러 아주 적은 기능만을 탑

재하고 있다. 이 애플리케이션의 목적은 애플리케이션 개발의 복잡한 측면에 얽매이지 않고 스프

링 시큐리티의 개념에 집중할 수 있도록 돕는 데 있다. 애플리케이션의 예제 소스 코드를 미리 읽

어본 후 예제를 따라해보면 이 책을 이해하기가 한결 수월할 것이다. 책의 시작과 관련한 팁은 1장,

‘보안에 취약한 애플리케이션의 해부’와 부록, ‘추가 참고 자료’에서 확인할 수 있다.

이 책에서 다루는 내용

1장, ‘보안에 취약한 애플리케이션의 해부’에서는 우리가 만든 e-커머스 사이트의 가상 보안 감사

를 통해 스프링 시큐리티를 제대로 적용해 해결할 수 있는 흔히 발생하는 문제점들을 짚어본다.

이 장에서는 기본적인 보안 관련 용어와 예제 애플리케이션 구동에 필요한 사전 지식을 다룬다.

Page 21: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

xxi

2장, ‘스프링 시큐리티 시작하기’에서는 기본 설정과 폼 기반 인증 설정을 살펴보고, 이어서 웹 요

청 보안을 위해 스프링 시큐리티가 하는 작업을 처음부터 끝까지 전체적으로 살펴본다.

3장, ‘사용자 경험 개선’에서는 스프링 시큐리티에서 제공하는 사용자와 밀접한 기능들을 통해

보안이 적용된 사이트에서 사용자 경험을 향상시키는 법을 살펴본다. 이 장에서는 remember me

기능, 스타일이 적용된 로그인 페이지, 로그아웃, 비밀번호 변경 기능 등을 소개한다.

4장, ‘크리덴셜 1 안전하게 저장하기’에서는 스프링 시큐리티 API를 사용해 JDBC 데이터베이스

에 사용자 정보를 안전하게 저장하는 데 필요한 주요 설정에 대해 설명한다. 비밀번호를 안전하게

저장하는 것과 관련한 주요 보안 개념도 이 장에서 소개된다.

5장, ‘미세 접근 제어’에서는 페이지 내 권한 체크(페이지 부분 렌더링)와 스프링 시큐리티의 메

서드 보안 기능을 사용한 비즈니스 레이어 보안에 대해 설명한다.

6장, ‘고급 설정과 확장’에서는 커스텀 서블릿 필터, 커스텀 인증 프로바이더, 커스텀 예외 처리

등 스프링 시큐리티 구현과 관련한 편리한 기능들을 소개한다. 이 장에서는 세션 고정과 현재 연

결된 세션 제어에 대해 분석하고 필요한 설정을 적용해 이러한 내용을 사이트에 적절히 반영하는

법을 설명한다. 마지막으로 빈 기반 설정을 사용해 책에서 설명한 스프링 시큐리티의 모든 기능을

적용하는 법을 보여준다.

7장, ‘접근 제어 목록’에서는 스프링 시큐리티 접근 제어 목록(ACL) 모듈을 사용하는 비즈니스

객체 레벨의 보안 개념과 이를 기본적으로 구현하는 법을 설명한다. 접근 제어 목록 모듈은 다양

한 비즈니스 보안 문제에 유연하게 적용할 수 있는 강력한 모듈이다.

8장, ‘OpenID에 대한 개방’에서는 OpenID를 사용한 로그인과 사용자 정보 교환을 살펴보고

OpenID를 사용할 수 있는 시스템에서의 논리적인 보안 흐름에 대해 전체적으로 설명한다.

9장, ‘LDAP 디렉터리 서비스’에서는 LDAP 디렉터리 서버와 애플리케이션을 연동하는 방법과

함께 LDAP 데이터와 다양한 방식으로 연동을 시도할 때 도움되는 실전 팁들을 소개한다.

10장, ‘CAS를 활용한 싱글 사인 온’에서는 CAS(Central Authentication Service)와 연동해 스프링

시큐리티를 사용한 애플리케이션에서 싱글 사인 온 기능을 지원하는 방법을 설명한다.

11장, ‘클라이언트 인증서 인증’에서는 관리된 인증서가 애플리케이션의 보안 레이어를 추가시킬

수 있는 비즈니스 시나리오를 상정해 X.509 인증서 기반 인증을 사용하는 법을 보여준다.

1   (옮긴이) 인증에 사용되는 사용자명, 비밀번호와 같은 개인정보를 통칭하는 용어다. 이 책에서는 주로 크리덴셜이 비밀번호와

같은 의미로 사용되지만 예를 들어 인증서 인증 시에는 인증서 자체가 크리덴셜이 된다.

Page 22: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

xxii

12장, ‘스프링 시큐리티 확장 프로젝트(Spring Security Extensions)’에서는 유닉스 커베로스나 마

이크로소프트 액티브 디렉터리(Microso� Active Directory)와 연동되는 커베로스 연동 레이어를

스프링 시큐리티 애플리케이션의 사용자 인증에 사용할 수 있게 해 주는 스프링 시큐리티 커베로

스 확장 프로젝트에 대해 설명한다.

13장, ‘스프링 시큐리티 3 마이그레이션’에서는 스프링 시큐리티 2와 스프링 시큐리티 3 사이의

주요 차이점을 살펴본다. 특히 주의할만한 설정 변경, 클래스와 패키지 마이그레이션, 새로운 주요

기능들을 설명한다. 스프링 시큐리티 2에 익숙한 독자라면 13장을 먼저 읽을 것을 권장한다. 13장

을 먼저 읽는다면 예제에 사용된 코드를 기존에 사용하던 방식의 코드로 이해하기가 한결 쉬워질

것이다.

부록, ‘추가 참고 자료’에서는 저자들이 도움이 된다고 판단하지만 이 책의 지면에 싣기에는 다

소 광범위한 주제에 속하는 (대부분 공식 문서에 나오지 않는) 참고 자료들을 소개한다.

참고사항

오픈소스 소프트웨어 시대에는, 특히나 오늘날 같이 개발이 빠르게 진행되고 개발 환경이 열려 있

으며 인터넷의 집단 지성이 힘을 발휘하는 시대에는, 책은 오래될수록 그 가치를 잃기 마련이다. 우

리는 스프링 시큐리티가 발전함에 따라 온라인에 추가되는 내용들을 이 책에 수록하려고 노력했

지만, 이 책에 사용된 예제들은 스프링 시큐리티 3.0.x 시절에 작성된 것으로 여러분이 사용하는

버전에 따라 약간씩 수정해야 할 수도 있다.

스프링 시큐리티 프레임워크에 대한 개념적인 설명이나 일반 보안 관련 주제 및 연동에 대

한 부분들은 앞으로 시간이 흐르더라도 크게 내용이 바뀌거나 정확도가 떨어지지는 않을 것이

다. 우리는 언제든 여러분의 제안이나 피드백을 들을 준비가 돼 있다. 피드백을 전하려면 Packt

출판사의 웹 사이트를 이용하거나 이 책과 관련해 필자가 운영하는 사이트인 http://www.

springsecuritybook.com/을 방문해 주기 바란다.

이 책에서는

스프링 시큐리티 프레임워크와 관련한 실질적이고 예제에 기반한 구현 설명을 한다. ▶

여러분이 자바 웹 애플리케이션 개발과 그에 필요한 개념들을 이미 알고 있다고 가정한다. ▶

이 책에 사용된 예제 애플리케이션은 스프링 MVC 기술을 사용해 개발됐기 때문에 이 책

에서는 여러분이 스프링 MVC의 동작 원리에 대한 최소한의 지식이 있다고 가정한다(이

Page 23: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

xxiii

책에서는 고급 스프링 MVC 기술을 사용하지 않으므로, 스프링 MVC를 전에 사용해 본

경험이 없고 이론적으로만 알고 있는 개발자라도 책의 내용을 따라오는 데는 무리가 없으

리라 생각한다).

예제 코드를 따라 하고 책의 내용을 그대로 테스트해 볼 것을 권장한다. 물론 책을 읽으면 ▶

서 책에 나온 코드를 직접 개발 환경에서 테스트하는 게 필수 사항은 아니지만 이렇게 책

의 예제를 따라 해본다면 훨씬 더 많은 것을 얻을 수 있을 것이다.

이 책에서 다루지 않는 내용

이 책에서는 상업용 웹 애플리케이션의 보안 적용에 필요한 모든 절차에 대해서는 설명 ▶

하지 않는다. 이 책에서는 스프링 시큐리티에서 제공하는 툴과 세션 고정 공격과 같은 특

정 기술을 다루고 실습하지만, 보안 위협과 관련한 전체 주제에 대한 설명은 이 책의 범위

를 벗어난다. 필요한 경우에는 웹 애플리케이션 보안 규칙과 관련한 추가 참고 자료를 알

려줄 것이다. 이 분야에서 여러분이 사용할 수 있는 가장 중요한 툴은 다름 아닌 여러분의

두뇌다.

이 책에서는 이 책과 별로 관련 없는 주제인 AOP, 스프링 MVC, LDAP, CAS, 윈도우 액티 ▶

브 디렉터리 등에 대해 설명하지 않는다. 물론 특정 내용을 설명하면서 이들 주제에 대해

간단히 언급하기는 하지만, 이들 주제는 제대로 설명하려면 개별 기술 서적 한 권이 필요한

주제들이다. 여러분은 똑똑해질 필요가 있다. 프로젝트에 고급 기술을 사용해 많은 투자

를 했다면 이들 주제를 하나씩 배워 볼 필요가 있다. 이러한 주제들을 배우는 데 들인 시간

만큼 여러분도 많은 것을 얻게 될 것이다.

이 책에서는 모든 어트리뷰트, 엘리먼트, API 호출에 대해 하나하나 모두 설명하지는 않는 ▶

다. 루크의 스프링 시큐리티 팀과 커뮤니티에서는 많은 시간을 할애해 레퍼런스 매뉴얼을

항상 최신 버전으로 업데이트하고 소스 코드의 주석도 충실히 기재하고 있다. 이러한 소스

코드에 익숙해지고 JavaDoc을 읽는 것을 두려워하지 말기를 바란다. 그런 다음 이 책에 사

용된 예제와 다이어그램을 접하면 모든 부분이 완벽히 이해될 것이다. 이 책에서는 처음부

터 끝까지 가장 일반적인 스프링 시큐리티의 사용 사례를 다루려고 노력했다. 이러한 접근

방식을 택한 이유는 가장 일반적인 주제를 독자가 완벽히 이해할 때에만 독자들이 추가로

나오는 주제들도 쉽게 이해할 수 있다고 판단하기 때문이다. 이 책에서 여러분이 가장 좋아

하는 스프링 시큐리티의 기능을 상세히 다루지 못한 점에 대해서는 미리 양해를 구한다.

Page 24: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

xxiv

이 책에서 사용한 표기 관례에 대해 설명하겠다.

XML 엘리먼트는 항상 다음과 같이 꺽쇠 괄호로 표기했다 ▶ <intercept-url>. 이것은 XML

엘리먼트를 XML 어트리뷰트와 구분하기 위한 것으로 XML 어트리뷰트에는 다음과 같은

코드 폰트를 사용하고 있다 use-expressions.

간결한 표기를 위해 텍스트나 도면에서 전체 클래스명을 표기할 때는 ▶ org.springframework.

security라는 패키지명을 o.s.s로 줄여서 표기했다. 하지만 혼란을 피하기 위해 코드와 설

정 예제에는 전체 패키지명을 그대로 사용했다. 클래스 이름을 처음 언급할 때는 전체 패

키지명을 포함한 클래스명을 사용해 궁금한 독자들이 클래스를 한 번에 알아볼 수 있게

했다.

또 간략한 표기를 위해 자바 코드 예제에서 주로 ▶ import 명령문 표기를 생략하고 경우에

따라 중요하지 않거나 반복되는 설정 엘리먼트는 <!-- ... -->와 같은 XML 주석으로 대체

했다. 이렇게 한 것은 해당 예제와 관련한 설정 부분을 설명하는 데 집중하기 위해서다. 따

라서 실제 설정에 이와 같이 XML 주석을 사용해서는 안 된다.

전체 패키지를 포함한 클래스명이나 XML 설정 라인의 길이로 인해 어떤 코드 예제에서는 ▶

줄이 다음 줄로 넘어가는 경우도 있다. 우리는 여러분이 어느 부분에서 줄 바꿈이 허용되

고 어느 부분에서 줄 바꿈이 허용되지 않는지와 관련해 자바와 XML에 대해 충분히 알고

있으리라 생각한다. 소스 줄바꿈과 관련해 확실하지 않은 부분은 다운로드할 수 있는 이

책의 소스 코드를 참고하자.

감사의 말

기술 서적을 집필하는 데는 믿을 수 없을 만큼의 연구와 검토, 지원, 그리고 무엇보다도 시간이 필

요하다. 특히나 기술 서적 집필과 동시에 직장에서 고된 정규 업무를 담당하고 아이에게도 좋은

아빠가 되기란 여간 어려운 일이 아니었다. 그래서 필자는 이 책을 쓰면서 다시는 기술 서적을 건성

으로 읽지 않기로 다짐했다.

기술 서적을 처음 집필하는 필자가 이 책을 내기까지 오랫동안 지원을 아끼지 않은 Packt 출판

사 직원들에게 감사 드린다. 스티븐 윌딩은 이 책에 대한 아이디어를 필자에게 주었고, 푸비 네어

는 필자의 집필을 독려했으며 네하 팟와리와 미타 라자니는 최종 리뷰와 편집을 도와주었고, 파트

리샤 웨어는 책의 집필 전 과정에서 도움을 주었다. 루크 테일러와 벤 알렉스, 그리고 스프링 시큐

리티/아시지 프로젝트에 참여하는 다른 기여자들에게도 감사의 인사를 전한다. 여러분들 덕분에

Page 25: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

xxv

엄청나게 복잡하지만 흥미롭고, 다른 무엇보다도 유용한 프레임워크가 탄생하게 됐다. 책의 기술

리뷰을 도와준 스콧 바타글리아와 카를로스 산체스에게도 감사의 말을 전한다. 두 분의 소중한

피드백은 책에 많은 도움이 됐다.

나의 친구이자 오랜 동료인 마크 피츠에게도 감사한다. 마크는 책이 집필되는 동안 내게 격려의

말을 아끼지 않았다. 마크, 네 책에는 내가 친필 사인을 해줄게! 나를 격려해 준 가네스 머시, 매트

올슨, 크리스 허긴스에게도 감사한다. 사업가적 영감을 준 존 번즈, 티에리 휴버트, 매트 그로에게

도 감사한다. 아울러 롤링 스톤즈의 부두라운지 음반에 참여한 모든 분께 감사를 드린다. 부두라

운지는 길이길이 남을 명반이다. 또 aTao 음향 시스템 직원들에게도 감사의 말을 전한다.

스프링 시큐리티 지원 포럼에서 기여하는 회원들에게도 감사한다. 포럼에 올라오는 질문에 답

하면서 스프링 시큐리티에 대해 필자는 더 깊게 이해할 수 있게 됐고 그 결과 책의 내용도 그만큼

풍성해지고 완성도도 높일 수 있었다.

마지막으로 (다시 한 번!) 아내와 아이들에게 감사한다. 다른 무엇보다도 내가 자투리 시간에 책

을 쓰는 일을 눈감아주고 지지해 준 데 대해 감사한다. 그리고 아이들 잠자리에 읽어주기에는 적

당하지 않은 책이라 아이들에게는 미안할 따름이다.

이 책의 대상 독자

이 책은 웹 프로젝트와 웹 애플리케이션을 개발하는 자바 개발자들을 위한 책이다. 이 책에서는

여러분이 자바, XML, 스프링 프레임워크에 대한 기본 지식을 가지고 있다고 가정한다. 스프링 시

큐리티를 처음 시작하는 개발자라도 이 책의 내용을 활용하는 데에는 문제가 없다.

조판 관례

이 책에서는 서로 다른 정보를 구분하기 위해 다양한 텍스트 스타일을 사용하고 있다. 아래는 이

책에 사용된 스타일 예시와 그와 관련한 설명이다.

텍스트에 사용된 코드는 다음과 같이 표기한다 “include 디렉티브를 사용하면 다른 컨텍스트를

포함시킬 수 있다.”

코드 블록은 다음과 같이 표기한다.

<c:url value="/j_spring_security_logout" var="logoutUrl"/>

<li><a href="${logoutUrl}">Log Out</a></li>

Page 26: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

xxvi

특정 코드 블록을 강조할 때는 관련 라인이나 항목들을 볼드체로 표기한다.

<http auto-config="true" use-expressions="true">

<logout invalidate-session="true"

logout-success-url="/"

logout-url="/logout"/>

</http>

이 책에서는 특별한 정보를 담고 있는 내용은 아래와 같이 표현한다.

알아두세요!▶▶

알아두세요! 경고문 또는 주요 참고 사항은 다음 상자를 통해 표시한다.!

잠깐만요!▶▶

잠깐만요! 팁과 트릭은 다음과 같이 표시한다.

독자 피드백

독자들이 주는 피드백은 언제든 환영한다. 이 책에 대한 여러분의 생각, 이 책의 어떤 점이 좋았

고 어떤 점이 싫었는지 등을 우리에게 알려주기 바란다. 독자 여러분의 피드백은 우리가 여러분이

가장 많은 것을 얻을 수 있는 새로운 책들을 개발하는 데 많은 도움이 된다. 피드백을 보낼 때는

[email protected]으로 이메일을 보낼 때 메일 제목에 책 제목을 쓰면 된다. 필요한 책이

있고 우리 출판사에서 출판해 주기를 바란다면 www.packtpub.com에서 SUGGEST A TITLE 양

식을 작성해 쪽지를 남기거나 [email protected]으로 이메일을 보내면 된다.

여러분이 잘 알고 있는 분야가 있고 책을 집필하거나 책에 기여하고 싶다면, www.packtpub.

com/authors에서 안내 문구를 확인할 수 있다.

독자 지원

이제 여러분은 Packt 출판사 서적의 자랑스러운 독자이므로 여러분을 돕기 위해 출판사에서는 다

음과 같은 사항들을 지원하고 있다.

Page 27: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

xxvii

이 책의 예제 코드는 아래 링크 에서 직접 다운로드할 수 있다.

https://www.packtpub.com/sites/default/files/downloads/9744_Code.zip

다운로드한 파일에는 해당 파일의 사용 방법도 담겨 있다.

위키북스 홈페이지 www.wikibook.co.kr에서도 소스 코드를 다운로드할 수 있다.

오탈자

책의 정확성을 담보하기 위해 많은 노력을 기울이고 있지만 실수가 생기기도 한다. 책에서 실수

(텍스트나 코드상의 실수)를 찾아내면 우리에게 알려주면 고맙겠다. 이렇게 오탈자를 신고해 주면

다른 독자들의 고생을 덜어줌은 물론 이 책의 다음 버전을 출간할 때 책의 오류가 수정되도록 도

와줄 수 있다. 오탈자를 찾아낸 경우에는 http://www.packtpub.com/support에서 책의 제목을

고른 후 let us know 링크를 클릭해 오탈자에 대한 내용을 구체적으로 입력해 주기 바란다. 오탈자

가 확인되면, 여러분이 보낸 의견이 채택돼 기존 오탈자 목록에 오탈자가 새로 추가된다. 기존 오

탈자 목록은 http://www.packtpub.com/support에서 책의 제목을 선택해 확인할 수 있다.

번역서에 대한 오탈자나 오류는 [email protected]로 연락 바란다.

불법 복제

인터넷 공간에서 저작물을 불법 복제하는 일은 매체를 막론하고 일어나고 있는 문제다. Packt 출

판사는 저작권과 라이선스 보호 문제를 아주 심각하게 생각한다. 인터넷상에서 어떤 형태로든 우

리 출판사의 불법 복제물을 발견하거든 바로 조취할 수 있도록 우리에게 웹 사이트의 주소나 이름

을 알려주기 바란다. 불법 복제물로 의심되는 자료가 링크된 곳이 보이면 copyright@packtpub.

com로 연락하면 된다. 우리 출판사의 저자들과 출판사의 권리를 불법 복제로부터 지켜 줌으로써,

계속해서 양질의 콘텐트를 제공할 수 있게 해 주는 독자 여러분들께 감사 드린다.

질문

이 책과 관련한 질문은 [email protected]로 문의하면 최선을 다해 질문에 답하겠다.

Page 28: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

Spri

ng S

ecur

ity

3

Page 29: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

01보안에 취약한 애플리케이션의 해부

21세기에 웹 기반 애플리케이션을 개발할 때 보안 부분은 다소 이견이 있을 수는 있지만 설계 요

소 중 가장 중요한 부분 중 하나다. 맬웨어(malware)와 범죄자들, 불량 직원들이 도처에 있는 오늘

날, 소프트웨어를 불법 사용으로부터 보호하기 위해 다양한 테스트를 거치고 프로젝트에 보안을

폭넓고 적절하게 적용하는 기술은 여러분이 책임질 핵심 사항이라 할 수 있다.

이 책은 보안이라는 복잡한 주제를 공략하는 데 도움이 되는 개발 패턴을 따라가는 방식으로

구성돼 있다. 이 책에서는 스프링 3 기반 웹 기반 애플리케이션을 통해 스프링 시큐리티 3의 핵심

개념과 전략들을 독자들이 이해하기 쉽게 설명하고 있다.

기존에 스프링 시큐리티를 사용하고 있었든 아니면 스프링 시큐리티에 대한 기본 지식을 더 확

장하고 싶든 이 책을 통해 여러분은 필요한 부분에서 도움을 얻게 될 것이다.

이번 장에서는 다음과 같은 내용을 살펴보겠다.

가상의 ▶ 보안 감사 결과 검토

▶ 웹 기반 애플리케이션에서 흔히 발생하는 보안 문제 소개

주요 소프트웨어 보안 관련 용어 및 개념 학습 ▶

기본적인 보안 관련 용어에 익숙하다면 스프링 시큐리티 프레임워크를 설명하기 시작하는

2장, ‘스프링 시큐리티 시작하기’로 바로 넘어가도 무방하다.

Page 30: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

2 l 스프링 시큐리티 3

보안 감사

어느 날 아침 일찍 Circle Pants Online Pet Store(JBCPPets.com)의 소프트웨어 개발자인 여러분

이 모닝 커피를 반쯤 마셨을 때 상사로부터 다음과 같은 메일을 받게 된다.

To: Star Developer <[email protected]> From: Super Visor <[email protected]>

제목: 보안 감사

오늘 외부 보안 회사에서 우리 e-커머스 상점에 대해서 감사를 시작합니다. 보안 문제로 드러날 수 있는 부분은 미리 조치해 주시기 바랍니다. 물론 사이트 설계 당시 보안에 대해서

는 충분히 고려해서 설계했을 것이라고 믿기 때문에 별 걱정은 없습니다.

- 상사로부터

드디어 올 것이 왔다. 애플리케이션 설계 당시 보안에 대해서 전혀 심각하게 생각하지 않은 것이

다. 이제부터 우리는 외부 보안 감사팀에게서 쓴소리를 많이 듣게 될 것이다. 먼저 시간을 조금 내

서 보안 감사의 대상이 되는 애플리케이션부터 살펴보자.

예제 애플리케이션 소개

이 책에서는 미리 고안한 시나리오를 바탕으로 설명을 이어가겠지만, 애플리케이션의 설계나 우리

가 수정할 내용들은 스프링 기반 애플리케이션에서 실제 사용되는 사례를 가져온 것이다.

이 애플리케이션은 여러분이 ORM이나 복잡한 UI 기술 등에 얽매이지 않고 주요 보안 주제에

만 집중할 수 있게 최대한 단순한 형태로 설계됐다. 이 책에 사용된 예제 코드에서 제공하는 다른

기본 기능과 관련해 모르는 내용이 있다면 다른 보조 자료들을 참고하기 바란다.

이 책에 사용된 코드는 스프링 3와 스프링 시큐리티 3를 바탕으로 작성했지만, 스프링 시큐리티

2에도 비교적 쉽게 적용할 수 있다. 스프링 시큐리티 2와 3 사이의 변경 사항과 관련한 상세 내용

이 수록된 13장, ‘스프링 시큐리티 3 마이그레이션’을 참고하면 스프링 시큐리티 2에 맞게 예제를

수정하는 데 도움될 것이다.

이 애플리케이션은 실제 온라인 애완동물 상점을 개발하는 기초 코드로 사용하기에는 적합하

지 못하다. 이 애플리케이션은 독자 여러분이 책에서 설명하는 개념과 설정에 집중할 수 있도록 일

부러 단순한 구조로 설계했기 때문이다.

Page 31: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

01 보안에 취약한 애플리케이션의 해부 l 3

JBCP 애완동물 상점 애플리케이션의 아키텍처

이 웹 애플리케이션은 표준 3 티어 아키텍처를 따르고 있으며 다음 다이어그램에서 볼 수 있듯이

웹 레이어, 서비스 레이어, 데이터 접근 레이어로 구성돼 있다.

웹 레이어 (스프링 MVC)

서비스 레이어 (스프링 코어)

데이터 접근 레이어 (스프링 ORM / JPA)

웹 레이어는 MVC 코드와 기능을 캡슐화하는 레이어다. 이 예제 애플리케이션에서는 스프링

MVC 프레임워크를 사용하고 있지만 웹 레이어로 스프링 웹 플로우나 스트러츠 또는 아파치 위켓

과 같이 스프링 친화적인 프레임워크를 사용하는 것도 가능하다.

스프링 시큐리티를 사용하는 전형적인 웹 애플리케이션의 경우 웹 레이어는 많은 설정을 적용

하고 코딩해야 할 양이 가장 많은 레이어다. 따라서 웹 애플리케이션을 개발해 본 경험이 많지 않

고 특별히 스프링 MVC에 익숙하지 않다면 기본 코드를 충분히 학습하고 이해한 후 좀더 복잡한

주제로 넘어가는 것이 좋다. 거듭 말하지만 우리는 최대한 웹 사이트를 단순하게 만들려고 노력했

고 온라인 애완동물 상점이 이런 접근 방식에 적합하다고 판단했다. 이런 접근 방식은 각종 튜토리

얼에서 예제로 사용하는 복잡한 자바 EE 애완동물 클리닉과는 대조되는 접근 방식이다.

서비스 레이어는 애플리케이션에 대한 비즈니스 로직을 캡슐화한다. 예제 애플리케이션에서는

애플리케이션 서비스 메서드에 보안을 적용하는 것과 관련한 특정 부분들을 설명하기 위해 서비

스 레이어를 데이터 접근 레이어에 대한 아주 가벼운 파사드(facade)로 사용한다.

전형적인 웹 애플리케이션에서는 이 레이어에 비즈니스 규칙의 유효성 검증, 비즈니스 객체들에

대한 조합 및 분할, 로깅과 같은 공통 관심사들(cross-cutting concerns)이 놓인다.

데이터 접근 레이어는 데이터베이스 테이블을 관리하는 데 사용하는 코드를 캡슐화한다. 많은

스프링 애플리케이션에서 하이버네이트나 JPA 같은 ORM이 사용되는 레이어가 바로 이 레이어

다. 예제 코드에서는 기본적인 JDBC 기능을 사용해 인 메모리 HSQL 데이터베이스에 데이터를

저장한다.

Page 32: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

4 l 스프링 시큐리티 3

전형적인 웹 애플리케이션에서는 이보다 광범위한 데이터 접근 솔루션이 사용된다. ORM, 특히

데이터 접근 부분은 일부 개발자들에게는 어려운 주제에 해당하므로 설명을 명확하게 하기 위해

이 부분을 최대한 단순화했다.

애플리케이션 개발도구

우리는 스프링 개발자라면 누구나 자신의 개발 장비에 설치했을 법한 기본적인 개발 도구들을 사

용해 애플리케이션을 실행하기 쉽도록 배려했다. 하지만 이러한 개발 도구를 설치하지 않은 개발

자들을 위해 부록, ‘추가 참고 자료’에 이와 관련한 추가 설명을 수록했다.

이 책의 예제 코드를 따라 할 때 더 생산성 있게 작업하고자 한다면 다음과 같은 통합 개발 환경

을 권장한다.

이클립스 3.4 또는 3.5 자바 EE 번들 ▶

http://www.eclipse.org/downloads/에서 다운로드 가능

스프링 IDE 2.2 (2.2.2 또는 상위 버전) ▶

http://springide.org/blog/에서 다운로드 가능

이 책의 일부 예제와 캡처 화면에서는 이클립스와 스프링 IDE를 사용하고 있으므로 여러분도

이러한 IDE를 함께 사용할 것을 권장한다.

무료로 제공되는 이클립스의 스프링 툴 스위트(STS) 배포 버전을 다운로드해서 사용해도 된다.

STS는 스프링 소스에서 배포하는 이클립스 번들로 차세대 스프링 IDE 기능들을 포함하고 있다

(http://www.springsource.com/products/springsource-tool-suite-download에서 다운로드할

수 있다). 일부 사용자들은 이 번들 버전에서 제공하는 많은 기능과 스프링 소스라는 이름이 IDE

의 브랜드로 사용된 것을 싫어하기도 하지만 스프링 프레임워크를 많이 사용하는 개발자라면 분

명 도움이 되는 많은 기능들을 제공해 준다.

이 책에서는 주로 이클립스 3.4와 호환되는 프로젝트를 사용해 이클립스에서 코드 작업을 할

수 있게 했고 톰캣 6.x 서버에 애플리케이션을 배포하는 것을 기준으로 설명한다. 많은 개발자들

이 이클립스에 익숙하기 때문에 우리는 이 방식이 예제를 패키징하는 가장 쉬운 방식이라고 생각

했다. 아울러 예제용 아파치 Ant 빌드 스크립트와 함께 아파치 메이븐 모듈도 함께 제공한다. 여

러분이 어떤 개발 환경에 놓여있든 이 책을 읽으면서 여러분의 환경에 맞게 책의 예제들을 사용할

수 있으리라 믿는다.

Page 33: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

01 보안에 취약한 애플리케이션의 해부 l 5

책을 따라 하기 전에는 스프링 3와 스프링 시큐리티 3도 함께 다운로드해야 한다. 이해되지 않

는 부분이 있거나 더 많은 정보가 필요할 때는 언제든 이들 프레임워크에서 제공하는 JavaDoc 문

서를 참고하기 바란다. 이러한 공식 문서와 함께 예제와 설명을 같이 보면 이해 수준도 그만큼 높

아질 것이다.

보안 감사 결과 검토

우리 이메일로 돌아와 보안 감사가 어떻게 진행되는지 확인해 보자. 이를 어쩌나. 결과가 좋지 않다.

To: Star Developer <[email protected]> From: Super Visor <[email protected]>

제목 : FW: 보안 감사 결과

아래 문제점을 살펴보시고 해결 방안을 마련해 주세요.

- 상사로부터

[ 애플리케이션 보안 감사 결과 ]

이 애플리케이션에서 다음과 같은 문제점들을 찾아냈습니다.

URL 보호 및 전반적인 인증 절차 미비로 인한 잘못된 권한 적용 �

권한부여의 부적절한 사용 또는 권한부여 부재 �

데이터베이스에 저장된 크리덴셜의 보안 미적용으로 인한 쉬운 정보 접근 �

개인 정보 또는 민감한 정보들이 쉽게 접근 가능하고 암호화돼 있지 않음 �

SSL 암호화 부재로 인해 전송 레벨 보안이 취약함 �

위험 수위 : 높음 �

우리는 이런 보안 문제들이 모두 해결되지 전까지는 이 애플리케이션을 웹에서 내릴 것을 권

장합니다.

이런! 회사에 문제가 될 만큼 보안 문제가 심각한 것으로 드러났다. 여기서 나온 문제들을 가능

한 한 빨리 해결하지 않으면 안될 것 같다.

외부 보안 전문가들은 종종 회사(또는 협력사나 고객에 의해)에서 고용돼 선량한 해킹(white hat

Page 34: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

6 l 스프링 시큐리티 3

hacking), 소스 코드 리뷰, 애플리케이션 개발자나 설계자들과의 공식/비공식 대화를 통해 소프트

웨어 보안 수준을 감사한다.

보안 감사자

개발자와의 대화 ▶

개발자

보안을 뚫거나 우회하려고 시도 보안 평가

소프트웨어 시스템

애플리케이션 기능

소스 코드

네트워크 인프라스트럭처

소프트웨어 인프라스트럭처

이런 보안 감사의 목적은 주로 소프트웨어 관리자나 고객에게 보안과 관련한 기본적인 개발 관

행들이 잘 지켜져서 고객의 데이터와 시스템 기능이 안전하다는 확증을 얻기 위한 것이다. 소프트

웨어가 대상으로 하는 산업 분야에 따라 이러한 보안 감사에는 특정 산업 표준 또는 법정 준수 항

목을 기준으로 감사가 이뤄질 수도 있다.

잠깐만요! 여러분이 언젠가 마주칠 수 있는 이런 보안 표준에는 지불 카드 산업 데이터 보안

표준(PCI DSS)과 의료 보험 이전 및 책임에 관한 법률(HIPAA)이 있다. 둘 모두 표준 규칙

으로 일련의 절차와 소프트웨어 제어를 통해 특정 민감 정보(각각 신용 카드와 의료 정보)

를 보호하는 것을 목적으로 하고 있다.

많은 산업 분야와 다른 국가들에서도 민감한 정보 또는 개인 신원 정보(Personally

Identifiable Information, PII)와 관련해 이와 비슷한 표준들을 적용하고 있다. 이러한 표

준을 따르지 않는 것은 잘못된 개발 관행일 뿐 아니라 보안 사고가 발생할 때 회사에 (언론

의 질타는 물론이고) 심각한 법적인 책임을 묻게 할 수도 있다.

Page 35: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

01 보안에 취약한 애플리케이션의 해부 l 7

보안 감사 결과를 받고 나니 이제야 비로소 정신이 번쩍 들 것이다. 하지만 보안 문제점들을 해결

하는 과정을 지금부터 하나씩 진행하면 보안과 관련한 많은 내용들을 배울 수 있음은 물론 소프

트웨어도 개선할 수 있을 것이다. 아울러 이 과정을 통해 안전한 소프트웨어를 개발하는 데 필요

한 개발 관행과 정책도 자연스럽게 몸소 익힐 수 있을 것이다.

보안 감사 업체에서 발견한 문제점들을 확인하고 이를 어떻게 해결할 수 있는지 좀더 자세히 살

펴보자.

인증

� URL 보호 및 전반적인 인증 절차 미비로 인한 잘못된 권한 적용.

인증(authentication)은 안전한 애플리케이션을 개발하기 위해 여러분이 익혀야 할 두 가지 주요 보

안 개념 중 하나다(다른 하나는 권한부여다).

인증은 시스템의 단일 사용자의 특정 신원을 확인하고 이 사용자를 식별가능한 (아울러 안전

한) 엔티티와 매핑하는 과정을 말한다. 보통 소프트웨어 시스템은 다음 그림과 같이 인증되지 않

은 (또는 익명의) 영역과 인증된 영역으로 나뉜다.

인증되지 않은 ("익명") 영역

일반적인, 사용자와 무관한 기능

인증된 ("안전한") 영역

사용자 의존적인 ("개인화된") 기능

보안 기능 (예, 관리자 기능)

익명 영역에서 애플리케이션이 담당하는 기능은 사용자의 신원과 무관한 기능들이다(온라인

상점의 상품 목록 배열과 같은 기능을 생각하면 쉽다).

Page 36: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

8 l 스프링 시큐리티 3

익명 영역에서는 다음과 같은 일들을 하지 않는다.

사용자가 시스템에 로그인하도록 요청하거나 사용자의 개인 정보를 사용하는 일 ▶

이름, 주소, 신용 카드, 주문 내역 등 민감한 정보를 보여주는 일 ▶

시스템의 전체 상태 또는 데이터를 관리할 수 있는 기능을 제공하는 일 ▶

시스템에서 인증되지 않은 영역은 모두를 위한 사용 공간으로, 이 영역에는 아직 자신이 누구

인지를 드러내지 않은 사용자도 포함된다. 하지만 식별된 사용자에 대해서는 이 영역에서도 추가

기능을 제공할 수 있다(예를 들어, 흔히 볼 수 있는 ‘환영합니다 {사용자 이름} 님’ 텍스트 등). 이

와 같이 인증된 사용자에 대해 정보를 선택적으로 더 보여주는 기능은 스프링 시큐리티의 태그

라이브러리를 통해 완벽히 지원된다. 이와 관련해서는 5장, ‘미세 접근 제어’에서 다룬다.

2장에서는 스프링 시큐리티의 자동 설정 기능을 활용해 이러한 인증 문제를 해결하고 폼 기반

인증(form-based authentication) 기능을 구현할 것이다. 이보다 더 복잡한 인증 양식은 (주로 기업

또는 외부 인증 업체와 연동하는 인증 시스템) 이 책의 후반부인 8장, ‘OpenID에 대한 개방’부터

설명한다.

권한부여

� 권한부여의 부적절한 사용 또는 권한부여 부재.

권한부여(Authorization)는 애플리케이션 보안을 이해하는 데 두 번째로 중요한 핵심 개념이다. 권

한부여는 권한이 부여된 사용자들만 특정 기능 또는 데이터에 접근을 허용하는 기능이다. 애플리

케이션에서 이와 같은 권한부여 모델을 적용하려면 권한, 기능 및 데이터, 사용자에 맞게 애플리

케이션의 기능과 데이터 영역을 구분하는 작업이 필요하다. 보안 감사에서 지적한 애플리케이션의

문제는 애플리케이션의 기능이 사용자의 역할(role)에 의해 제한되지 않는다는 점이다. 예를 들어,

우리가 e-커머스 사이트를 운영하고 있는데, 사이트를 방문하는 모든 사용자가 다른 고객의 정보

를 볼 수 있음은 물론 다른 고객이 주문한 내역을 확인하거나 주문을 취소하고 수정할 수 있다면

어떻게 될까?

이와 같은 기본적인 권한부여와 관련된 문제는 2장에서 스프링 시큐리티의 권한부여 인프라스

트럭처를 다루면서 살펴보겠다. 아울러 웹 티어와 비즈니스 티어에서의 고급 권한부여 주제에 대

해서는 각각 5장과 6장에서 설명하겠다.

Page 37: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

01 보안에 취약한 애플리케이션의 해부 l 9

데이터베이스 크리덴셜 보안

� 데이터베이스에 저장된 크리덴셜의 보안 미적용으로 인한 쉬운 정보 접근.

애플리케이션 소스 코드와 설정 파일들을 보고 난 후, 감사팀에서는 사용자 비밀번호가 설정 파일

에 일반 텍스트로 저장돼 있어서 서버에 접근할 수 있는 악의적 사용자가 이를 통해 애플리케이션

에 접근할 수 있다는 사실을 확인했다.

애플리케이션에는 개인 정보와 금융 정보가 들어 있어서 불량 사용자가 이러한 정보에 접근하

게 되면 회사가 신원 절도 또는 악용 피해에 노출될 수 있다. 애플리케이션에 접근하는 데 사용되

는 이러한 개인 정보를 보호하는 일은 우리로서는 최우선 순위를 둬야 하는 일로서, 이를 위해서

는 먼저 한 부분에서 보안 문제가 발생하더라도 전체 시스템이 보안 위협에 노출되지 않게 하는 것

이 필요하다.

JDBC 연결을 사용해 크리덴셜을 저장하도록 스프링 시큐리티의 데이터베이스 접근 레이어를

설정하는 작업은 4장, ‘크리덴셜 안전하게 저장하기’에서 살펴보겠다. 4장에서는 데이터베이스에

저장된 비밀번호의 보안을 강화하는 내장 기술에서 대해서도 설명한다.

민감한 정보

� 개인 정보 또는 민감한 정보들이 쉽게 접근 가능하고 암호화돼 있지 않음.

감사팀에서는 신용카드 번호를 포함해 중요하고 민감한 정보들이 시스템상에서 전혀 암호화돼 있

지 않고 보호되지 않는다는 사실을 지적했다. 이러한 문제는 단순히 열악한 데이터 보안 사례에

해당할 뿐 아니라 신용카드 번호를 제대로 보호하지 않아 PCI 표준에 대한 위반 사례에 해당한다.

다행히 스프링 시큐리티의 애노테이션 기반 AOP 지원 기능을 사용하면 간단한 설계 방식과 툴

만으로도 이러한 정보를 보호할 수 있다. 이러한 종류의 데이터를 보호하는 내용과 관련해서는

5장에서 데이터 접근 레이어를 다루면서 설명하겠다.

전송 레벨 보안

� SSL 암호화 부재로 인해 전송 레벨 보안이 취약함.

현실에서는 온라인 상업 사이트에서 SSL 보호를 적용하지 않은 채 웹 사이트를 운영하는 것은

말도 안 되는 일이다. 하지만 JBCP Pets 웹 사이트는 아쉽게도 이런 사이트에 속한다. SSL 보호

Page 38: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

10 l 스프링 시큐리티 3

는 브라우저 클라이언트와 웹 애플리케이션 사이의 통신을 각종 탬퍼링(tampering) 1 과 스누핑

(snooping) 2 으로부터 안전하게 보호해 준다.

5장에서는 애플리케이션의 안전한 보안 구조를 정의하면서 전송 레벨 보안을 사용할 때의 기본

옵션을 살펴보고, 이 과정에서 애플리케이션의 어떤 영역에 SSL 암호화를 강제로 적용해야 하는

지 살펴본다.

보안 문제 해결을 위한 스프링 시큐리티 3 사용

스프링 시큐리티 3는 많은 보안 관련 기본 관례를 정의하고 이를 쉽게 설정할 수 있는 풍부한 기

능을 제공한다. 이어지는 장에서는 소스 코드와 애플리케이션 설정을 함께 적용해 보안 감사팀에

서 지적한 문제(와 더불어 그 이상의 문제들)를 해결할 것이다. 그리고 이 과정을 통해 우리가 만든

e-커머스 사이트가 보안 위협으로부터 안전한 사이트라는 확신을 얻게 될 것이다.

스프링 시큐리티 3를 사용하면 애플리케이션의 보안을 강화하기 위해 다음과 같은 일들을 할

수 있다.

시스템 사용자를 개별 사용자군으로 세분화한다. ▶

사용자 역할에 따라 권한부여 레벨을 부여한다. ▶

사용자군에 사용자 역할을 부여한다. ▶ 3

애플리케이션 리소스에 대해 전역으로 인증 규칙을 적용한다. ▶

모든 애플리케이션 아키텍처 레벨에서 권한부여 규칙을 적용한다. ▶

사용자의 세션을 조작하거나 훔치려는 일반적인 공격을 차단한다. ▶

왜 스프링 시큐리티인가?

스프링 시큐리티는 스프링 프레임워크가 처음 나왔을 때 한 것처럼 외부 자바 라이브러리의 부

족함을 메우기 위해 등장한 프레임워크다. JAAS(Java Authentication and Authorization Service)나

1   (옮긴이) 시스템의 기능을 해커가 임의로 수정해 보안을 약화시키거나 기능을 훼손하는 행위를 말한다.

2   (옮긴이) 원격지에서 각종 사용자 ID나 비밀번호 등을 알아내는 행위처럼 네트워크상의 주요 정보를 몰래 획득하는 행위를 말

한다.

3   (옮긴이) 이와 같은 사용자 역할에 대한 기획은 5장, ‘미세 접근 제어’에서 다룬다.

Page 39: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

01 보안에 취약한 애플리케이션의 해부 l 11

Java EE Security도 물론 같은 인증 및 권한부여 기능들을 제공하지만 위에서부터 아래로 내려오

는(top-to-bottom) 4 애플리케이션 보안 솔루션을 간편하고 합리적으로 구현할 수 있는 모든 기능

들을 제공한다는 점에서 스프링 시큐리티가 더 우수하다고 할 수 있다.

아울러 스프링 시큐리티는 기본 설정만으로도 일반적인 기업 인증 시스템과의 연동이 바로 가

능하다는 장점을 많은 개발자들에게 부각시키고 있다. 따라서 스프링 시큐리티를 사용할 경우 개

발자는 (설정을 제외하고는) 아주 적은 노력만으로도 각 상황에 보안을 적용할 수 있다. 스프링 시

큐리티가 폭넓게 사용되는 이유는 현존하는 어떠한 주류 프레임워크도 스프링 프레임워크만큼

훌륭하지 못하기 때문이다.

요약

이 장에서는 다음과 같은 내용을 다뤘다.

보안에 취약한 웹 애플리케이션의 주요 위험 요소를 살펴봤다. ▶

예제 e-커머스 애플리케이션의 기본 아키텍처를 살펴봤다. ▶

e-커머스 애플리케이션에 보안을 적용하기 위한 전략을 논의했다. ▶

2장에서는 스프링 시큐리티의 전체적인 구조를 살펴볼 텐데, 특별히 이 과정에서 애플리케이션

에 필요한 부분에 대해 중점적으로 설명하겠다.

4   (옮긴이) ‘위에서부터 아래로 내려오는(top-to-bottom)’이라는 표현은 가장 구체적인(most specific) 설정부터 가장 일반적인

(most general) 설정으로 내려오는 보안 설정을 말한다. 이러한 적용 방식은 스프링 시큐리티의 설정 파일 매핑에서 자주 보게

될 내용으로, 가장 구체적인 보안 관련 규칙을 먼저 적용하고 매칭되는 보안 규칙이 없을 경우 가장 일반적인(평범한) 보안 규

칙을 마지막에 적용한다는 원칙을 바탕으로 한다.

Page 40: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

Spri

ng S

ecur

ity

3

Page 41: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

02스프링 시큐리티 시작하기

이 장에서는 스프링 시큐리티의 핵심 개념들을 살펴보고, 주요 용어와 스프링 시큐리티의 아키텍

처에 대해 정리해 보겠다. 이 장에서는 스프링 시큐리티를 설정하는 일부 옵션에 대해 배우고 이러

한 옵션이 애플리케이션에 미치는 영향에 대해 배운다.

물론 이 장에서 할 가장 중요한 일은 직장에서 쫓겨나지 않도록 JBCP Pets 온라인 상점에 보

안 적용을 시작하는 일이다. 여기서는 먼저 1장, ‘보안에 취약한 애플리케이션의 해부’에서 살펴본

URL 보호 및 전반적인 인증 절차 미비로 인한 잘못된 권한 적용 문제를 해결해 보면서 온라인 상

점에서 인증이 필요한 부분에 어떻게 인증을 적용할지 살펴보겠다.

이 장에서 배울 내용은 다음과 같다.

핵심 보안 개념에 대한 이해 ▶

스프링 시큐리티의 빠른 설정 옵션을 활용한 JBCP Pets 온라인 상점에 기본 수준의 보안 ▶

구현

스프링 시큐리티의 고수준 아키텍처에 대한 이해 ▶

인증과 권한부여와 관련한 표준 설정 및 옵션 이해 ▶

스프링 시큐리티의 접근 제어 시 스프링 표현식 언어 활용 ▶

Page 42: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

14 l 스프링 시큐리티 3

핵심 보안 개념

보안에 대한 인식을 새롭게 해 준 보안 감사 이후, 여러분은 연구 끝에 스프링 시큐리티를 사용하

면 스프링 웹 MVC 기반의 JBCP Pets 온라인 상점의 감사에서 지적한 문제들을 해결할 수 있는

시스템을 구축할 수 있다는 사실을 깨달았다. 하지만 스프링 시큐리티를 더 효과적으로 사용하려

면 애플리케이션의 보안 프로필을 수정하기 전에 먼저 핵심 개념과 용어들을 정리해야 한다.

인증

1장에서 배운 것처럼 인증은 애플리케이션의 사용자가 해당 사용자가 주장하는 본인이 맞는지 확

인 1 하는 절차를 말한다. 온라인이든 오프라인이든 생활 속에서는 여러 가지 다양한 인증 방식을

접할 수 있다.

▶ 크리덴셜 기반 인증: 웹 기반 이메일 계정에 로그인할 때는 보통 사용자명과 비밀번호를 입

력한다. 이메일 제공 업체에서는 데이터베이스에서 사용자가 입력한 사용자명에 해당하는

사용자를 확인하고 입력한 비밀번호와 저장된 비밀번호가 일치하는지 확인한다. 이러한

크리덴셜은 이메일 시스템에서 특정 사용자가 시스템을 사용할 수 있는 사용자인지 여부

를 판단하는 데 사용된다. 우리는 우선 이 인증 방식을 사용해 JBCP Pets 온라인 상점에서

민감한 영역에 보안을 적용해 보겠다. 엄밀히 말해 이메일 시스템에서는 데이터베이스에

있는 크리덴셜뿐 아니라 다른 어느 위치에 있는 크리덴셜도 확인할 수 있다. 예를 들어, 마

이크로소프트 액티브 디렉터리 같은 기업용 디렉터리 서버도 크리덴셜을 저장하는 데 사

용할 수 있다. 이러한 연동 방식은 이 책의 후반부에서 설명하고 있다.

▶ 이중 인증: 은행의 ATM 기기에서 현금을 인출할 때는 개인 ID 카드를 긁고 나서 현금을

인출하거나 다른 거래를 하기 위해 개인 정보를 입력한다. 이러한 인증 방식은 사용자명과

비밀번호를 사용하는 인증 방식과 유사하지만 사용자의 이름이 카드의 마그네틱 선에 인

코딩돼 있다는 차이가 있다. 은행에서는 이와 같이 물리적인 카드에 저장된 정보와 사용자

가 입력한 비밀번호를 조합해 사용자가 특정 계좌에 접근할 수 있는지를 판단한다. 비밀번

호와 물리적인 장치(플라스틱 ATM 카드)를 조합하는 방식은 가장 흔히 볼 수 있는 이중

1   (옮긴이) 어떤 사용자가 해당 사용자가 주장하는 본인이 맞는지 확인한다는 말은, 예를 들어 악의적인 사용자(다른 사용자의

id를 기입하고 임의의 비밀번호를 입력해)가 ‘마치 자신이 다른 사용자인 양 행동하는지 여부를 판단’하는 과정을 말한다. 악의

적인 사용자가 아니라면, 예컨대 사용자 크리덴셜(비밀번호 등)을 정확히 제공할 것이고, 악의적인 사용자라면 사용자명을 도

용하고 비밀번호를 잘못 제공할 수 있을 것이다. 크리덴셜을 올바르게 제공한 사용자는 ‘사용자가 주장하는 사용자’에 해당하

며, 그렇지 않은 사용자는 ‘사용자가 주장하는 사용자가 아닌 사용자(즉, 해커는 자신이 A라는 사용자라고 주장하지만, 이는

거짓 주장이다)’라고 판단할 수 있다. 이러한 판단을 인증 과정에서 처리한다.

Page 43: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

02 스프링 시큐리티 시작하기 l 15

인증 방식에 속한다. 전문적이고 엄격한 보안이 필요한 환경에서는 금융이나 개인 신상 정

보를 취급하는 곳처럼 엄격한 보안이 필요한 곳에 접근하려고 할 때 이러한 장치를 사용하

는 것을 종종 볼 수 있다. RSA의 SecurID는 시간 기반 하드웨어 장치와 서버 기반 인증 소

프트웨어를 함께 사용한 엄격한 보안을 적용하고 있다.

▶ 하드웨어 인증: 아침에 차에 시동을 걸 때 우리는 시동 키를 꽂은 후 키를 돌려서 차에 시동

을 건다. 물론 자동차의 시동을 거는 행동이 앞의 두 인증 사례와 비슷해 보이지 않을 수도

있지만, 차에 꽂은 자동차 키의 생김새와 점화 스위치의 텀블러는 하드웨어 인증 방식의 한

예다.

소프트웨어와 하드웨어 보안에 적용할 수 있는 인증 방식에는 수십 가지가 있고 각 방식마다 장

단점이 있다. 앞으로 스프링 시큐리티를 적용하면서 이러한 방식 중 일부를 검토할 것이다. 실제로

이 책의 후반부에서는 스프링 시큐리티를 사용해 인증을 구현할 때 가장 많이 사용되는 인증 방

식을 설명하고 있다. 스프링 시큐리티는 인증된 주체를 고유 식별하는 데 사용되는 자바 표준 보

안 개념인 인증 Principal(java.security.Principal)을 확장해 사용한다. 전형적인 Principal에서는 시

스템 사용자에 대한 1:1 매핑을 사용하지만 이러한 Principal은 시스템의 어떠한 클라이언트, 이를

테면 웹 서비스 클라이언트, 자동 배치 피드 등에도 매핑할 수 있다. 하지만 스프링 시큐리티를 사

용할 때 Principal은 대부분 단일 사용자를 나타내는 데 사용한다. 따라서 이 책에서 Principal 또

는 주체라고 하면, 이는 곧 ‘사용자’를 나타내는 것으로 생각해도 된다.

권한부여

보통 권한부여라는 용어는 보안이 적용된 시스템의 접근성을 나타내기 위한 두 가지 서로 다른 작

업을 아우르는 용어다.

첫 번째 작업은 인증된 주체를 하나 이상의 권한(보통 역할이라고 부른다)에 매핑하는 일이다.

예를 들어 웹 사이트를 단순 방문한 사용자는 방문자 권한을 부여받지만 웹 사이트 관리자는 관

리자 권한을 부여받는 것이 여기에 해당한다.

두 번째 작업은 보호된 리소스에 대한 권한 체크다. 이러한 권한 체크는 주로 시스템이 개발될

때 코드상에 명시적으로 선언하거나 설정 매개변수를 통해 이뤄진다. 예를 들어 온라인 애완동물

상점의 재고를 관리하는 화면은 관리자 권한을 가진 사용자만 볼 수 있다.

Page 44: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

16 l 스프링 시큐리티 3

알아두세요! 보호된 리소스는 사용자의 권한에 따라 조건적으로 나타나야 하는 시스템상의

모든 영역이 될 수 있다.

웹 애플리케이션에서 보호된 리소스는 개인 웹 페이지나 웹 사이트 전체 또는 개인 페이지

의 일부 영역이 될 수 있다. 이와 비교해 보호된 비즈니스 리소스는 클래스에 대한 메서드

호출 또는 개별 비즈니스 객체가 될 수 있다.

!

개별 주체에 대한 권한 체크는 사용자 계정을 확인한 후 해당 주체가 실제 관리자인지 등을 확

인하는 과정을 통해 이뤄진다. 이러한 권한 체크를 거쳐 보호된 영역에 접근하려는 주체가 실제로

관리자라고 판단되면 요청이 성공한다. 하지만 해당 주체가 충분한 권한을 갖고 있지 않은 경우에

는 접근 요청이 거부된다.

보안이 적용된 특정 페이지인 재고 관리 페이지를 예로 들어 이 부분을 좀더 자세히 살펴보자.

재고 관리 페이지에 접근하려면 관리자 권한이 필요하다(일반 사용자가 상점의 재고 목록을 관리

하는 건 적합하지 않다!). 따라서 이 페이지에 접근하려는 주체는 특정 수준 이상의 권한이 있는

지 먼저 확인 과정을 거쳐야 한다.

사이트 관리자가 보호된 리소스에 접근하려고 할 경우 어떻게 권한을 판단할지에 대한 로직은

필수 권한과 실제 권한을 사전 설정한 원칙에 따라 비교하는 방식으로 진행된다. 이러한 관리자에

대한 권한 판단은 아래와 같은 벤 다이어그램으로 정리할 수 있다.

관리자

관리자

재고 페이지

사용자 권한

사용자

필수 권한

앞의 그림에서는 사용자 권한 (사용자와 관리자)과 필수 권한(관리자) 사이에 교집합 영역이 보

인다. 이 경우 이 사용자는 페이지에 접근할 수 있다.

이 그림을 권한부여를 받지 않은 방문자의 경우와 대조해 보자.

Page 45: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

02 스프링 시큐리티 시작하기 l 17

방문자

관리자

사용자 권한

사용자

필수 권한

재고 페이지

이번에는 권한들이 서로 떨어져 있어서 공통 권한 영역이 없음을 볼 수 있다. 이때는 페이지에

대한 사용자 접근이 거부된다. 이와 같은 간단한 원칙이 리소스 접근 시 권한을 부여하는 기본 원

칙이다.

실제 개발에서는 보호된 리소스에 대한 사용자 요청에 대해 권한을 부여하거나 거부하는 결정

을 코드가 내린다. 다음 다이어그램은 스프링 시큐리티에서 적용하는 고수준 권한부여 절차를 보

여준다.

접근 결정 관리자

요청을 가로챔재고 페이지에 접근

관리자

가로챈 요청 재고 페이지

리소스의 권한 조건을 체크

“관리자 권한을 요구한다”

사용자가 필수 권한을 갖고 있는가?

예 – 요청 성공

아니오 – 요청 거부사용자에게 요청

실패를 알림

Page 46: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

18 l 스프링 시큐리티 3

이 그림에서는 접근 결정 관리자(access decision manager) 2 가 주체가 갖고 있는 권한과 리소스에

서 요구하는 권한을 바탕으로 주체가 적절한 접근 권한을 갖고 있는지 판단하는 것을 볼 수 있다.

접근 결정 관리자가 접근을 승인하거나 거부하는 판단은 주체가 갖고 있는 권한과 보호된 리소

스에 접근하는 데 필요한 필수 권한 사이의 교집합 영역을 판단하는 것처럼 간단한 일이다.

그럼, 이제부터 JBCP Pets 온라인 상점에 스프링 시큐리티를 기본적으로 구현하고, 이어서 인증

과 권한부여에 대해 좀더 자세히 살펴보겠다.

간단한 절차를 통한 애플리케이션 보안 적용

스프링 시큐리티는 설정이 무척 복잡해질 수도 있지만 스프링 시큐리티의 개발자들은 아주 간단

한 기초 설정만 적용하더라도 프레임워크 기능의 상당 부분을 활용할 수 있도록 배려하고 있다. 이

러한 기초 설정의 토대 위에 설정을 추가로 적용하면 애플리케이션의 보안과 관련한 세부적인 설

정 및 제어가 가능하다.

여기서는 보안이 적용되지 않은 현 상태부터 시작해 3단계에 걸쳐 사이트에 간단한 사용자명

과 비밀번호 인증 보안을 적용해 보겠다. 여기서 사용하는 인증 방식은 단순히 웹 애플리케이션

에 스프링 시큐리티를 적용하는 과정을 보여주기 위한 용도에 불과하다. 설명이 진행되는 동안

접근 방식상 분명히 문제가 되는 점들을 보겠지만 이러한 오류들은 추가로 설정을 수정하면서

차차 해결할 것이다.

스프링 시큐리티 XML 설정 파일 구현

첫 번째로 할 기본 설정 작업은 표준 웹 요청을 처리하는 데 필요한 모든 스프링 시큐리티 컴포넌

트들을 나타내는 XML 설정 파일을 만드는 일이다. WEB-INF 디렉터리에 dogstore-security.xml이라

는 XML 파일을 만들자. 이 파일의 내용은 다음과 같다.

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

2   (옮긴이) 이 책에서는 Access Decision Manager를 접근 결정 관리자로 번역하겠다. 실제로 AccessDecisionManager

는 decide(Authentication authentication, java.lang.Object object,java.util.Collection<ConfigAttribute> configAttributes),

supports(java.lang.Class<?> clazz) 등의 메서드를 정의하는 인터페이스의 이름이지만, 이를 가리킬 때는 명시적으로 인터페이스

를 지칭하는 경우를 제외하고는 접근 결정 관리자라는 번역 용어를 사용하겠다. 따라서 독자들은 접근 결정 관리자라는 용어

를 AccessDecisionManager 인터페이스와 같은 의미로 이해하면 된다.

Page 47: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

02 스프링 시큐리티 시작하기 l 19

xmlns:beans="http://www.springframework.org/schema/beans"

xsi:schemaLocation="

http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/security

http://www.springframework.org/schema/security/

spring-security-3.0.xsd">

<http auto-config="true">

<intercept-url pattern="/*" access="ROLE_USER"/>

</http>

<authentication-manager alias="authenticationManager">

<authentication-provider>

<user-service>

<user authorities="ROLE_USER" name="guest" password="guest"/>

</user-service>

</authentication-provider>

</authentication-manager>

</beans:beans>

최소한의 표준 설정을 사용해 웹 애플리케이션에 보안을 적용하는 데 필요한 설정은 이게 전부

다. 스프링 시큐리티 관련 XML 구문을 사용하는 이러한 설정은 XML 설정 엘리먼트와 관련한

XML 네임스페이스(http://www.springframework.org/schema/security)에서 이름을 따와서 security

네임스페이스 방식이라고 부른다. 기존 스프링 빈과 XML 네임스페이스를 함께 사용하는 또 다른

방식은 6장, ‘고급 설정과 확장’에서 설명한다.

알아두세요! 스프링의 XML 설정을 싫어하는 개발자라면 스프링 프레임워크와는 달리 스프

링 시큐리티에서는 앞에서 본 XML 설정을 대신할 애노테이션 기반 설정 방식을 제공하지

않아서 실망할지도 모르겠다. 하지만 스프링 시큐리티는 주로 개별 객체나 클래스보다는

전체 시스템 행동에 영향을 미친다는 사실을 고려하면 이러한 제약을 이해할 수 있을 것이

다. 아울러 향후 버전에서는 설정 파일에 URL 패턴을 지정하는 방식 대신 스프링 MVC 컨

트롤러에 보안 애노테이션을 사용하는 게 가능해질 수도 있다.

!

스프링 시큐리티에서는 애노테이션 방식이 일반적인 방식은 아니지만, 클래스나 메서드에 적용

되는 보안 요소에 대해서는 개발자들이 기대하는 것처럼 애노테이션을 사용할 수 있다. 이와 관련

해서는 5장, ‘미세 접근 제어’에서 설명한다.

Page 48: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

20 l 스프링 시큐리티 3

web.xml 파일 수정 및 스프링 DelegatingFilterProxy 추가

스프링 시큐리티에서 웹 애플리케이션에 주로 영향을 주는 방식은 일련의 ServletRequest 필터를

사용한 방식이다(이 내용은 이 장에서 스프링 시큐리티의 아키텍처를 설명하면서 살펴보겠다). 이

러한 필터들은 마치 애플리케이션에 대한 모든 요청을 감싸는 샌드위치 형태의 보안 기능이라 볼

수 있다.

o.s.web.filter.DelegatingFilterProxy는 스프링 시큐리티가 모든 애플리케이션 요청을 감싸게 해

서 모든 요청에 보안이 적용되게 하는 서블릿 필터다.

알아두세요! DelegatingFilterProxy는 실제로는 스프링 프레임워크에서 제공하는 필터로

보안과 직접적인 관련은 없다. 이 필터는 주로 스프링 기반의 웹 애플리케이션에서 서블릿

필터 라이프 사이클과 연계해 스프링 빈 의존성을 서블릿 필터에 바인딩하는 데 사용된다.

!

web.xml 파일에서 스프링 MVC의 <servlet-mapping> 엘리먼트 바로 다음에 아래 코드를 추가하

면 이 필터에 대한 설정을 추가할 수 있다.

<filter>

<filter-name>springSecurityFilterChain</filter-name>

<filter-class>

org.springframework.web.filter.DelegatingFilterProxy

</filter-class>

</filter>

<filter-mapping>

<filter-name>springSecurityFilterChain</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

여기서 하는 일은 ServletRequest 필터를 적용해 해당 필터가 지정된 URL 패턴(/*)과 일치하는

요청을 처리하게끔 하는 일이다. 여기서 사용한 와일드카드 패턴은 모든 URL과 매칭되기 때문에

이 필터는 결국 모든 요청에 적용된다. 좀더 주의 깊게 설정을 살펴보면 방금 적용한 설정이 앞에

서 dogstore-security.xml에 적용한 스프링 시큐리티 설정과 아직 아무런 관련이 없음을 볼 수 있

다. 이 둘이 서로 연동되게 하려면 web.xml 파일에 XML 설정 파일 레퍼런스를 추가해야 한다.

Page 49: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

02 스프링 시큐리티 시작하기 l 21

web.xml 파일 수정 및 스프링 시큐리티 XML 파일 레퍼런스 추가

스프링 웹 애플리케이션을 설정하는 방식에 따라 web.xml에 XML 설정 파일에 대한 명시적인 레퍼

런스를 추가하거나 추가하지 않아도 된다. 스프링 웹 ContextLoaderListener는 기본적으로 스프링

웹 서블릿과 이름이 같은 XML 설정 파일을 찾는다. 여기서는 새로운 스프링 시큐리티 XML 설정

파일을 기존 JBCP Pets 온라인 상점 사이트에 추가하는 과정을 살펴보겠다.

이를 위해서는 먼저 현재 이 사이트에서 스프링 MVC의 설정 파일 자동 룩업 기능을 사용하는

지 확인해야 한다. 스프링 MVC 설정 파일을 자동으로 찾으려면 web.xml 파일에서 <servlet-name>

엘리먼트에 서블릿의 이름을 다음과 같이 지정하면 된다.

<servlet>

<servlet-name>dogstore</servlet-name>

<servlet-class>

org.springframework.web.servlet.DispatcherServlet

</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>

이 서블릿의 이름(<servlet-name>)은 dogstore이므로 스프링의 CoC(Convention over Con�guration) 3

규칙에 따라 스프링에서는 WEB-INF에서 dogstore-servlet.xml 파일을 찾게 된다. 이러한 관례를 활

용하면 기본 기능을 오버라이드하거나 스프링 MVC와 관련한 설정에 거의 손을 대지 않고도 WEB-

INF 디렉터리에서 설정 파일을 찾게 할 수 있다.

알아두세요! 스프링 웹 플로나 스프링 MVC를 사용하는 많은 사용자들은 CoC 규칙이 어떻

게 적용되거나 스프링 코드의 어떤 부분에서 이러한 규칙을 정하는지 제대로 이해하지 못

한다. 이러한 내용을 이해하려면 o.s.web.context.support.XmlWebApplicationContext 클

래스와 이 클래스의 상위 클래스부터 공부하면 도움될 것이다. JavaDoc을 참고하면 스프

링 웹 MVC 프레임워크 기반 웹 애플리케이션과 관련한 설정 매개변수들에 대한 상세 설명

을 볼 수 있다.

!

스프링 MVC 서블릿과 관련한 설정 파일이 로드되기 전에 추가로 로드할 스프링 Application

Context 설정 파일을 선언할 수도 있다. 이때는 스프링 o.s.web.context.ContextLoaderListener 클래

3   (옮긴이) 직역하면, “설정보다 관례”라는 말로 스프링의 동작 원리를 잘 설명해 주는 대표적인 표현이다. CoC 규칙에 따르면 명

시적인 설정이 없을 경우 관례가 설정을 대신하므로 스프링 개발자들은 모든 설정을 명시하는 대신 관례에 따라 쉽게 개발을

진행할 수 있다.

Page 50: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

22 l 스프링 시큐리티 3

스를 사용해 스프링 MVC의 ApplicationContext와 독립적인 ApplicationContext를 생성해야 한다.

이 방식은 스프링 MVC 빈에 속하지 않는 빈을 설정할 때 주로 사용하며 스프링 시큐리티 설정을

추가할 때도 이 방식이 사용된다.

ContextLoaderListener를 설정하는 데 사용할 XML 파일은 다음과 같이 웹 애플리케이션 배포

디스크립터의 <context-param> 안에 나열하면 된다.

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>

/WEB-INF/dogstore-base.xml

</param-value>

</context-param>

이 책에서 dogstore-base.xml 파일에는 주로 데이터 소스, 서비스 빈 등 표준 스프링 빈 정의가 포

함된다. 이제 아래처럼 수정한 <context-param> 엘리먼트에 보이는 대로 스프링 시큐리티 설정 정보

를 저장한 새로운 XML 설정 파일에 대한 레퍼런스를 추가하자.

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>

/WEB-INF/dogstore-security.xml

/WEB-INF/dogstore-base.xml

</param-value>

</context-param>

스프링 시큐리티 설정 파일에 대한 파일 레퍼런스를 웹 배포 디스크립터에 추가한 다음 웹 애플

리케이션을 재가동 4 하고 http://localhost:8080/JBCPPets/home.do를 통해 홈페이지에 접근해 보자.

그럼 다음과 같은 화면이 나타날 것이다.

4   (옮긴이) 애플리케이션 배포 및 가동과 관련해서는 부록, ‘추가 참고 자료’를 참고하자.

Page 51: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

02 스프링 시큐리티 시작하기 l 23

수고했다! 이로써 스프링 시큐리티를 사용해 세 단계에 걸쳐 애플리케이션에 기본적인 보안 레

이어를 구현하는 데 성공했다. 현 상태에서 guest라는 사용자명과 비밀번호를 사용하면 사이트에

로그인할 수 있다. 이처럼 로그인하면 아주 기본적인 JBCP Pets 홈페이지가 나타날 것이다.

설명을 간단히 하기 위해 이 장에서 제공하는 소스 코드는 전체 JBCP Pets 애플리케이션에서

아주 작은 영역(단일 페이지)에 해당하는 소스 코드만 포함하고 있다. 이처럼 예제 소스 코드에 일

부 코드만 들어 있는 이유는 앞으로 나올 여러 장에 있는 많은 기능들에 주눅들지 않고 애플리케

이션을 개발하고 배포하는 과정에만 집중할 수 있도록 배려한 것이다. 아울러 이렇게 간단한 소스

코드를 사용하면 설정 매개변수를 실험해 보고 애플리케이션을 다시 배포해 결과를 바로 테스트

할 수 있기 때문에 이 장에서 설명하는 기본 지식을 튼튼히 쌓을 수 있다.

부족한 부분들에 대한 고려사항

이쯤에서 잠시 멈춘 후 지금까지 개발한 내용을 생각해 보자. 아마도 지금껏 설명한 과정에서 실

제 배포하기 전에 추가 작업을 해야 하거나 스프링 시큐리티 프레임워크에 대해 더 많이 이해해야

할 부분이 있었을 것이다. 새로 보안을 적용한 이 사이트를 일반에 공개하기 전에 먼저 풀어야 할

과제들의 목록을 정리해서 살펴보자.

스프링 시큐리티의 기본 프로필을 설정하는 과정은 눈 깜짝할 사이에 일어났고 이로 인해 로그

인 페이지, 사용자명과 비밀번호 인증뿐 아니라 온라인 상점의 URL에 대한 자동 인터셉션 기능을

구현할 수 있었다. 하지만 이러한 자동 설정과 우리의 궁극적인 목표 사이에는 어느 정도 간극이

있다.

사용자명, 비밀번호, 사용자의 역할 정보를 XML 설정 파일에 하드코딩했다. 앞에서 추가 ▶

한 XML의 내용을 다시 살펴보자.

<authentication-manager alias="authenticationManager">

<authentication-provider>

<user-service>

<user authorities="ROLE_USER" name="guest" password="guest"/>

</user-service>

</authentication-provider>

</authentication-manager>

사용자명과 비밀번호가 파일에 그대로 적혀 있는 것을 볼 수 있다. 물론 이 상태에서 시스

템을 사용하는 모든 사용자에 대해 이러한 설정 파일을 하나씩 추가한다는 것은 말이 안

Page 52: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

24 l 스프링 시큐리티 3

된다! 이런 문제를 해결하려면 데이터베이스 기반의 인증 프로바이더 5 를 사용해야 한다

(이 내용은 4장, ‘크리덴셜 안전하게 저장하기’에서 설명한다).

고객이 익명으로 방문할 수 있는 페이지를 포함해 온라인 상점의 모든 페이지에 바로 접근 ▶

할 수 없게 했다. 익명 사용자나 인증된 사용자, 관리자와 같은 사용자가 페이지를 사용할

수 있으려면 사용자 역할을 먼저 정의해야 한다(이 내용도 4장에서 설명한다).

로그인 페이지가 많은 도움이 되는 건 사실이지만 지나치게 평범해서 전혀 JBCP Pets 온라 ▶

인 상점의 본래 모습을 찾아 볼 수 없다. 이 문제를 해결하려면 사이트 본래의 외양과 느낌

을 살린 로그인 폼을 추가해야 한다(이 내용은 다음 장에서 설명한다).

자주 발생하는 문제들

많은 사용자들이 스프링 시큐리티를 애플리케이션에 처음 적용할 때 어려움을 겪는다. 자주 발생

하는 문제점과 이에 대한 권고 사항을 아래에 정리했다. 책의 설명 내용을 따라오면서 책에 있는

예제를 따라 해 보기를 권장한다.

스프링 시큐리티를 적용하기 전에 애플리케이션을 빌드하고 배포할 수 있도록 준비 작업 ▶

을 마치자. 필요하다면 이와 관련한 소개 예제를 참고하거나 서블릿 컨테이너에 있는 주석

을 참고하자.

서블릿 컨테이너를 실행할 때는 이클립스와 같은 IDE를 사용하는 게 가장 쉽다. 이 경우 ▶

배포가 편할 뿐 아니라 콘솔을 통해 바로 에러를 확인할 수도 있다. 또 예외가 발생할 때 실

행되는 주요 부분에 브레이크 포인트를 설정해 에러를 더 잘 확인할 수도 있다.

XML 설정 파일이 잘못 작성되면 다음과 같은 에러(또는 이와 유사한 에러)가 발생한다. ▶

org.xml.sax.SAXParseException: cvc-elt.1: Cannot �nd the declaration of element 'beans'.

개발자들은 스프링 시큐리티를 제대로 설정하는 데 필요한 다양한 XML 네임스페이스 레

퍼런스들을 서로 혼동하는 경우가 자주 있다. 이때는 예제를 다시 확인하고 스키마 선언에

서 줄 바꿈 부분을 다시 한 번 살펴보고 XML 형식이 잘못되지는 않았는지 확인할 수 있게

XML 유효성 검증기를 사용해 유효성을 검증하는 게 좋다.

사용하는 스프링과 스프링 시큐리티의 버전이 책과 반드시 일치하게 하고 애플리케이션에 ▶

불필요한 스프링 JAR 파일들이 남아 있지 않도록 주의한다.

5   (옮긴이) 인증 프로바이더란 AuthenticationProvider 인터페이스 또는 그 구현체를 말한다.

Page 53: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

02 스프링 시큐리티 시작하기 l 25

보안은 복잡한 주제다–보호된 웹 요청의 아키텍처

앞에서 본 3단계 설정은 눈 깜짝할 사이에 구현됐다. 이 모두는 스프링 시큐리티의 강력한 기본 설

정 기능과 <http> 엘리먼트의 auto-config 어트리뷰트를 통해 적용한 합리적인 기본 인증 기능 덕분

이다.

하지만 아쉽게도 애플리케이션 구현 관점이나, 설계상의 제약, 인프라스트럭처와의 통합 등으로

스프링 시큐리티를 실제 구현하는 부분은 단순 설정에서 제공하는 내용보다 훨씬 더 복잡해지기

마련이다. 스프링 시큐리티를 사용하는 많은 사용자들은 스프링 시큐리티의 기본 아키텍처나 스

프링 시큐리티의 각 요소가 어떻게 상호작용해 전체를 이루는지 이해하지 못해 이처럼 기본 설정

을 벗어나 추가적인 설정을 적용할 때 많은 어려움을 겪곤 한다.

스프링 시큐리티와 관련한 고급 주제를 이해하려면 먼저 웹 요청의 전체 흐름과 각 요청이 어떤

책임 사슬(chain of responsibility)을 통과하는지 이해해야 한다. 시스템의 전체 아키텍처에 인증과

권한부여를 적용할 때는 항상 앞에서 배운 보안 인증과 권한부여에 대한 개념을 염두에 두자.

사용자

페이지를 요청

인증 관리자

요청을 가로챔

유효한 크리덴셜

접근 결정 관리자

요청을 가로챔

사용자가 적절한 권한을 가지고 있음

홈 페이지

크리덴셜을 요청

크리덴셜을 제공알고 있는 크리덴셜과

비교해 검증

사용자 크리덴셜 저장소

사용자가 보호된 리소스에 접근할 권한이 있는지 검증

시스템 리소스 권한부여 선언

요청은 어떻게 처리될까?

스프링 시큐리티에서는 주로 위임과 서블릿 필터를 활용해 웹 애플리케이션 요청 컨텍스트를 둘

러싼 기능 레이어를 제공한다.

서블릿 필터들(javax.servlet.Filter 인터페이스를 구현하는 클래스들)은 기능에 따라 웹 요청

Page 54: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

26 l 스프링 시큐리티 3

을 가로챈 후 전처리 또는 후처리를 수행하거나 요청 전체를 리다이렉트하는 용도로 사용한다.

JBCP Pets 온라인 상점에서 최종 서블릿은 스프링 MVC의 디스패처 서블릿(dispatcher servlet)이

지만 이론상 이러한 최종 서블릿은 어떤 웹 서블릿도 될 수 있다. 다음 다이어그램은 서블릿 필터

가 사용자의 웹 요청을 어떻게 감싸는지 보여준다.

사용자

서블릿 필터

요청 전처리 서블릿 렌더링 홈 페이지

응답 반환응답 후처리

스프링 시큐리티 XML 설정 파일의 자동 설정 어트리뷰트는 자바 EE 서블릿 필터 체인을 사

용해 순차적으로 적용되는 열 개의 서블릿 필터를 설정한다. 필터 체인은 자바 EE 서블릿 API 개

념으로서 웹 애플리케이션에서 서블릿 필터 체인을 모든 요청에 적용하게 하는 javax.servlet.

FilterChain 인터페이스에서 지정한다.

금속 연결 고리로 이뤄진 물리적인 체인과 마찬가지로 개별 서블릿 필터는 사용자의 요청을 처

리하는 데 사용되는 메서드 체인 내의 연결 고리를 나타낸다. 사용자의 요청은 이러한 체인을 통

과하고 각 필터에 의해 차례로 처리된다.

사용자

요청

응답

필터 체인

서블릿 필터 1

서블릿 필터 2

서블릿 필터 3

doFilter

doFilter

doGet

서블릿

물리적인 사슬을 생각하면 쉽게 유추할 수 있는 것처럼 필터 체인을 통과하는 서블릿 요청은 미

리 정의된 순서에 따라 한 필터에서 다른 필터로 이동해 최종 서블릿에 도달한다. 반대로 서블릿에

서 요청을 처리하고 응답을 반환하면 필터 체인 호출 스택은 모든 필터에 대해 역순으로 진행된다.

Page 55: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

02 스프링 시큐리티 시작하기 l 27

스프링 시큐리티에서는 필터 체인 개념을 사용해 별도의 추상 체인인 VirtualFilterChain을 구현

하고 스프링 시큐리티 XML 설정에서 지정한 (이 부분은 표준 자바 EE의 FilterChain을 웹 애플리

케이션의 배포 디스크립터에 지정하는 것과 다른 점이다) URL 패턴에 따라 FilterChain이 동적으

로 구성되게 해 준다.

알아두세요! 서블릿 필터는 이름에서 알 수 있는 필터링(또는 요청 차단) 용도 이외의 많은

용도로 사용할 수 있다. 사실 많은 서블릿 필터들은 웹 애플리케이션의 런타임 환경에서 웹

요청을 AOP 방식처럼 가로채는 프록시 기능을 수행하는 데 활용한다. 이렇게 하면 서블릿

컨테이너에 대한 웹 요청 전ㆍ후에 특정 기능을 수행할 수 있다.

필터의 이러한 다용도 성격은 스프링 시큐리티에도 그대로 반영됐다. 스프링 시큐리티에서

사용하는 많은 필터도 사용자의 요청에 직접 영향을 끼치는 용도로는 잘 사용되지 않는다.

!

자동 설정 옵션을 사용하면 10개의 스프링 시큐리티 필터가 자동으로 설정된다. 스프링 시큐리

티를 사용해 좀더 고급 작업을 하려면 이러한 기본 필터가 무슨 일을 하고 관련 필터를 어디서 어

떻게 설정하는지 먼저 이해해야 한다. 이들 필터의 목록과 필터가 적용되는 순서를 다음 표에 정리

했다. 이들 중 대부분은 우리가 JBCP Pets 온라인 상점 예제를 보충해가면서 향후 다시 등장하므

로 지금 당장 각 필터의 기능을 제대로 이해하지 못하더라도 걱정할 필요는 없다.

필터 설명

o.s.s.web.context.Security

ContextPersistenceFilter

SecurityContextRepository에서 SecurityContext를 로드하고

저장하는 일을 담당한다. SecurityContext는 사용자의 보호되고 인증된 세션을 나타낸다.

o.s.s.web.authentication.

logout.LogoutFilter

로그아웃 URL(기본값 /j_spring_security_logout)로 지정된 가상 URL에 대한 요청을 감시하고 매칭되는 요청이 있으면 사용자를 로

그아웃시킨다.

o.s.s.web.authentication.

UsernamePassword

AuthenticationFilter

사용자명과 비밀번호로 이뤄진 폼 기반 인증에 사용하는 가상 URL 요

청(기본값은 /j_spring_security_check)을 감시하고 요청이 있으면 사용자의 인증을 진행한다.

o.s.s.web.authentication.ui.

DefaultLoginPageGenerating

Filter

폼 기반 또는 OpenID 기반 인증에 사용하는 가상 URL(기본값은

/spring_security_login)에 대한 요청을 감시하고 로그인 폼 기능을 수행하는 데 필요한 HTML을 생성한다.

o.s.s.web.authentication.www.

BasicAuthenticationFilter

HTTP 기본 인증 헤더를 감시하고 이를 처리한다.

o.s.s.web.savedrequest.

RequestCacheAwareFilter

로그인 성공 이후 인증 요청에 의해 가로채어진 사용자의 원래 요청

(original request)을 재구성하는 데 사용된다.

Page 56: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

28 l 스프링 시큐리티 3

필터 설명

o.s.s.web.servletapi.

SecurityContextHolderAware

RequestFilter

HttpServletRequest를 HttpServletRequestWrapper를 상속하는

하위 클래스(o.s.s.web.servletapi.SecurityContextHolder

AwareRequestWrapper)로 감싼다. 필터 체인상 더 하단에 위치한 요청 프로세서에 추가 컨텍스트를 제공한다.

o.s.s.web.authentication.

AnonymousAuthenticationFilter

이 필터가 호출되는 시점까지 사용자가 아직 인증을 받지 못했다면 요

청 관련 인증 토큰에서 사용자가 익명 사용자로 나타나게 된다.

o.s.s.web.session.

SessionManagementFilter

인증된 주체를 바탕으로 세션 트래킹을 처리해 단일 주체와 관련한 모

든 세션들이 트래킹되도록 돕는다.

o.s.s.web.access.

ExceptionTranslationFilter

이 필터는 보호된 요청을 처리하는 동안 발생할 수 있는 기대한 예외의

기본 라우팅과 위임을 처리한다.

o.s.s.web.access.intercept.

FilterSecurityInterceptor

이 필터는 권한부여와 관련한 결정을 AccessDecisionManager에게 위임해 권한부여 결정 및 접근 제어 결정을 쉽게 만들어 준다.

스프링 시큐리티에는 사용자의 요청 행동을 별도로 제어해야 하는 경우 선택적으로 사용할 수

있는 약 25개의 필터가 있다. 물론 원한다면 javax.servlet.Filter 인터페이스를 구현해 커스텀 서블

릿 필터를 추가할 수도 있다.

앞의 표에 나온 서블릿 필터들은 설정 XML 파일에서 auto-config 어트리뷰트를 사용할 때 자동

으로 생성되는 서블릿 필터 목록이라는 사실을 기억하자. 이러한 서블릿 필터들은 나중에 보겠지

만 설정 디렉티브를 사용해 명시적으로 추가하거나 제외할 수 있다.

또 필터 체인을 아예 처음부터 설정하는 것도 가능하다. 이 설정은 연결해야 할 의존성이 많기

때문에 번거로울 수 있지만 아주 유연한 설정과 애플리케이션에 최적화된 설정이 가능하다는 장

점이 있다. 이러한 설정을 하는 데 필요한 스프링 빈 설정은 6장에서 설명한다.

이쯤에서 DelegatingFilterProxy에서 스프링 시큐리티가 설정한 필터 체인을 어떻게 찾아내는지

궁금한 독자도 있을 것이다. 앞에서 본 web.xml 파일에서 <filter-name>에 DelegatingFilterProxy를 추

가했던 부분을 떠올려 보자.

<filter>

<filter-name>springSecurityFilterChain</filter-name>

<filter-class>

org.springframework.web.filter.DelegatingFilterProxy

</filter-class>

</filter>

이 필터의 이름은 우연히 정해진 게 아니고 스프링 시큐리티가 DelegatingFilterProxy와 연결되

게끔 일부러 이렇게 지정한 것이다. 이처럼 명시적으로 설정하지 않으면 DelegatingFilterProxy는

Page 57: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

02 스프링 시큐리티 시작하기 l 29

(�lter-name 엘리먼트에 지정된 이름과) 동일한 이름의 스프링 WebApplicationContext에서 설정 빈

을 찾는다. DelegatingFilterProxy 설정에 대한 자세한 사항은 이 클래스의 JavaDoc을 참고하자.

auto-config가 내부적으로 하는 작업들

스프링 시큐리티 3에서 auto-config는 아래의 세 가지 인증과 관련한 기능을 자동으로 제공하는 데

사용한다.

HTTP 기본 인증 ▶

폼 로그인 인증 ▶

로그아웃 ▶

물론 설정 엘리먼트를 사용해 auto-config가 제공하는 수준보다 더 정확하게 앞의 세 기능을 사

용하도록 선언할 수도 있다. 이러한 고급 기능과 관련한 설정은 이어서 나오는 장에서 하나씩 보게

될 것이다.

잠깐만요! auto-config는 예전의 auto-config가 아니다!

스프링 시큐리티 3 이전 버전에서는 auto-config가 지금보다 더 많은 설정을 수행했다. 하

지만 여전히 security 네임스페이스 방식의 설정을 사용하면 스프링 시큐리티 2에서 auto-

config가 수행했던 설정 중 많은 부분이 기본으로 수행된다. 스프링 시큐리티 2에서 3으로

옮기는 마이그레이션 이슈에 대한 좀더 자세한 내용은 13장, ‘스프링 시큐리티 3 마이그레

이션’을 참고하자.

이러한 인증 관련 기능과 더불어 필터 체인의 나머지 부분들도 <http> 엘리먼트를 사용하는 순

간 자동으로 설정된다.

사용자들은 어떻게 인증받을까?

사용자가 로그인 폼에 정보를 입력하면 입력한 정보는 보호된 시스템상에서 사용자가 다음 단계

로 진행하기 전에 크리덴셜 저장소에 대한 검증 절차를 거쳐야 한다. 사용자가 제시한 크리덴셜을

검증하는 과정에는 인증 절차를 캡슐화하기 위한 일련의 논리적인 컴포넌트들이 사용된다.

이 절에서는 사용자명과 비밀번호로 구성된 로그인 폼 예제를 설명하면서 필요할 때마다 사용

Page 58: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

30 l 스프링 시큐리티 3

자명/비밀번호 인증과 관련한 인터페이스 및 구현체에 대해 언급하겠다. 여기서 설명하는 인증과

관련한 전체 아키텍처는 폼 기반 로그인 요청을 인증하든 CAS 같은 외부 인증 프로바이더를 사

용하든, 또는 사용자 크리덴셜 저장소가 데이터베이스에 있든 LDAP에 있든 동일하다. 여기서 살

펴보는 폼 기반 로그인 인증 개념이 이처럼 좀더 복잡한 인증 방식과 어떻게 연동되는지는 이 책의

후반부에서 설명한다.

다음 다이어그램에는 인증과 관련한 주요 인터페이스들이 정리돼 있다.

사용자

요청

요청 처리 생성

요청에서 받은 사용자 크리덴셜을 나타냄검증을 위해 o.s.s.core.Authentication 객체를 전송

유효한 크리덴셜인지 체크

다음과 대조해 크리덴셜을 검증

인증 크리덴셜 저장소

유효하지 않은 크리덴셜

예외를 던짐

유효한 크리덴셜일 경우 추가 Principal 정보를 채워 넣음

이후 처리를 위해 요청을 정리

사용자가 인증됨

인증 저장소와 1..n 관계

이 그림에서는 고수준 다이어그램을 통해 개발자를 대신해 어려운 작업 대부분을 처리해 주는

세 가지 주요 컴포넌트를 보여준다.

인터페이스 이름 설명 / 역할

AbstractAuthenticationProcessingFilter 웹 기반 인증 요청에 사용한다. 폼 POST, SSO 정보 또는

기타 사용자가 제공한 크리덴셜을 포함한 요청을 처리한다.

책임 사슬을 따라 사용자 크리덴셜을 전달하기 위해 부분

적으로 채워진 Authentication 객체를 생성한다.

AuthenticationManager 사용자의 크리덴셜을 검증하고, (인증 실패 시) 특정 예외

를 던지거나 (인증 성공 시) 권한 정보와 같은 내용을 포함

해 Authentication 객체를 완전히 채우는 역할을 한다.

Page 59: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

02 스프링 시큐리티 시작하기 l 31

인터페이스 이름 설명 / 역할

AuthenticationProvider AuthenticationManager에 크리덴셜 검증 기능을 제공

하는 일을 담당한다. 일부 AuthenticationProvider 구현체들은 데이터베이스와 같은 크리덴셜 저장소를 바탕으

로 크리덴셜을 받아들일지 여부를 부분적으로 결정한다.

이 중 아래 두 주요 인터페이스 구현체 6 는 인증 체인에 참여한 대상에 의해 인스턴스가 생성되

며, 인증된 (또는 아직 인증되지 않은) 주체 및 해당 주체의 권한에 대한 상세 정보를 캡슐화하는

데 사용된다.

o.s.s.core.Authentication 인터페이스는 자주 사용하게 될 인터페이스로, 사용자 식별자(예,

사용자명), 크리덴셜(예, 비밀번호)에 대한 상세 정보 및 사용자에게 주어진 하나 이상의 권한

(o.s.s.core.GrantedAuthority)을 저장한다. 개발자는 주로 Authentication 객체를 사용해 인증된 사

용자에 대한 상세 정보를 가져오고 커스텀 인증이 필요한 경우에는 애플리케이션 의존적인 추가

정보를 가지고 Authentication 객체를 확장한다.

Authentication 인터페이스에서 사용할 수 있는 메서드의 목록은 다음과 같다.

메서드 시그너처 설명

Object getPrincipal() 주체에 대한 고유 식별자를 반환한다. (예, 사용자명)

Object getCredentials() 주체의 크리덴셜을 반환한다.

List<GrantedAuthority> getAuthorities() 인증 저장소에 의해 결정된 주체의 권한 집합을 반환한다.

Object getDetails() 주체에 대한 인증 프로바이더 의존적인 상세 정보를 반환

한다.

위의 표를 유심히 본 독자라면 Authentication 인터페이스에 java.lang.Object 타입의 객체를 반

환하는 메서드가 일부 들어 있는 것을 봤을 것이다. 이러한 메서드를 사용할 때는 컴파일 시에

Authentication 객체에 대한 메서드 호출 결과로 어떤 타입의 객체가 반환될지 파악하기가 쉽지

않다.

AuthenticationProvider는 AuthenticationManager 인터페이스에서 직접 사용하거나 참조하

지 않는다는 점에 주의하자. 스프링 시큐리티에서는 AuthenticationManager를 구현한 구체적

인 구현체를 (하위 클래스에 해당하는 7 ) o.s.s.authentication.ProviderManager 하나만 제공하

6   (옮긴이) AuthenticationManager와 AuthenticationProvider 구현체를 말한다.

7   (옮긴이) ProviderManager 클래스는 AuthenticationManager 인터페이스를 구현하는

AbstractAuthenticationManager 추상 클래스의 하위 클래스다.

Page 60: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

32 l 스프링 시큐리티 3

고 있다. 이 클래스는 앞에서 설명한 대로 하나 이상의 AuthenticationProvider를 사용한다.

AuthenticationProvider는 매우 자주 사용되고 ProviderManager와도 연계성이 크기 때문에 기본 설

정을 사용한 가장 일반적인 상황에서 이 클래스를 어떻게 사용하는지 살펴보면 이해하는 데 많은

도움이 될 것이다.

웹 기반의 사용자명/비밀번호 인증 요청을 처리하는 데 포함되는 클래스에 대해 좀더 자세히 살

펴보면서 세부적인 내용들을 확인해 보자.

아래 필터에 작업을 위임

아래 인터페이스를 사용해 크레덴셜을 검증

아래 클래스에 의해 구현됨

순환문을 통해 모든 AuthenticationProvider를

검사한다

인증된 사용자와 권한을 나타내는 Authentication 객체를 반환

다음을 통해 사용자명과 비밀번호를 획득

생성

이 인증 프로바이더에서 UsernamePassword

AuthenticationToken을 지원하는가?

아니오

아니오

예외 발생

인증이 성공했는가?

앞에서 본 고수준 다이어그램에 표현된 전체적인 작업 흐름을 살펴보고 실제 폼 기반 인증을 구

현한 예제를 통해 이를 개념적으로 이해해 보자. 이 그림에서는 UsernamePasswordAuthenticationFi

lter가 (추상 상위 클래스로부터의 위임을 통해) UsernamePasswordAuthenticationToken(Authenticat

ion 인터페이스의 구현체)을 생성하고 HttpServletRequest에 있는 정보를 바탕으로 토큰의 내용을

일부 채우는 것을 확인할 수 있다. 그렇다면 사용자명과 비밀번호는 대체 어디에서 오는 것일까?

spring_security_login은 무엇이고 우리는 여기까지 어떻게 온 것일까?

브라우저에서 JBCP Pets 온라인 상점의 주소를 입력하면 http://localhost:8080/JBCPPets/spring_

security_login으로 페이지가 리다이렉트된 것을 볼 수 있다.

Page 61: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

02 스프링 시큐리티 시작하기 l 33

이 URL에서 spring_security_login 부분은 DefaultLoginPageGeneratingFilter 클래스에 지정된

기본 로그인 페이지를 나타낸다. 이 페이지가 애플리케이션에 적합한 이름으로 보이게 하려면 설

정 어트리뷰트를 수정하면 된다.

잠깐만요! 로그인 페이지 URL의 기본값은 바꾸는 게 좋다.

로그인 페이지 URL의 기본값을 바꾸면 URL이 사용자와 검색 엔진에 더 친숙해질 뿐 아

니라 스프링 시큐리티를 사용해 보안을 적용한다는 사실을 숨길 수 있는 장점이 있다. 이

와 같이 스프링 시큐리티를 사용하는 사실을 숨기면 향후에 혹시라도 스프링 시큐리티에

보안 취약점이 생겼을 때 이를 악용해 사이트를 공격하려는 해커로부터 사이트를 보호하

는 데 조금이나마 도움될 것이다. 물론 이렇게 URL을 은폐한다고 해서 애플리케이션 자

체의 취약점이 보완되는 것은 아니지만 해커가 사이트의 취약점을 자동으로 판단하는 표

준 해킹 툴을 사용할 경우 공격을 어렵게 만들 수 있다. 하지만 "spring"이라는 이름이 꼭

이 URL에만 등장하는 것은 아니다! 앞으로도 이와 유사한 부분들을 만나게 될 것이다.

UsernamePasswordAuthenticationFilter에서 기대하는 값을 보기 위해 (테이블 레이아웃 정보를

제외한) 이 폼의 HTML 소스를 살펴보자.

<form name='f' action='/JBCPPets/j_spring_security_check' method='POST'>

User:<input type='text' name='j_username' value=''>

Password:<input type='password' name='j_password'/>

<input name="submit" type="submit"/>

<input name="reset" type="reset"/>

</form>

소스를 보면 범상치 않은 이름(j_username와 j_password)이 사용자명과 비밀번호 폼 필드에 사용

되고 이 폼의 액션이 우리가 전혀 설정한 적이 없는 j_spring_security_check로 된 것을 볼 수 있다.

Page 62: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

34 l 스프링 시큐리티 3

그럼 이런 값들은 어디서 온 것일까?

폼 필드 이름은 UsernamePasswordAuthenticationFilter 자체에서 지정된 것이고, 자바 EE 서블릿

2.x 명세(SRV.12.5.3절)를 따른 것이다. 이 명세에서는 로그인 폼이 특정 필드명을 사용하고 특정

폼 액션인 j_security_check를 사용하도록 규정한다. 이러한 설계 패턴의 목적은 자바 EE 서블릿

기반 애플리케이션 개발자들이 표준 방식으로 서블릿 컨테이너의 보안 설정과의 연동을 돕는 데

있다.

예제 애플리케이션에서는 호스트 서블릿 컨테이너의 보안 컴포넌트를 사용하지 않기 때문에

UsernamePasswordAuthenticationFilter를 명시적으로 설정해 다른 이름의 폼 필드를 기대하게 할 수

도 있다. 하지만 이러한 설정은 생각보다 복잡하므로 지금은 UsernamePasswordAuthenticationFilter

의 흐름을 파악하고 어떻게 이 필터를 설정에서 사용하게 되는지부터 먼저 이해하자(이 필터와 관

련한 설정은 6장에서 설명한다).

UsernamePasswordAuthenticationFilter는 <http> 설정 디렉티브의 <form-login> 하위 엘리먼트를

사용해 설정된다. 앞에서 언급한 것처럼 auto-config 어트리뷰트는 명시적으로 디렉티브를 포함시

키지 않을 경우 애플리케이션에 <form-login> 기능을 자동으로 추가한다.

예상한 독자도 있겠지만 j_spring_security_check라는 URL은 애플리케이션의 물리적인 대상에

매핑한 URL이 아니다. 이 URL은 특수 URL로서 폼 기반 로그인을 처리하도록 UsernamePassword

AuthenticationFilter에서 감시하는 URL이다. 스프링 시큐리티에는 스프링 시큐리티의 전역 행동

을 처리하는 이러한 특수 URL이 몇 개 있다. 이러한 URL에 대한 표는 부록, ‘추가 참고 자료’에 나

와 있다.

사용자의 크리덴셜은 어디에서 검증될까?

앞의 3단계 설정에서는 빠른 예제 진행을 위해 인 메모리 크리덴셜 저장소를 사용했다.

<authentication-manager alias="authenticationManager">

<authentication-provider>

<user-service>

<user authorities="ROLE_USER" name="guest" password="guest"/>

</user-service>

</authentication-provider>

</authentication-manager>

Page 63: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

02 스프링 시큐리티 시작하기 l 35

앞에서는 AuthenticationProvider를 명시적인 구현체와 연결하지 않아도 security 네임스페이스

핸들러에서 많은 설정을 자동으로 해 주는 것을 봤다. 기본 AuthenticationManager 구현체는 하나

이상의 AuthenticationProvider 구현체를 지원한다는 점을 기억하자. <authentication-provider> 선

언은 기본적으로 o.s.s.authentication.dao.DaoAuthenticationProvider 구현체의 인스턴스를 생성한

다. 이때 <authentication-provider> 엘리먼트를 선언하면 AuthenticationProvider가 설정된 (예제의

경우, 자동으로 설정된) AuthenticationManager와 자동으로 연결된다.

DaoAuthenticationProvider는 AuthenticationProvider 인터페이스를 구현한 가벼운 래퍼

AuthenticationProvider로서, o.s.s.core.userdetails.UserDetailsService 인터페이스 구현체에 작업

을 위임한다. UserDetailsService는 o.s.s.core.userdetails.UserDetails 구현체를 반환하는 기능을

담당한다.

UserDetails에 대한 JavaDoc을 살펴보면, 앞에서 본 Authentication 인터페이스와 매우 유사한

점을 확인할 수 있다. 하지만 이렇게 메서드의 이름과 기능들이 상당 부분 겹치더라도 이 둘의 용

도는 서로 다르므로 혼동해서는 안 된다.

인터페이스 용도

Authentication 주체의 신원, 비밀번호, 인증 요청과 관련한 컨텍스트에 대한 정보를 저장한다. 이 객체

에는 사용자에 대한 인증 후 정보(이 정보에는 UserDetails 인스턴스가 포함될 수도 있다)도 들어 있을 수 있다. 인증 과정에서 특별히 필요한 정보를 지원하기 위한 용도를 제

외하고는 확장하지 않는 게 일반적이다.

UserDetails 이름, 이메일, 전화번호 등 주체의 프로필을 저장하는 데 사용된다. 업무상의 요구조건을

지원하기 위해 확장해 사용하는 게 일반적이다.

앞에서처럼 <user-service> 하위 엘리먼트를 선언하면 UserDetailsService를 구현한 o.s.s.core.

userdetails.memory.InMemoryDaoImpl 구현체가 설정된다. 쉽게 생각할 수 있는 것처럼 이 구현체에서

는 XML 보안 설정 파일에 설정한 사용자를 메모리에 상주하는 데이터 저장소에 저장한다. 이 서

비스 설정에서는 사용자 계정을 비활성화하거나 사용자 계정에 락을 거는 다른 어트리뷰트들도

사용할 수 있다.

DaoAuthenticationProvider의 컴포넌트들이 어떻게 상호작용해 AuthenticationManager에 인증 기

능을 제공하는지 그림을 통해 살펴보자.

Page 64: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

36 l 스프링 시큐리티 3

인증 결정을 위해 제공됨

Authentication 인스턴스

UserDetails를 조회

Authentication 객체에서 얻은 크리덴셜을 UserDetails에서

얻은 크리덴셜과 비교해 검증 UserDetails를 반환

조회에 사용됨

인메모리저장소

쉽게 예상할 수 있는 것처럼 인증에는 극도의 설정이 들어갈 수도 있다. 대부분의 스프링 시큐리

티 예제에서는 인 메모리 사용자 크리덴셜을 사용하거나 JDBC (데이터베이스) 크리덴셜 저장소

를 사용한다. 앞의 설명에서 JBCP Pets 애플리케이션의 크리덴셜을 데이터베이스에 저장하게끔

애플리케이션을 수정하는 게 좋다고 말한 적이 있는데 이와 관련한 설정은 4장에서 설명한다.

잘 진행되던 인증이 어긋나는 시점은 언제일까?

스프링 시큐리티에서는 예상한 (애플리케이션) 예외에 잘 대처해 특정 예외 상황을 표현하며 이러

한 예외 상황을 잘 정의된 로직을 통해 처리한다. 스프링 시큐리티를 사용할 때 매일 이런 예외와

직접 부딪치지는 않겠지만 이러한 예외를 잘 알아두면 문제를 디버깅하거나 애플리케이션의 흐름

을 이해하는 데 많은 도움이 된다.

인증과 관련한 모든 예외 클래스는 o.s.s.core.AuthenticationException 클래스를 상속한다. 표준

예외 기능을 지원하기 위해 AuthenticationException에는 오류를 디버깅하거나 사용자에게 도움이

되는 메시지를 보여주기 위한 두 가지 멤버 필드가 있다.

authentication ▶ : 인증 요청과 관련한 Authentication 인스턴스를 보관한다.

▶ extraInformation : 특정 예외와 관련한 부가 정보를 포함한다. 예를 들어 이 필드에서

UsernameNotFoundException은 인증에 실패한 사용자명을 나타낸다.

Page 65: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

02 스프링 시큐리티 시작하기 l 37

가장 빈번히 발생하는 예외는 아래 표에 정리돼 있다. 인증 예외와 관련한 전체 목록은 부록, ‘추

가 참고 자료’에 나와 있다.

예외 클래스 발생 시점 부가 정보

BadCredentialsException 사용자명이 제공되지 않았거나 인증 저장소에 있는

비밀번호와 사용자명이 일치하지 않는 경우UserDetails

LockedException 사용자의 계정에 락이 걸린 경우 UserDetails

UsernameNotFoundException 사용자명을 인증 저장소에서 찾을 수 없거나 사용자

명에 부여된 GrantedAuthority가 없는 경우

(사용자명을 포

함하는) 문자열

이러한 예외 또는 다른 예외는 필터 체인을 따라 위로 전달돼 요청을 처리하는 필터를 통해 사

용자를 적절한 페이지(로그인 또는 접근 거부 페이지)로 리다이렉트하거나 HTTP 403(접근 거부)

과 같은 상태 코드를 반환하는 방식으로 깔끔하게 처리된다.

요청은 어떻게 권한부여를 받을까?

기본 스프링 시큐리티 필터 체인에서 제일 마지막 서블릿 필터는 FilterSecurityInterceptor로 이 필

터는 특정 요청을 받아들일지 거부할지를 결정하는 일을 담당한다. FilterSecurityInterceptor 필

터가 호출되는 시점에서 사용자는 이미 인증이 완료되고 시스템에서는 사용자가 유효한 사용자

라는 것을 이미 알고 있다. Authentication 인터페이스에서 주체에 대한 권한 목록을 반환하는 특

정 메서드(List<GrantedAuthority>getAuthorities())를 정의한 사실을 기억하자. 권한부여 절차에서

는 바로 이 메서드에서 반환하는 정보를 활용해 특정 요청에 대해 요청을 승인할지 거부할지를 판

단한다.

권한부여는 이진 결정(binary decision)이라는 사실을 기억하자. 사용자는 보호된 리소스에 접근

하거나 접근하지 못할 뿐이다. 권한부여와 관련해 모호한 영역이란 존재하지 않는다.

스프링 시큐리티 프레임워크에는 똑똑한 객체지향적 설계를 폭넓게 적용하고 있고 이 점에 있

어서는 권한부여도 마찬가지다. 이 장의 앞에서는 접근 결정 관리자(AccessDecisionManager)라는

컴포넌트가 권한부여 결정을 책임진다고 설명했다.

스프링 시큐리티에서 o.s.s.access.AccessDecisionManager 인터페이스는 요청을 처리하는 결정 흐

름에 꼭 부합하는 두 개의 간단하고 논리적인 메서드들을 정의하고 있다.

▶ supports : 실제로 이 논리적 작업은 AccessDecisionManager 구현체가 현재 요청을 지원하는

지 여부를 알려주는 두 개의 메서드 8 로 이뤄져 있다.

8  (옮긴이) supports(java.lang.Class<?> clazz) 메서드와 supports(ConfigAttribute attribute) 메서드다.

Page 66: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

38 l 스프링 시큐리티 3

▶ decide : 이 메서드에서는 AccessDecisionManager가 요청 컨텍스트와 보안 설정을 바탕으로

접근을 승인할지 여부를 결정하게끔 도와준다. decide에는 반환값이 없고 대신 접근 거부

를 나타내는 예외를 던져 요청이 거부됐음을 알려준다.

인증 과정에서 예상한 에러를 처리하기 위해 AuthenticationException과 하위 클래스들을 사용

했던 것처럼 특정 타입의 예외 클래스들을 사용하면 애플리케이션이 권한부여 결정을 내리는 행

동을 제어할 수 있다. o.s.s.access.AccessDeniedException 클래스는 권한부여 시 가장 흔히 발생하

는 예외로, 필터 체인에서 특별히 처리해야 할 예외다. 이에 대한 자세한 내용은 6장에서 고급 설정

을 다루면서 설명하겠다.

AccessDecisionManager 구현체는 표준 스프링 빈 바인딩과 레퍼런스를 사용해 완전히 설정할 수

있다. 기본 AccessDecisionManager 구현체는 AccessDecisionVoter와 보트(vote) 9 취합을 통한 접근 승

인 방식을 제공한다.

보터(voter)는 권한부여 과정에서 다음 중 하나 또는 전체를 판단하는 주인공이다.

보호된 리소스에 대한 요청 (IP 주소를 요청하는 URL) 컨텍스트 ▶

(존재할 경우) 사용자가 제시한 크리덴셜 ▶

접근하려는 보호된 리소스 ▶

시스템의 설정 매개변수와 리소스 자체 ▶

AccessDecisionManager는 요청된 리소스에 대한 (코드에서는 ConfigAttribute 인터페이스의 구현

체를 통해 참조되는) access 선언 10 을 보터에게 전달하는 책임도 진다. 웹 URL과 관련해 보터는 리

소스의 access 선언에 대한 정보를 갖고 있다. 기본 설정 파일의 URL 인터셉트 선언을 살펴보면 사

용자가 접근하려는 리소스에 대한 접근 설정으로 ROLE_USER를 사용하는 것을 볼 수 있다.

<intercept-url pattern="/*" access="ROLE_USER"/>

보터는 자신이 가지고 있는 지식을 바탕으로 사용자가 리소스에 접근할 수 있는지 판단한다. 스

프링 시큐리티에서는 보터가 세 가지 중 한 가지 결정을 내리는데, 이러한 결정에 대한 논리적 정의

9   (옮긴이) 스프링 시큐리티의 권한부여 과정에서는 보터(voter)가 참여해 권한을 승인할지, 거부할지, 보류할지 판단한다.

이러한 판단은 AccessDecisionVoter 인터페이스를 구현한 보터 구현체가 하며, 보터 구현체는 vote(Authentication

authentication, java.lang.Object object, java.util.Collection<ConfigAttribute> attributes) 메서드를 통해 상

수로 정의된 정수 값을 보트 결과로 반환한다.

10   (옮긴이) access 엘리먼트에 선언한 선언으로 이 책에서는 access 선언 또는 접근 선언이란 용어를 사용했다.

Page 67: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

02 스프링 시큐리티 시작하기 l 39

는 o.s.s.access.AccessDecisionVoter 인터페이스의 상수와 매핑돼 있다.

결정 타입 설명

승인 (ACCESS_GRANTED) 보터가 리소스에 대한 접근 권한을 줄 것을 권장한다.

거부 (ACCESS_DENIED) 보터가 리소스에 대한 접근 권한을 거부할 것을 권장한다.

보류 (ACCESS_ABSTAIN) 보터가 리소스에 대한 접근 권한을 보류할 것을 권장한다(의사 결정을 내

리지 않는다). 이러한 결정 보류는 몇 가지 이유로 일어날 수 있다.

• 보터가 결정적인 정보를 갖고 있지 못한 경우

• 보터가 이 타입의 요청에 대해 결정을 내릴 수 없는 경우

접근 권한 관련 객체와 인터페이스의 설계 부분에서 짐작한 독자도 있겠지만 스프링 시큐리티

를 웹 도메인에 국한된 인증 및 접근 제어 시나리오에만 쓸 수 있는 건 아니다. 5장, ‘미세 접근 제

어’에서 메서드 레벨의 보안을 처리하면서 보터와 접근 결정 관리자에 대해 다시 알아볼 것이다.

이러한 내용들을 종합하면 ‘웹 요청에 대한 기본 인증 체크’의 진행 과정은 다음 다이어그램과

유사하게 정리할 수 있다.

ConfigAttribute를획득

여기서 ConfigAttribute는 요청 URL 패턴, 예를 들어 ROLE_USER를 바탕으로 기대한 역할(들)에 해당한다

순환문을 통해 모든 보터를 검사한다

익명 사용자 또는 remember me 인증 사용자를 처리하기 위한 AuthenticatedVoter도 제공된다

ACCESS_GRANTED가 있는가?

ACCESS_DENY가 있는가?

모두 보류일 경우 거부로 판단하는가?

사용자가 접근을 승인 받음

사용자의 GrantedAuthority가

ROLE_ 접두사로 시작하는가?

사용자의 GrantedAuthority가 제공된 ConfigAttribute 중 하나와

일치하는가?

아니오

아니오

아니오

아니오

아니오

웹의 기본 보터

Page 68: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

40 l 스프링 시큐리티 3

앞의 그림에서는 ConfigAttribute의 추상화로 인해 관여하는 클래스들이 ConfigAttribute의 내용

을 이해하지 못하더라도 데이터가 (DefaultFilterInvocationSecurityMetadataSource에 보관된) 설정

선언으로부터 ConfigAttribute에 대해 동작하는 보터로 전달되는 사실을 확인할 수 있다. 이와 같은

관심사의 분리는 동일한 접근 결정 패턴을 사용해 새로운 유형의 보안 선언(메서드 보안과 관련해

보게 될 선언)을 개발하는 데 튼튼한 기초가 된다.

접근 결정의 취합 방식 설정

스프링 시큐리티에서는 security 네임스페이스에서 AccessDecisionManager를 설정할 수 있다.

<http> 엘리먼트의 access-decision-manager-ref 어트리뷰트는 AccessDecisionManager를 구현한 스

프링 빈에 대한 레퍼런스를 설정할 수 있게 해 주는 어트리뷰트다. 스프링 시큐리티에서는 이 인터

페이스를 구현하는 세 가지 구현체 클래스를 제공하는데, 이들 클래스는 모두 o.s.s.access.vote 패

키지에 들어 있다.

클래스 이름 설명

AffirmativeBased 보터가 접근을 승인하면 이전에 거부된 내용과 상관없이 접근이 곧바로 승인된다.

ConsensusBased 다수 표(승인 또는 거부)가 AccessDecisionManager의 결정에 영향을 준다. 동률 또는 무효표에 대한 처리는 설정 가능하다.

UnanimousBased 모든 보터가 만장일치로 접근을 승인해야 한다. 그렇지 않을 경우 접근이 거부된다.

UnanimousBased 접근 결정 관리자를 사용하도록 설정하기

애플리케이션에서 UnanimousBased 접근 결정 관리자를 사용하도록 수정하려면 두 부분을 수정해

야 한다. 먼저 access-decision-manager-ref 어트리뷰트를 <http> 엘리먼트에 추가해야 한다.

<http auto-config="true"

access-decision-manager-ref="unanimousBased"

>

이 어트리뷰는 표준 스프링 빈에 대한 레퍼런스이므로 빈의 id 어트리뷰트와도 일치해야 한다.

이어서 (dogstore-base.xml 파일에서) 앞에서 참조한 것과 동일한 ID를 사용해 빈을 선언한다.

<bean class="org.springframework.security.access.vote.UnanimousBased" id="unanimousBased">

<property name="decisionVoters">

<list>

<ref bean="roleVoter"/>

<ref bean="authenticatedVoter"/>

</list>

Page 69: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

02 스프링 시큐리티 시작하기 l 41

</property>

</bean>

<bean class="org.springframework.security.access.vote.RoleVoter" id="roleVoter"/>

<bean class="org.springframework.security.access.vote.AuthenticatedVoter"

id="authenticatedVoter"/>

여기서 decisionVoters 속성이 하는 일이 궁금할 것이다. 이 속성은 커스텀 AccessDecisionManager

를 선언하기 전까지는 자동으로 설정된다. 기본 AccessDecisionManager에서는 인증 결정에 참여할

보터의 목록을 선언하고 있다. 앞에 나온 두 보터는 security 네임스페이스 설정에서 기본으로 제

공하고 있는 두 보터를 명시적으로 나열한 것이다.

아쉽게도 스프링 시큐리티에서 다양한 보터들을 기본 제공하지는 않지만 AccessDecisionVoter

인터페이스를 직접 구현한 클래스를 추가하는 건 어렵지 않다. 이에 대한 예제는 6장에서 확인할

수 있다.

앞에서 참조한 두 보터 구현체는 다음과 같은 일을 한다.

클래스 이름 설명 예제

o.s.s.access.

vote.RoleVoter

사용자가 선언된 역할에 부합하는 GrantedAuthority를

가졌는지 확인한다. 이 구현체에서는 access 어트리뷰트

에서 콤마 구분자를 사용해 개별 GrantedAuthority 이

름을 정의할 것으로 예상한다. 접두어로는 ROLE_ 접두어를 사용할 것으로 예상하지만, 접두어는 설정 가능하다.

access="ROLE_

USER, ROLE_

ADMIN"

o.s.s.access.vote.

Authenticated

Voter

와일드카드와 매칭되는 특수 선언들을 지원한다.

• IS_AUTHENTICATED_FULLY – 새로운 사용자명과 비밀번호가 입력된 경우 접근을 승인한다.

• IS_AUTHENTICATED_REMEMBERED – 사용자가 remember me 기능을 사용해 인증한 경우 접근을

승인한다.

• IS_AUTHENTICATED_ANONYMOUSLY – 사용자가 익명 사용자인 경우 접근을 승인한다.

access="IS_

AUTHENTICATED_

ANONYMOUSLY"

스프링 표현식 언어를 사용한 접근 설정

RoleVoter에서 구현하는 표준 역할 기반 보트 방식 대신 스프링 표현식 언어(SpEL)를 사용해 보트

에 대한 복잡한 규칙을 임의로 지정할 수도 있다. 이 방식을 사용하려면 다음과 같이 <http> 설정

엘리먼트에서 use-expressions 어트리뷰트를 추가하면 된다.

<http auto-config="true" use-expressions="true">

Page 70: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

42 l 스프링 시큐리티 3

이와 같이 하면 URL 인터셉트 규칙 정의에서 SpEL 표현식을 기대하도록 access 어트리뷰트

의 행동이 수정된다. SpEL 표현식 방식을 사용하면 표현식 언어를 사용해 접근 조건을 지정할

수 있다. 이때는 단순히 ROLE_USER와 같은 문자열을 사용하는 대신 메서드를 호출하거나 시스템

properties를 참조하거나 값을 연산하는 등 복잡한 작업들을 수행하는 표현식을 설정 파일에 지

정할 수 있다.

SpEL 문법은 태피스트리(Tapestry) 프레임워크나 JSP, JSF 통합 표현식 언어(JSP 및/또는 JSF 개

발 시 사용)에서 사용되는 객체 그래프 표기법 언어(OGNL) 같은 다른 표현식 언어와 유사한 형태

다. SpEL 문법은 양이 많기 때문에 이 언어에 대한 전체 설명은 이 책의 범위를 벗어난다. 하지만

여기에서 사용한 표현식 예제들을 참고하면 이해하는 데 도움될 것이다.

use-expressions 어트리뷰트를 설정해 SpEL 표현식 기반의 접근 규칙을 사용할 때 주의할 점은

앞에서 간단한 설정을 적용했을 때와는 달리 역할 선언을 이해하고 있는 RoleVoter의 자동 설정 기

능을 사용할 수 없다는 점이다.

<intercept-url pattern="/*" access="ROLE_USER"/>

이 말은 역할만을 기준으로 접근을 필터링할 경우 접근 정의를 바꿔야 한다는 뜻이다. 다행히

이 부분은 SpEL 표현식에서 이미 고려해 설계됐고, SpEL 메서드인 hasRole을 사용하면 역할을 체

크할 수 있다. 표현식을 사용해 예제 설정 파일을 다시 작성하려면 설정 파일을 다음과 같이 수정

하면 된다.

<http auto-config="true" use-expressions="true" 11 >

<intercept-url pattern="/*" access="hasRole('ROLE_USER')"/>

</http>

이미 예상한 독자도 있겠지만 SpEL에 대한 처리는 SpEL 표현식을 어떻게 처리해야 할지 이해

하고 있는 다른 보터 구현체인 o.s.s.web.access.expression.WebExpressionVoter를 통해 이뤄진다.

WebExpressionVoter는 이러한 SpEL 처리를 위해 o.s.s.web.access.expression.WebSecurity

ExpressionHandler 인터페이스 구현체에 의존한다. WebSecurityExpressionHandler는 표현식을 해석

하는 일과 표현식에서 참조한 보안과 관련된 특정 메서드를 제공하는 일을 한다. 이 인터페이스의

기본 구현체에서는 o.s.s.web.access.expression.WebSecurityExpressionRoot 클래스에 정의된 메서드

를 노출시킨다.

11   (옮긴이) 현재 예제 소스 파일에의 설정에는 이 엘리먼트 선언부에 access-decision-manager-ref="unanimousBased"가 추

가돼 있는데 책의 내용처럼 이 부분을 빼야 예제가 제대로 동작한다. 위키북스에서 제공하는 예제 소스에서는 이 부분을 수

정했다.

Page 71: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

02 스프링 시큐리티 시작하기 l 43

이들 클래스 사이의 흐름과 관계는 다음 다이어그램에 나와 있다.

보트를 획득

SpEL 컨텍스트를 획득

SpEL 메서드를 획득

완료된 경우

표현식을 Boolean 값으로 해석

AccessDecisionManager에게 반환된 보트

SpEL 접근 표현식에 대한 메서드와 의사 속성들은 WebSecurityExpessionRoot 클래스와 그것의

상위 클래스에 정의돼 있는 public 메서드에서 정의한다.

알아두세요! 의사 속성이란 매개변수를 받지 않는 메서드로서, 자바 빈 명명 규약에 따

라 게터(getter)로 컴파일되는 메서드를 말한다. 이러한 의사 속성 덕분에 SpEL 표현식

에서는 괄호는 물론 메서드와 관련된 is 또는 get 접두어를 생략할 수 있다. 예를 들어

isAnonymous() 메서드는 anonymous라는 의사 속성을 통해 접근할 수 있다.

!

스프링 시큐리티 3에서 기본으로 제공하는 SpEL 메서드와 의사 속성들은 다음 표에 정리돼 있

다. 이 가운데 ‘웹 전용’으로 표시하지 않은 메서드와 속성은 메서드 호출과 관련한 보안처럼 SpEL

을 사용하는 다른 유형의 보안을 적용할 때 사용한다는 점을 주의하자. 여기 나온 예제 코드는

<intercept-url> access 선언에서 메서드나 속성을 사용하는 방법을 보여준다.

Page 72: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

44 l 스프링 시큐리티 3

메서드 웹 전용 여부 설명 예제

hasIpAddress

(ipAddress)

O 요청 IP 주소가 특정 IP 주소 또는

IP 주소와 넷 마스크를 합친 것과

매칭되는지 판단하는 데 사용된다.

access="hasIpAddress('162

.79.8.30')"

access="hasIpAddress('162

.0.0.0/224')"

hasRole(role) X 역할이 GrantedAuthority와 매칭되는지 확인하는 데 사용된다.

access="hasRole('ROLE_

USER')"

hasAnyRole

(role)

X 사용자의 GrantedAuthority와 역할 목록의 내용 중 매칭되는 내

용이 있는지 확인하는 데 사용된

다. 역할 중 매칭되는 항목이 있는

사용자는 접근이 승인된다.

access="hasAnyRole(

'ROLE_USER','ROLE_ADMIN')"

앞의 표에 있는 메서드 이외에 SpEL 표현식에서는 속성으로 사용할 수 있는 다양한 메서드를

제공하고 있다. 이러한 메서드는 괄호나 메서드 인자 없이 사용할 수 있다.

속성 웹 전용 여부 설명 예제

permitAll X 모든 사용자에 대해 접

근을 항상 승인한다.access="permitAll"

denyAll X 모든 사용자에 대해 접

근을 항상 거부한다.access="denyAll"

anonymous X 사용자가 익명 사용자인

지 나타낸다.access="anonymous"

authenticated X 사용자가 인증됐는지 나

타낸다.access="authenticated"

rememberMe X 사용자가 remember

me 기능을 사용해 인증

됐는지 나타낸다.

access="rememberMe"

fullyAuthenticated X 사용자가 이번 요청 동안

크리덴셜을 전부 갖추고

인증됐는지 나타낸다.

access="fullyAuthenticated"

보터 구현체는 요청 컨텍스트를 바탕으로 (승인, 거부 또는 보류에 해당하는) 의사 결정을 항상

반환해야 한다는 사실을 기억하자. 앞의 그림에서 hasRole은 마치 Boolean을 반환하는 것처럼 보

인다. 실제로 hasRole은 Boolean을 반환한다. SpEL 기반의 접근 선언은 항상 Boolean 결과를 반환

하는 표현식으로만 구성돼야 한다. 이때 결과가 true이면 보터가 접근을 승인함을 나타내고 결과

가 false이면 보터가 접근을 거부함을 나타낸다.

Page 73: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

02 스프링 시큐리티 시작하기 l 45

잠깐만요! 만일 이때 Boolean 값으로 해석할 수 없는 표현식을 반환하려고 하면 다음과 같

은 메시지와 함께 예외가 발생한다.

org.springframework.expression.spel.SpelException:

EL1001E:Type conversion problem, cannot convert from

class java.lang.Integer to java.lang.Boolean

아울러 표현식에서는 접근 선언에 유효하지 않은 SpEL 표현식을 사용한 경우를 제외하고는 보

류를 나타내는 결과를 반환할 수 없다. 접근 선언에 유효하지 않은 SpEL 표현식이 사용된 경우에

한해서만 보터가 보트를 보류한다.

앞에서 본 사소한 제약에 구애받지 않는다면 SpEL 기반의 접근 선언은 접근 의사 결정을 설정

하는 매우 유연한 방식으로 활용할 수 있다.

요약

이 장에서는 인증과 권한부여라는 두 가지 주요 보안 개념에 대한 기본 지식을 자세히 소개했다.

이번 장에서 학습한 내용은 다음과 같다.

보호된 시스템의 고수준 아키텍처를 살펴봤다. ▶

3단계에 걸쳐 스프링 시큐리티의 자동 설정 기능을 사용해 JBCP Pets 웹 사이트에 보안을 ▶

적용했다.

스프링 시큐리티의 서블릿 필터 사용법과 중요성에 대해 살펴봤다. ▶

인증과 권한부여 절차상의 주요 요소들을 살펴보고 이 과정에서 ▶ Authentication과

UserDetails 같은 주요 객체를 살펴봤다.

접근 규칙을 정의하는 SpEL 언어를 가지고 여러 가지 설정을 실험해봤다. ▶

다음 장에서는 기본 사용자명과 비밀번호 인증을 한 단계 발전시켜 좀더 향상된 웹 사이트 사

용자 경험을 전달하도록 주요 기능들을 추가해 보겠다.

Page 74: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

Spri

ng S

ecur

ity

3

Page 75: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

03사용자 경험 개선

이 장에서는 사용자에게 좀더 친숙하고 편리한 경험을 전해 줄 수 있도록 JBCP Pets 온라인 상점

에 기능을 추가하고 안전한 웹 사이트의 주요 기능들을 지원하도록 웹 사이트를 개선해 보겠다.

이 장에서 할 일은 다음과 같다.

로그인, 로그아웃 페이지를 기호 ▶ (preference)에 맞게 커스터마이징하고 이와 같은 커스텀 페

이지를 표준 스프링 웹 MVC 컨트롤러와 연동한다.

사용자 편의를 위해 remember me 기능을 지원하고 이 기능을 보안 측면에서 이해한다. ▶

비밀번호 변경 및 비밀번호 찾기 기능을 포함한 사용자 계정 관리 기능을 개발한다. ▶

로그인 페이지 커스터마이징

앞 장에서는 스프링 시큐리티의 기본 security 네임스페이스 설정을 사용했다. 물론 이런 기본 설

정을 사용하더라도 아주 기본적인 로그인, 인증, 권한부여는 가능하다. 하지만 이 정도 기본 기능

만 가지고는 애플리케이션을 배포하기에 무리가 있다. 상사에게 보고하기 전에 가장 먼저 개선할

부분은 기본 로그인 페이지가 사이트의 다른 페이지와 같은 느낌을 갖게 하는 것이다.

Page 76: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

48 l 스프링 시큐리티 3

현재 로그인 페이지는 다음과 같은 형태로 돼 있다.

자동 설정은 스타일이 적용된 로그인 페이지를 비롯해 다른 주요 기능을 제공하지 않는다. 따라

서 이 장에서는 사이트에 다음 기능을 추가하기로 한다.

헤더, 푸터와 더불어 JBCP Pets 사이트의 다른 페이지와 같은 외양과 느낌을 내는 로그인 ▶

페이지

사용자가 로그아웃할 때 사용할 링크 ▶

사용자가 자신의 비밀번호를 바꿀 수 있는 페이지 ▶

위의 기능을 모두 완성하면 로그인과 로그아웃 기능의 흐름이 다음 화면과 비슷해질 것이다.

사용자

페이지를 요청

접근 결정 관리자

요청을 검사

사용자 인증 실패 시

커스텀 로그인 페이지

사이트 페이지

사용자가 홈 페이지로 리다이렉트됨

다음 페이지에 포함됨

다음 페이지에 포함됨

로그아웃 링크 클릭 시

사용자가 로그아웃됨

사이트 헤더

그럼 이제부터 예제를 통해 사이트 구조를 하나씩 개선해 보자. 여기서는 실제 로그인과 로그아

웃 기능을 구현하고 각 과정을 설명함으로써 이러한 설명을 바탕으로 실제로 사이트의 기본 구조

를 확장할 때 여러분이 각 주제를 자기 것으로 소화할 수 있도록 하겠다.

Page 77: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

03 사용자 경험 개선 l 49

커스텀 로그인 페이지 구현

우선 현재 온라인 상점 사이트와 연동돼 있는 기본 스프링 시큐리티 로그인 페이지를 온라인 상점

에 맞는 로그인 페이지로 대체할 필요가 있다. 이때 구현하려는 로그인 흐름은 다음 그림과 같다.

사용자

페이지를 요청

접근 결정 관리자

요청을 가로챔

사용자에게 적절한 권한이 있는 경우

홈 페이지

사용자가 로그인하지 않았고, 리소스 접근에 인증이 필요한 경우

커스텀 로그인 페이지

로그인 컨트롤러 구현

먼저 로그인을 처리할 수 있게 새로운 스프링 MVC 컨트롤러를 추가하고 이어서 로그아웃 기능

을 구현하겠다. JBCP Pets 사이트는 스프링 MVC의 애노테이션 기반 방식으로 컨트롤러와 웹 사

이트 경로 및 리소스를 설정한다.

com.packtpub.springsecurity.web.controller 패키지에 LoginLogoutController라는 이름으로 새로

운 컨트롤러를 생성하고, 내용을 다음과 같이 작성하자.

// 임포트 생략

@Controller

public class LoginLogoutController extends BaseController{

@RequestMapping(method=RequestMethod.GET,value="/login.do")

public void home() {

}

}

여기서는 코드에서 볼 수 있는 것처럼 아주 간단한 컨트롤러를 추가하고 컨트롤러가 갖고 있는

단일 메서드를 /login.do라는 URL로 매핑하고 있다. 스프링 시큐리티에서 기본 설정을 통해 제공

하는 기본 로그인 페이지를 원하는 페이지로 바꾸는 데 필요한 작업은 현재까지는 이게 전부다.

BaseController 상위 클래스는 애플리케이션의 모든 컨트롤러에서 사용할 수 있는 메서드들을 구

현하는 데 사용할 기저 클래스다.

Page 78: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

50 l 스프링 시큐리티 3

로그인 JSP 페이지 추가하기

/login.do 레퍼런스는 WEB-INF/dogstore-servlet.xml에서 설정한 스프링 MVC 뷰 리졸버가 /WEB-INF/

views에서 login.jsp 페이지를 찾게 해 준다. 이어서 스프링 시큐리티에서 인식하도록 로그인 폼이

들어간 간단한 JSP 페이지를 추가하자. 2장에서 배운 내용을 한 번 떠올려 보자. 모든 작업이 정상

동작하려면 로그인 폼에는 두 가지 주요 엘리먼트를 올바르게 설정해야 한다.

폼 액션은 ▶ UsernamePasswordAuthenticationFilter 서블릿 필터에서 설정한 액션과 일치해야

한다. 이 폼의 액션은 기본 설정상 j_spring_security_check로 설정돼 있다.

사용자명과 비밀번호를 나타내는 폼 필드는 서블릿 명세와 일치해야 한다. 폼 필드명의 기 ▶

본값은 각각 j_username와 j_password이다.

스프링 시큐리티 태그 라이브러리

스프링 시큐리티 태그 라이브러리는 표준 JSP 태그 라이브러리로서 도움이 되는 몇 가지 기능을

제공하며 다른 JSP 태그 라이브러리와 비슷하게 호출할 수 있다. 스프링 시큐리티 태그 라이브러

리의 상세 기능은 5장과 7장에서 사용하겠지만 여기서는 태그 라이브러리 소개 차원에서 간단한

기능을 추가해 보겠다.

헤더 JSP의 태그 라이브러리 레퍼런스 추가

WEB-INF/common/header.jsp에서 스프링 시큐리티 태그 라이브러에 대한 레퍼런스를 추가한다.

<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>

JSTL 태그 라이브러리와 마찬가지로 스프링 시큐리티 태그 라이브러리 레퍼런스도 태그 라이브러

리를 사용할 모든 페이지에 추가해야 한다.

현재 사용자명 표시

예제 사이트에서는 인증된 사용자가 사이트를 방문하면 헤더에서 로그인한 방문자를 환영하는

내용을 보여주려고 한다. 다행히 스프링 시큐리티 태그 라이브러리를 활용하면 이 기능을 쉽고 빠

르게 구현할 수 있다. 다음 코드를 WEB-INF/common/header.jsp에 추가하자.

<div id="header">

<div class="username">

Welcome, <strong><sec:authentication property="principal.username"/></strong>

</div>

<ul>

Page 79: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

03 사용자 경험 개선 l 51

이제 로그인하면 사용자에게 친근한 환영 메시지가 보일 것이다. <authentication> 태그는 뷰에

서 접근할 수 있도록 전체 Authentication 객체를 노출시킨다. 표준 자바빈 속성 구문을 사용하

면 이 객체의 모든 속성에 접근할 수 있다. 2장에서 배운 것처럼 Authentication에는 인증 후 주로

UserDetails를 반환하는 getPrincipal() 메서드가 있다는 사실을 기억하자. <authentication> 태그에

서 사용할 수 있는 다른 정보와 관련해서는 해당 인터페이스의 JavaDoc을 참고할 것을 권장한다.

JSP 페이지에서는 전체 페이지에 공통으로 들어가는 헤더 및 푸터도 포함한다(다운로드 가능

한 이 장의 예제 코드에는 헤더 1 와 푸터 인클루드 코드가 포함돼 있지만 현재 설명하고 있는 주제

와는 큰 관련이 없어서 이 부분에 대한 코드는 여기 싣지 않았다). 헤더와 푸터까지 연결하면 다음

처럼 간단한 JSP 페이지를 만들 수 있다.

<?xml version="1.0" encoding="ISO-8859-1" ?>

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"

pageEncoding="ISO-8859-1"%>

<jsp:include page="common/header.jsp">

<jsp:param name="pageTitle" value="Login"/>

</jsp:include>

<h1>Please Log In to Your Account</h1>

<p>

Please use the form below to log in to your account.

</p>

<form action="j_spring_security_check" method="post">

<label for="j_username">Login</label>:

<input id="j_username" name="j_username" size="20" maxlength="50"

type="text"/>

<br />

<label for="j_password">Password</label>:

<input id="j_password" name="j_password" size="20" maxlength="50"

type="password"/>

<br />

<input type="submit" value="Login"/>

</form>

<jsp:include page="common/footer.jsp"/>

여기서 폼에 POST를 사용해야 한다는 점을 주의하자. 이렇게 하지 않으면 UsernamePassword

AuthenticationFilter에 의해 로그인 요청이 거부된다.

1   (옮긴이) 3장 이후 예제 코드에서 사용하는 헤더에서는 jstl 태그 라이브러리를 사용하고 있는데 jstl 태그 라이브러리는 톰캣

6.x 버전에 기본으로 포함돼 있지 않다. 아쉽게도 저자가 제공하는 예제 소스에도 이 부분이 포함돼 있지 않으므로 독자들은

직접 태그 라이브러리를 WEB-INF/lib 폴더에 추가해야 한다. 이와 관련한 사항은 http://www.mularien.com/blog/2008/02/19/

tutorial-how-to-set-up-tomcat-6-to-work-with-jstl-12/를 참고하자. 아울러 위키북스에서 제공하는 예제 소스에서는 독자들

의 편의를 위해 appserv-jstl.jar, javaee.jar를 개별 웹 애플리케이션의 WEB-INF/lib 폴더에 함께 추가했다.

Page 80: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

52 l 스프링 시큐리티 3

마지막으로 스프링 시큐리티에서 새로운 로그인 페이지를 참조할 수 있도록 스프링 시큐리티

의 자동 설정을 수정하자. 지금까지 작업한 내용을 정리해 보면 지금까지 한 작업은 애플리케이

션에 새로운 페이지를 추가한 것이다. 현 시점에서 지금까지 작업한 내용을 확인하려면 http://

localhost:8080/JBCPPets/login.do 2 라는 주소를 입력하면 된다.

주소를 입력하고 어떤 일이 일어나는지 확인하자. 스프링 시큐리티에서 먼저 요청을 가로채고

(spring_security_login으로 리다이렉트된다) 여전히 기존 폼이 보일 것이다. 이렇게 되는 이유는

스프링 시큐리티에서 아직까지 DefaultLoginPageGeneratingFilter 클래스에서 생성하는 기본 로그

인 페이지를 참조하기 때문이다. 이 필터에서 생성하는 기본 로그인 페이지를 제거해야 비로소 새

로운 커스터마이징된 로그인 페이지를 만나게 된다. 이제 마지막으로 할 일은 기본 페이지를 제거

하고 우리 페이지를 로그인 페이지로 사용하는 일이다.

스프링 MVC 로그인 페이지를 사용하도록 스프링 시큐리티를 설정하기

얼핏 보면 이제 다음 예제 코드처럼 스프링 시큐리티 설정 파일에서 <form-login> 엘리먼트를 수정

해 login-page 디렉티브를 추가하는 일만 남은 것 같다.

<http auto-config="true" use-expressions="true">

<intercept-url pattern="/*" access="hasRole('ROLE_USER')"/>

<form-login login-page="/login.do" />

</http>

이렇게 수정하고 애플리케이션을 시작해 웹 브라우저의 주소창에서 홈페이지의 주소(http://

localhost:8080/JBCPPets/home.do)를 입력해 보자. 인터넷 익스플로러를 사용하는 독자라면 페이지

가 렌더링되지 않고 브라우저가 계속 로딩을 시도하는 것을 보게 될 것이다. 이번에는 파이어폭스

를 사용해 같은 URL을 입력해 보자. 파이어폭스를 사용하면 현재 일어나고 있는 일에 대해 더 많

은 정보를 확인할 수 있다.

2   (옮긴이) 앞의 헤더 추가로 인해 현재 소스에는 다소 문제가 있다. 바로 처음 로그인 시 AnonymousAuthenticationToken에서

principal.username이라는 잘못된 빈 속성을 참조하게 되면서 최초 로그인 시도가 실패하는 것이다. 이 문제를 해결하려면

header.jsp의 내용을

<c:if test="${principal.username}">

Welcome, <strong><sec:authentication property="principal.username"/></strong>

</c:if>

<c:if test="${!principal.username}">

Welcome, <strong><sec:authentication property="name"/></strong>

</c:if>

처럼 수정해야 한다. 위키북스 홈페이지에서 배포하는 예제 코드에서는 이 부분이 모두 수정됐다.

Page 81: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

03 사용자 경험 개선 l 53

문제는 URL 인터셉션과 관련해 설정한 규칙에 있다.

<intercept-url pattern="/*" access="hasRole('ROLE_USER')"/>

이렇게 설정할 경우 사용자가 URL과 매칭되는 페이지에 접근하려면 사용자가 ROLE_USER 역할

을 가지고 있어야 하기 때문이다.

/*는 새로 만든 로그인 페이지를 포함해 애플리케이션의 모든 페이지에 적용된다!

다음 다이어그램을 보면 현재 무슨 일이 일어나는지 알 수 있다.

사용자

/home.do를 요청

접근 결정 관리자

요청을 가로챔

접근 거부 - /login.do로 리다이렉트됨

접근 거부 - /login.do로 리다이렉트됨

사용자가 보호된 리소스에 접근할 권한을

가지고 있는지 검증

시스템 리소스 권한부여 선언

홈 페이지

커스텀 로그인 페이지

요청을 가로챔

이 문제를 해결하려면 익명 사용자들도 로그인 페이지에 접근할 수 있게 권한부여 규칙을 수정

해야 한다.

Page 82: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

54 l 스프링 시큐리티 3

알아두세요! 스프링 시큐리티는 모든 URL 요청에 대해 위에서 아래로 내려오는(top to

bottom) 순서대로 권한부여 규칙을 해석해 적용한다. 따라서 사용자에게 적용되는 규칙은

URL 패턴과 가장 먼저 매칭되는 권한부여 규칙이다. 일반적으로 이 말은 권한부여 규칙이

가장 구체적인 규칙부터 가장 구체적이지 않은 규칙 순으로 적용된다는 의미다. 복잡한 규

칙을 개발할 때는 이 사실을 항상 기억하는 게 좋다. 종종 복잡한 권한 규칙을 개발하다 보

면 어떤 권한 규칙이 실제로 적용되는지 가끔은 혼동스럽기 때문이다. 그냥 위에서 아래로

내려오는 순서대로 권한 규칙이 적용된다는 사실만 항상 기억하자. 그러면 각 시나리오에

서 어떤 권한 규칙이 적용될지 쉽게 파악할 수 있다.

!

여기서는 좀더 구체적인 권한 규칙을 추가하고 있으므로 우리가 추가할 권한 규칙은 권한 규칙

목록의 상단에 두어야 한다. 이렇게 수정하면 전체 권한 규칙 목록이 다음과 같이 설정된다.

<intercept-url pattern="/login.do" access="permitAll"/>

<intercept-url pattern="/*" access="hasRole('ROLE_USER')"/>

이렇게 수정하고 나면 모든 사용자가 로그인 페이지에 접근할 수 있으면서 사이트의 나머지 영

역에는 인증된 사용자만 접근할 수 있게 되므로 우리가 의도한 대로 권한 규칙이 적용된다. 3 로그

인 관련한 내용은 이것으로 모두 마쳤다. 이어서 로그아웃 기능을 추가하기 위해 해야 할 일을 살

펴보자.

로그아웃 기능의 이해

로그아웃은 사용자의 안전한 세션을 무효화하는 사용자의 행동을 나타내는 용어다. 일반적으로

로그아웃할 경우 사용자는 사이트의 보호되지 않은 영역으로 리다이렉트된다. 그럼 사이트 헤더

에 Log Out 링크를 추가하고 사이트를 다시 열어서 로그아웃이 어떻게 동작하는지 확인해 보자.

사이트 헤더의 Log Out 링크 추가

2장에서 본 것처럼 스프링 시큐리티에는 필터 체인에 속하는 하나 이상의 서블릿 필터에서 특정

3   (옮긴이) 여기서는 /login.do URL과 해당 URL에 대해 permitAll이라는 권한부여 규칙을 지정하고 있다. permitAll은 (익명

사용자를 포함한) 모든 사용자의 접근을 허용하는 것이므로 구체적이지 않은 일반적인 선언으로 생각할 수도 있겠지만 이처럼

‘특정 URL과 관련해 모두 허용’이라는 의미로 사용될 때는 일반적인 권한부여 규칙도 구체적인 규칙이 된다. 엄밀히 말해 구체

적인 규칙인지 아닌지 여부는 다분히 비즈니스적 요구와 권한부여 규칙 우선순위에 의존할 뿐, permitAll이 항상 일반적인 규

칙이 되는 것은 아니다. 아울러 권한부여 규칙은 이처럼 URL 구체성, 역할 구체성의 두 특징을 조합해 지정한다.

Page 83: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

03 사용자 경험 개선 l 55

기능을 수행하기 위해 감시 포인트로 사용하는 특수 URL이 있다. 사용자 로그아웃 관련 URL

은 /j_spring_security_logout이다. 로그아웃 링크를 추가하는 일 자체는 header.jsp 파일에 적절한

href를 사용해 앵커 태그를 놓으면 간단히 해결된다.

<c:url value="/j_spring_security_logout" var="logoutUrl"/>

<li><a href="${logoutUrl}">Log Out</a></li>

이제 사이트를 다시 열어서 Log Out 링크를 클릭하면 로그인 폼으로 돌아올 것이다. 로그인과

로그아웃을 반복해 보면서 재미를 느껴 보자!

잠깐만요! 상대 경로 URL 처리를 위해 JSTL URL 태그 사용하기

예제에서는 배포한 웹 애플리케이션에서 우리가 지정한 URL을 제대로 해석할 수 있도록

JSTL 코어 라이브러리의 url 태그를 사용한다. url 태그는 지정한 URL을 웹 애플리케이

션의 루트에 대한 (/로 시작하는) 상대 URL로 해석한다. JSP 표현식 코드를 사용하는 다

른 기법(<%= request.getContextPath() %>)도 많이 봤겠지만 JSTL url 태그를 사용하면

인라인 코드를 사용하지 않아도 된다는 장점이 있다!

이어서 지금처럼 간단해 보이는 작업 뒤에서 실제로 어떤 일들이 일어나는지 살펴보자.

로그아웃 동작 원리

Log Out 링크를 클릭할 때 실제로 어떤 일이 일어날까?

모든 URL 요청은 서블릿 요청으로 리졸브(resolve)되기 전에 항상 스프링 시큐리티의 전체 필터

체인을 통과한다는 사실을 기억하자. 따라서 /j_spring_security_logout에 대한 URL 요청이 시스

템에 있는 JSP 페이지와 일치하지 않더라도 이 요청을 처리하기 위해 실제 JSP나 스프링 MVC 대

상 URL을 두지 않아도 된다. 이러한 종류의 URL은 흔히 가상 URL이라고 부른다.

/j_spring_security_logout에 대한 URL 요청은 o.s.s.web.authentication.logout.LogoutFilter가

가로챈다. 기본 스프링 시큐리티 필터 체인에 속하는 많은 필터 중 하나인 LogoutFilter는 특정 가

상 URL을 감시하고 행동을 취한다.

기본 로그아웃 기능과 관련해 security 네임스페이스에서 제공하는 설정을 잠시 확인해 보자.

<http auto-config="true" use-expressions="true">

<logout invalidate-session="true"

Page 84: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

56 l 스프링 시큐리티 3

logout-success-url="/"

logout-url="/j_spring_security_logout"/>

</http>

이 기본 설정에서는 logout-url 어트리뷰트에 지정한 로그아웃 URL을 감시하고 사용자를 로그

아웃시킨다. 사용자를 로그아웃시키는 과정은 다음 3단계로 이뤄진다.

HTTP 세션 무효화(11 invalidate-session이 true로 설정된 경우).

SecurityContext21 초기화(사용자를 실제로 로그아웃시키는 부분).

logout-success-url31 에 지정된 URL로의 사용자 리다이렉트

다음 다이어그램을 보면 로그아웃 과정이 동작하는 원리를 쉽게 이해할 수 있다.

이 동작은 구현 방식에 따라 달라질 수 있음

사용자

로그아웃 요청을 수행

요청을 가로챔

순환문을 사용해 모든 LogoutHandler를 검사

로그아웃 성공 URL이 정의되었는가?

로그아웃 성공 URL로 리다이렉트

아니오

기본 URL("/")로 리다이렉트

로그아웃 메서드를 호출한다

세션 초기화, remember me 쿠키 초기화, SecurityContext 초기화

o.s.s.web.authentication.logout.LogoutHandler는 LogoutFilter가 사용자를 로그아웃시킬 때

호출하는 구현체 클래스에서 구현하는 인터페이스다. (복잡하기는 하지만) LogoutFilter 라이

프 사이클과 연계된 커스텀 LogoutHandler를 직접 구현할 수도 있다. LogoutFilter에 설정한 기

본 LogoutHandler는 세션을 초기화하고 remember me 기능을 초기화해 사용자의 세션에서 더

는 사용자 관련 인증이 남지 않게 한다. 마지막으로 로그아웃 후 일어나는 URL 리다이렉션은

o.s.s.web.authentication.logout.LogoutSuccessHandler 인터페이스의 기본 구현체가 담당한다. 이 기

본 구현체에서는 설정된 로그아웃 성공 URL(기본값은 /으로 설정돼 있다)로 URL을 리다이렉트

Page 85: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

03 사용자 경험 개선 l 57

하는 단순한 기능을 처리하는데, 이 구현체 클래스를 수정하면 사용자가 로그아웃한 후 애플리

케이션에서 처리할 작업들을 수행하는 것도 가능하다. 이때 한 가지 주의할 점은 사용자의 보호

된 세션에서 서로 일관되지 않은 인증 오류가 발생하는 것을 막으려면 모든 로그아웃 핸들러가 정

상적으로 실행돼야 하므로 로그아웃 핸들러에서는 예외를 던져서는 안 된다는 점이다. 따라서 커

스텀 로그아웃 핸들러를 구현할 때는 이와 같이 발생하는 예외들을 적절히 처리하고 예외에 대한

로그를 남기는 것이 좋다.

로그아웃 URL 변경하기

이번에는 기본 로그아웃 URL을 오버라이드해서 기본 동작을 수정하는 간단한 예제를 만들어 보

자. 이번에는 로그아웃 URL을 /logout으로 수정해 보겠다.

다음 코드처럼 <logout> 엘리먼트를 포함하도록 dogstore-security.xml 파일을 수정하자.

<http auto-config="true" use-expressions="true">

...

..<logout invalidate-session="true"

logout-success-url="/"

logout-url="/logout"/>

</http>

로그아웃 링크의 href 어트리뷰트가 새로운 URL과 일치하게끔 /common/header.jsp 파일도 다

음과 같이 수정하자.

<c:url value="/logout" var="logoutUrl"/>

<li><a href="${logoutUrl}">Log Out</a></li>

이제 애플리케이션을 재시작하고 로그아웃해 보자! 이번에는 /j_spring_security_logout 대신

/logout URL이 사용자를 로그아웃시킬 때 사용되는 것을 볼 수 있을 것이다. 아울러 /j_spring_

security_logout을 주소창에 치면 이 URL이 실제 서블릿 리소스와 일치하는 부분이 없고 이제

요청 필터에서도 이 URL을 처리하지 않아서 Page not Found (404) 에러가 발생하는 것을 볼 수

있다.

로그아웃 설정 디렉티브

<logout> 엘리먼트에서 추가 설정을 사용하면 다음 표에서 설명하는 좀더 정교한 로그아웃 기능

을 사용할 수 있다.

Page 86: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

58 l 스프링 시큐리티 3

어트리뷰트 설명

invalidate-session true로 설정되면 사용자의 HTTP 세션이 사용자가 로그아웃할 때 무효화된다. 일부

경우(예를 들어 사용자의 쇼핑 카트 관리)에는 이를 사용하지 않는 것이 좋다.

logout-success-url 사용자 로그아웃 시 사용자가 리다이렉트될 URL. 기본값은 /이다. 이 기능은

HttpServletResponse.redirect로 처리한다.

logout-url LogoutFilter가 읽는 URL(예제에서는 이 부분을 수정했다).

success-handler-ref LogoutSuccessHandler 구현체에 대한 빈 레퍼런스

remember me

웹 사이트를 자주 방문하는 사용자에게 편리한 기능 중 하나는 rememeber me 기능이다. 이 기능

은 간단한 쿠키를 암호화하고 사용자 브라우저에 저장해 재방문하는 사용자를 기억하는 기능이

다. 스프링 시큐리티에서 사용자가 remember me 쿠키를 갖고 있는 것을 인식하면 사용자는 사용

자명이나 비밀번호를 입력하지 않고도 애플리케이션에 자동 로그인할 수 있다.

지금까지 살펴본 다른 기능과 달리 remember me 기능은 security 네임스페이스를 사용하더라

도 자동으로 설정되지 않는다. 4 이 절에서는 remember me 기능을 사용해 보고 이 기능이 로그인

사용자 경험에 어떤 영향을 미치는지 살펴보겠다.

remember me 옵션 구현

이 예제를 마치고 나면 간단한 사용자 인식 기능을 온라인 상점 사이트에 추가할 수 있을 것이다.

dogstore-security.xml 설정 파일을 다음과 같이 수정해 <remember-me> 선언을 추가하자. key 어트

리뷰트를 jbcpPetStore로 설정한다.

<http auto-config="true" use-expressions="true"

access-decisionmanager-ref="affirmativeBased">

<remember-me key="jbcpPetStore"/>

<logout invalidate-session="true" logout-success-url="/" logouturl="/logout"/>

</http>

이 상태에서 애플리케이션을 테스트해 보더라도 아직까지 달라진 점은 못 느낄 것이다. 달라진

4   (옮긴이) 이 부분은 스프링 시큐리티 2와 스프링 시큐리티 3의 주요 차이점 중 하나다. 스프링 시큐리티 2와는 달리 스프링 시

큐리티 3에서는 remember me 기능이 기본 설정되지 않는다. 마이그레이션과 관련한 내용은 13장을 참고하자.

Page 87: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

03 사용자 경험 개선 l 59

점을 못 느끼는 이유는 사용자가 로그인 기능을 선택적으로 사용할 수 있도록 로그인 폼에 필드

를 추가하는 작업을 아직 안 했기 때문이다. 이어서 login.jsp 파일을 수정해 다음과 같이 체크박

스를 추가하자.

<input id="j_username" name="j_username" size="20" maxlength="50" type="text"/>

<br />

<input id="_spring_security_remember_me" name="_spring_security_remember_me"

type="checkbox" value="true"/>

<label for="_spring_security_remember_me">Remember Me?</label>

<br />

<label for="j_password">Password</label>

이제 Remember Me 체크박스를 체크하고 다시 로그인하면 remember me 쿠기가 사용자의 브

라우저에 설정된다.

이제 사용하던 브라우저를 닫고 JBCP Pets 웹 사이트의 인증된 페이지를 다시 열면 사용자는

로그인 페이지를 다시 거치지 않아도 된다. 이 기능을 직접 확인해 보자. Remember Me 옵션이 체

크된 상태에서 로그인하고 홈페이지를 즐겨찾기에 추가한 다음 브라우저를 다시 시작해 홈페이지

에 접근해 보자. 이제 사용자 크리덴셜을 입력하지 않고도 바로 로그인에 성공하는 것을 볼 수 있

을 것이다.

이 기능을 좀더 쉽게 테스트하려면 세션 쿠키를 관리(제거)해 주는 Firecookie(http://www.

softwareishard.com/blog/firecookie/) 같은 브라우저 플러그인을 사용하는 게 좋다. 이러한 플러그

인을 사용하면 사이트 개발 시 관련 기능의 개발과 검증을 더 쉽게 테스트할 수 있다.

remember me 기능의 동작 원리

remember me 기능은 다음 정보들이 들어 있는 Base64 인코딩된 문자열을 포함한 쿠키를 사용자

의 브라우저에 설정한다.

사용자의 사용자명 ▶

쿠키 만료 일자/시간 ▶

쿠키 만료 일자/시간, 사용자명, 비밀번호에 대한 MD5 해시 ▶

<remember-me> ▶ 엘리먼트의 key 어트리뷰트에 정의된 애플리케이션 키

이러한 정보 조각들은 하나의 쿠키를 형성해 향후 사용할 수 있도록 사용자 브라우저에 저장

된다.

Page 88: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

60 l 스프링 시큐리티 3

MD5는 유명한 암호화 해시 알고리즘 중 하나다. 암호화 해시 알고리즘은 임의의 길이를 갖는 입

력 데이터를 다이제스트라고 하는 압축된 고유 텍스트로 변환한다. 이후 다이제스트는 원본 입력

데이터가 없더라도 해시를 생성하는 데 사용한 입력값과 정확히 일치하는 알려지지 않은 입력값을

확인하는 데 사용된다. 다음 다이어그램은 이 알고리즘이 동작하는 원리를 보여주는 그림이다.

Spring Security isgreat!

연산 MD5 해시 알고리즘

저장된 MD5 해시를 사용

f3e5254fc72b1a944fd0c03f868f5ea2

알려지지 않은 입력값

알려지지 않은 입력값의 MD5 해시를 계산

알려지지 않은 입력값이 저장된 MD5 해시와

일치하는가?알려진 값과 불일치아니오

알려지지 않은 입력값 == "Spring Security…"

이 경우 사용자 로그인 등의 결과로 이어짐

이 그림에서 볼 수 있듯이 알려지지 않은 입력값은 저장된 MD5 해시와의 비교 검증을 거쳐 알

려진 입력값과 일치하는지 여부가 판단된다. 다이제스트와 암호화 알고리즘 사이의 주요 차이점

은 다이제스트 값은 원본 데이터를 리버스 엔지니어링하기가 매우 어렵다는 점이다. 리버스 엔지

니어링이 어려운 이유는 다이제스트는 (크기가 매우 클 수도 있는) 전체 데이터가 아니라 원본 데

이터에 대한 요약 정보 또는 암호화 지문(�ngerprint)만을 제공하기 때문이다.

암호화된 데이터를 디코딩하는 게 불가능하기는 하지만 MD5는 알고리즘 자체의 취약점을 이

용한 공격이나 레인보우 테이블 공격 같은 일부 공격에 취약하다. 레인보우 테이블에는 보통 수

백 만 개의 입력값에 대한 계산 전 해시값이 들어 있다. 레인보우 테이블을 사용하면 해커들은

레인보우 테이블에서 해시값을 검색해 (해시되지 않은) 실제값을 판단할 수 있다. 이러한 공격에

대처하는 방법은 비밀번호 보안을 다루는 4장, ‘크리덴셜 안전하게 저장하기’에서 자세히 살펴보

겠다.

remember me 쿠키의 구성 요소를 보면 해커가 해킹된 쿠키를 생성하기 어려울 정도로 쿠키의

구성 요소가 복잡하다는 사실을 알 수 있다. 4장에서는 악의적인 사이트 공격자로부터 remember

me 기능 자체의 보안을 더 강화하는 추가 기술에 대해서도 설명한다.

Page 89: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

03 사용자 경험 개선 l 61

쿠키는 설정이 가능한 만료 일정에 따라 만료된다. 쿠키가 만료되기 전에 사용자가 사이트를 재

방문하면 쿠키는 애플리케이션에서 설정한 다른 쿠키와 함께 애플리케이션에 전달된다.

remember me 쿠키의 경우 <remember-me> 설정 디렉티브에서 필터 체인에 삽입하는 o.s.s.web.

authentication.rememberme.RememberMeAuthenticationFilter가 쿠키의 내용을 검토하고 올바른

remember me 쿠키에 해당할 때만 (이 과정이 왜 필요한지에 대해서는 64쪽의 ‘remember me 기

능은 과연 안전할까?’ 절을 참고하자) 사용자 인증에 remember me 쿠키를 사용한다.

다음 다이어그램에서는 remember me 쿠키의 검증 절차에 사용되는 다양한 컴포넌트를 보여

준다.

사용자

요청을 가로챔

요청을 검사

구현

쿠키를 추출

쿠키가 있는가?

예 예

아니오

아니오

아니오

아니오

쿠키가 제대로 디코딩되는가?

예외를 던짐

기대한 쿠키 MD5 시그너처를 계산

시그너처가 매칭되는가?

사용자 계정이 올바른가?

InvalidCookieException 예외를 던짐

요청

인증 토큰을 생성

사용자가 인증된 경우

필터 체인상의

다음 필터

RememberMeAuthenticationFilter는 SecurityContextHolderAwareRequestFilter 바로 다음, Anonymous

ProcessingFilter 바로 전 위치에서 필터 체인에 삽입된다. 필터 체인에 속하는 다른 필터가 하는 것

처럼 RememberMeAuthenticationFilter 필터도 요청을 검사하고 필요하다면 행동을 취한다.

다이어그램에서 볼 수 있는 것처럼 이 필터는 사용자가 요청의 일부로 remember me 쿠키를 제

Page 90: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

62 l 스프링 시큐리티 3

공했는지 여부를 검사하는 일을 한다. remember me 쿠키를 찾으면 쿠키는 Base64 디코딩되고

쿠키에 들어 있는 사용자명과 비밀번호를 바탕으로 MD5 해시값 연산이 이뤄진다. 쿠키가 이러한

검증을 모두 통과하면 사용자는 비로소 로그인된다.

알아두세요! 아마도 사용자명이 바뀌거나 비밀번호가 달라지면 remember me 토큰을 더

는 사용할 수 없다는 사실을 쉽게 짐작할 수 있을 것이다. 이처럼 사용자 계정 정보를 수정

가능하도록 둘 때는 적절한 메시지를 사용자에게 반드시 제공하는 게 좋다. 4장에서는 사

용자명에만 의존하고 비밀번호를 사용하지 않는 remember me 기능을 대체할 또 다른 방

법에 대해 설명한다.

!

앞에서는 RememberMeAuthenticationFilter가 쿠키를 검증할 때 o.s.s.web.authentication.

RememberMeServices 구현체에 의존한다는 사실을 볼 수 있다. 여기 사용되는 이 구현체 클래스는

폼 매개변수로 _spring_security_remember_me라는 이름을 사용한 성공적인 폼 기반 로그인 시에도

사용된다. 이때 쿠키는 앞에서 설명한 정보를 안전하게 인코딩해 브라우저에 Base64 인코딩 방식

으로 저장되고 타임스탬프와 사용자의 비밀번호를 갖고 있는 MD5 해시를 포함하게 된다.

한 가지 기억할 점은 remember me 쿠키를 사용해 인증받은 사용자와 사용자명/비밀번호(또는

이와 동일한 정보) 크리덴셜을 제공해 인증받은 사용자를 서로 구분할 수 있다는 점이다. 이 부분

은 remember me 기능의 보안성을 검토하면서 잠시 후 살펴보겠다.

remember me와 사용자 라이프사이클

RememberMeServices의 구현체는 사용자 라이프사이클(인증된 사용자의 세션 라이프사이클)이 진

행되는 동안 몇몇 위치에서 호출된다. remember me 기능을 좀더 명확히 이해하려면 어떤 시점에

서 RememberMeServices 구현체가 세션 라이프사이클에 대한 정보를 받는지 알아두는 게 좋다.

사용자 행동 일어나는 일

로그인 성공 구현체 클래스가 (폼 매개변수가 전송된 경우) remember me 쿠키를 설정한다.

로그인 실패 쿠키가 존재한다면 구현체 클래스가 쿠키를 무효화한다.

사용자 로그아웃 쿠키가 존재한다면 구현체 클래스가 쿠키를 무효화한다.

커스텀 인증 핸들러를 생성할 때도 어느 위치에서 또 어떻게 RememberMeServices 인터페이스가

사용자 라이프사이클과 연계되는지 이해하는 게 중요하다. 커스텀 인증 핸들러를 유용하고 안전하

게 사용하려면 인증 프로세서에서 RememberMeServices와 항상 일관되게 연계해야 하기 때문이다.

Page 91: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

03 사용자 경험 개선 l 63

remember me 설정 디렉티브

기본 remember me 기능을 수정할 때는 두 가지 설정 방식이 주로 사용된다.

어트리뷰트 설명

key 애플리케이션과 관련한 remember me 쿠키에 대한 고유 키를 정의한다.

token-validity-seconds 시간 범위를 (초 단위로) 정의한다. remember me 기능은 여기서 설정한 시간

동안 유효하다.

이 어트리뷰트는 쿠키 만료 타임스탬프를 설정하는 용도로도 사용된다.

앞에서 쿠키의 내용이 어떻게 해시되는지 설명한 내용을 보면 key 어트리뷰트가 remember me

기능의 보안에서 가장 핵심적인 역할을 한다는 사실을 알 수 있다. 이러한 key의 설정값으로는 애

플리케이션과 관련한 고유값을 사용하고 쉽게 추측할 수 없을 정도로 긴 문자열을 사용하는 게

좋다.

단순 참고용으로 여기서는 key 값을 비교적 간단하게 설정했다. 하지만 애플리케이션에서 실

제 remember me 기능을 사용할 때는 key에 애플리케이션의 고유 이름을 포함한 문자열을 사용

하고 적어도 36자 길이의 임의 문자열을 사용하는 게 좋다. 비밀번호 생성 툴(구글에서 ‘online

password generator’로 검색해 보자)을 사용하면 remember me의 key로 사용하기에 적합한 알파

벳, 숫자, 특수문자를 임의로 조합한 키를 생성할 수 있다.

여러 환경(개발, 테스트, 운영 환경 등)에서 사용하는 애플리케이션이라면 remember me 쿠키

값에 이러한 환경 정보도 포함하는 게 좋다. 이렇게 하면 테스트 기간 동안 혹시라도 잘못된 환경

에서 remember me 쿠키가 사용되는 것을 막을 수 있다!

운영 애플리케이션에서 사용하기에 적합한 키값을 예로 들면 다음과 같다.

jbcpPets-rmkey-paLLwApsifs24THosE62scabWow78PEaCh99Jus

token-validity-seconds 어트리뷰트는 토큰 무효화 시점을 초 단위로 지정하는 어트리뷰트다. 이

어트리뷰트를 사용하면 유효 시간을 제외한 다른 모든 유효성에 문제가 없는 remember me 토큰

을 자동 로그인 기능에서 더는 받아들이지 않는 시점을 초로 지정할 수 있다. 5 이 어트리뷰트는 또

사용자 브라우저에서 로그인 쿠키의 최대 유효 시간을 설정하는 용도로도 사용된다.

5   (옮긴이) ‘유효 시간을 제외한 다른 모든 유효성에 문제가 없는’이라는 말은 ‘유효 시간이 만료되지 않았다고 가정할 경우 완전

히 유효한’이라는 의미다. 따라서 이 어트리뷰트는 유효 시간을 제외한 모든 유효성을 만족시키는 토큰에 대해서만 무효화 시

점을 적용하는 어트리뷰트라 할 수 있다.

Page 92: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

64 l 스프링 시큐리티 3

잠깐만요! remember me 세션 쿠키의 설정

token-validity-seconds를 -1로 설정하면 로그인 쿠키는 사용자가 브라우저를 닫을 때

함께 사라지는 세션 쿠키에 설정된다. 이때 토큰은 (사용자가 브라우저를 닫지 않으면) 별

도 설정이 불가능한 2주 기간 동안 유효하다. 이 쿠키를 세션 ID를 저장하는 쿠키와 혼동

해서는 안 된다. 이 두 쿠키는 이름은 비슷하지만 엄연히 서로 다른 쿠키다!

remember me 기능의 고급 설정과 관련한 다른 설정 디렉티브도 있다. 이 중 일부는 이어지는

예제를 통해 살펴보고 나머지 디렉티브는 고급 권한부여 기능을 다루는 6장, ‘고급 설정과 확장’에

서 설명하겠다.

remember me 기능은 과연 안전할까?

사용자의 편의를 위해 제공하는 모든 보안 관련 기능은 신중한 설계 고민 끝에 보안을 적용한 사

이트를 보안 위협에 노출시킬 수 있는 위험성을 항상 안고 있다. remember me 기능도 기본 형태

로 사용하면 사용자의 쿠키를 중간에 가로채 악의적인 사용자가 재사용할 수 있다는 취약점이 있

다. 다음 다이어그램은 이러한 보안 위협이 어떻게 일어날 수 있는지 보여준다.

사용자

remember me 쿠키를 사용해 페이지를 요청

네트워크 스니퍼(sniffer)를 사용해 사용자 요청을 기록

악의적인 사용자

사이트 페이지

사용자 요청을 그대로 재현하고 remember me 쿠키를

통해 인증에 성공

SSL(4장에서 설명한다)을 사용하고 다른 네트워크 보안 기술을 함께 사용한다면 이러한 형태

의 공격을 저지할 수 있지만 remember me 기능을 사용하는 사용자의 세션을 훔치거나 이를 악

용하는 데에는 크로스 사이트 스크립팅(XSS)과 같은 공격도 사용될 수 있다는 점을 참고하자. 사

용자에게 편리한 기능이기는 하지만 remember me 기능을 사용한 세션을 해커가 악용해 사용자

의 금융 정보나 기타 개인 정보가 변경되거나 도난당하는 일을 겪고 싶지는 않을 것이다.

Page 93: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

03 사용자 경험 개선 l 65

알아두세요! 이 책에서는 악의적 사용자의 행동에 대해 자세히 다루고 있지 않지만 보안 시

스템을 구현할 때는 고객이나 임직원들의 정보를 해킹하려는 악의적 사용자들이 사용하

는 기술을 이해할 필요가 있다. XSS도 이러한 기술 중 하나이고 이 외에도 다양한 공격 기

술이 있다. OWASP TopTen(http://www.owasp.org/index.php/Category:OWASP_Top_Ten_

Project)을 참고해 가장 자주 사용되는 기본적인 공격 방식들의 목록을 먼저 파악하고 목

차에 기록된 다양한 공격 방식을 설명하고 있는 웹 애플리케이션 보안 관련 서적을 참고할

것을 적극 권장한다.

!

사용자 편의와 보안 사이에서 균형점을 찾기 위해 주로 사용하는 방식은 사이트상에서 개인 정

보나 민감한 정보가 존재하는 기능 영역을 별도로 분류하는 것이다. 이러한 사이트 영역에서는 항

상 사용자의 역할뿐 아니라 사용자가 사용자명과 비밀번호를 사용해 완전히 인증된 사용자인지

여부까지 확인하는 권한부여 규칙을 통해 사이트를 보호하는 게 좋다. 이러한 기능은 2장에서 살

펴본 권한부여 규칙에 SpEL 표현식이 지원하는 fullyAuthenticated 의사 속성을 사용해 적용할 수

있다.

remember me 사용자와 완전히 인증된 세션을 구별하는 권한부여 규칙

고급 권한부여 기술에 대해서는 5장, ‘미세 접근 제어’에서 자세히 보겠지만 인증된 세션이

remember me 기능을 사용했는지 여부를 바탕으로 접근 규칙을 차별화할 수 있다는 사실은 꼭

알아두자.

예를 들어 사용자가 remember me 세션을 통해 사이트에 접근해 ‘구매하고 싶은 목록’을 조회

하고 수정한다고 가정해 보자. 이는 소비자를 대상으로 하는 상업 사이트에서 주로 제공하는 기능

으로 사용자 개인 정보나 금융 정보를 노출할 위험 요소가 없다(사이트마다 성격이 다르므로 이

규칙을 안전한 사이트에 맹목적으로 적용해서는 안된다). 그에 반해 사용자 계정과 주문 기능은

중점적으로 보호해야 한다. 이를 위해서는 사용자가 계정 정보에 접근하려고 하거나 주문 결제를

할 때 remember me 세션을 사용한 사용자의 경우 자신을 인증하도록 강제해야 한다. 이러한 권

한부여 규칙을 설정하려면 다음과 같이 하면 된다.

<intercept-url pattern="/login.do" access="permitAll"/>

<intercept-url pattern="/account/*.do"

access="hasRole('ROLE_USER') and fullyAuthenticated"/>

<intercept-url pattern="/*" access="hasRole('ROLE_USER')"/>

여기서는 로그인 페이지와 ROLE_USER에 대한 기존 규칙은 그대로 남아 있음을 볼 수 있다. 이 설

Page 94: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

66 l 스프링 시큐리티 3

정에서는 계정 정보에 대한 요청에 대해 적절한 ROLE_USER의 GrantedAuthority를 갖도록 지정하고

사용자명과 비밀번호 또는 기타 합당한 크리덴셜을 제공해 완전한 인증을 거쳐서 사용자가 인증

세션을 통과하도록 규칙을 추가했다. 여기서 SpEL에서 논리 연산자로 사용하는 and, or, not과 같

은 연산자를 눈여겨보자. SpEL을 설계한 사람들은 XML에서 &&와 같은 연산자를 사용할 경우

XML 구문이 이상하게 보일 것을 우려해 이와 같은 연산자 표기법을 고안해 냈다.

이처럼 설정을 수정하고 remember me 기능을 사용해 로그인한 후 방금 보안을 적용한 My

Account 링크에 접근하면, 403 Access Forbidden 메시지가 나타난다. 이런 메시지가 나오는 이유

는 애플리케이션이 아직까지 AccessDeniedException 예외를 처리하는 기본 AccessDeniedHandler를

사용하도록 설정됐기 때문이다. 이러한 기본 동작의 커스터마이징은 AccessDeniedException 예외

가 처리되는 방법을 다루는 6장에서 살펴보겠다.

잠깐만요! 표현식을 사용하지 않고 완전한 인증 체크하기

애플리케이션에서 접근 선언으로 SpEL 표현식을 사용하지 않더라도 IS_AUTHENTICATED_

FULLY 접근 규칙(예를 들어 access="IS_AUTHENTICATED_FULLY")을 사용하면 사용자가 완전

한 인증을 거친 사용자인지 체크할 수 있다. 하지만 그렇다 하더라도 표준 역할 접근 선언은

SpEL처럼 표현 범위가 넓지 않으므로 복잡한 불리언 표현식을 처리하기는 어렵다.

에러 처리가 필요하기는 하지만 이러한 접근 방식은 remember me 기능이 제공하는 편리한 사

용자 경험을 그대로 활용하면서 사용자가 민감한 정보에 접근하려고 하면 완전한 크리덴셜을 사

용자에게 요구해 추가적인 보안 수준도 만족시켜 준다.

IP 주소를 인식하는 remember me 서비스 개발하기

remember me 기능을 더 안전하게 만드는 방법 중 하나는 사용자의 IP 주소를 쿠키의 내용과 연

계하는 방법이다. 예제를 통해 이러한 기능을 제공하는 커스텀 RememberMeServices 구현체를 어떻

게 개발할 수 있는지 알아보자.

이러한 구현체를 만들려면 o.s.s.web.authentication.rememberme.TokenBasedRememberMeServices 기

저 클래스를 상속하고 요청자의 IP 주소를 쿠키 자체와 나머지 remember me 요소의 MD5 해시

에도 함께 추가하도록 클래스를 확장해야 한다.

기저 클래스를 상속하려면 두 개의 주요 메서드를 오버라이드하고 간단한 헬퍼 메서드를 오버

라이드하거나 새로 구현해야 한다. 또 이 과정에서는 (사용자의 IP 주소를 가져오는 데 사용할)

Page 95: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

03 사용자 경험 개선 l 67

HttpServletRequest를 ThreadLocal에 임시 저장해야 한다. 이렇게 하는 이유는 일부 메서드가 매개

변수로 HttpServletRequest를 사용하지 않기 때문이다.

TokenBasedRememberMeServices 상속하기

먼저 TokenBasedRememberMeServices 클래스를 상속하고 부모 클래스의 특정 행동을 오버라이드해

보자. 물론 여기서 상속하는 부모 클래스는 오버라이드하기 쉽게 설계됐다. 하지만 프로그램의 흐

름상 일부 코드가 중복되는 건 바람직하지 않으므로 조금 이상해 보이더라도 여기서는 이 클래스

를 가능한 한 간결하게 만들어 보겠다. 다음과 같이 com.packtpub.springsecurity.security 페키지에

클래스를 생성하자.

public class IPTokenBasedRememberMeServices extends TokenBasedRememberMeServices {

ThreadLocal HttpServletRequest를 설정하고 가져오도록 다음과 같은 간단한 유틸리티 메서드들

을 사용한다.

private static final ThreadLocal<HttpServletRequest> requestHolder =

new ThreadLocal<HttpServletRequest>();

public HttpServletRequest getContext() {

return requestHolder.get();

}

public void setContext(HttpServletRequest context) {

requestHolder.set(context);

}

또 HttpServletRequest로부터 IP 주소를 가져오도록 아래 유틸리티 메서드도 추가한다.

protected String getUserIPAddress(HttpServletRequest request) {

return request.getRemoteAddr();

}

제일 먼저 오버라이드할 메서드는 remember me 프로세서를 위해 쿠키값을 설정하는 데 사용

할 onLoginSuccess 메서드다. 이 메서드에서는 ThreadLocal을 설정하고 작업이 끝난 후 이를 비워줄

필요가 있다. 여기서는 (사용자의 인증된 요청과 관련한 모든 정보를 취합하고 이를 쿠키에 종합

적으로 포함시키는) 부모 메서드의 흐름을 염두에 두자.

@Override

public void onLoginSuccess(HttpServletRequest request, HttpServletResponse response,

Authentication successfulAuthentication) {

try {

Page 96: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

68 l 스프링 시큐리티 3

setContext(request);

super.onLoginSuccess(request, response, successfulAuthentication);

} finally {

setContext(null);

}

}

부모 클래스의 onLoginSuccess 메서드는 인증 크리덴셜의 MD5 해시를 생성하는 데 사용되는

makeTokenSignature 메서드를 호출한다. 요청으로부터 IP 주소를 받아오고 스프링 프레임워크에서

제공하는 유틸리티 클래스를 사용해 반환된 쿠키를 인코딩하도록 이 메서드를 오버라이드하자.

@Override

protected String makeTokenSignature(long tokenExpiryTime,

String username,

String password) {

return DigestUtils.md5DigestAsHex((username + ":" + tokenExpiryTime + ":"

+ password + ":" + getKey() + ":"

+ getUserIPAddress(getContext())).getBytes());

}

마찬가지로 요청 IP 주소를 포함한 추가 인코딩 정보를 포함하도록 setCookie 메서드를 오버라

이드한다.

@Override

protected void setCookie(String[] tokens,

int maxAge,

HttpServletRequest request,

HttpServletResponse response) {

// 쿠키에 IP 주소를 추가한다.

String[] tokensWithIPAddress = Arrays.copyOf(tokens, tokens.length+1);

tokensWithIPAddress[tokensWithIPAddress.length-1] = getUserIPAddress(request);

super.setCookie(tokensWithIPAddress, maxAge, request, response);

}

이제 새로운 쿠키를 설정하는 데 필요한 정보가 모두 준비됐다.

마지막으로 사용자가 제공한 remember me 쿠키의 내용을 검증하는 데 사용할 processAuto

LoginCookie 메서드를 오버라이드한다. 이와 관련해서는 상위 클래스에서 거의 모든 기능을 처리

한다. 하지만 부모 메서드에서는 상당히 긴 코드를 사용하고 있으므로 여기서는 부모 메서드를

호출하기 전에 먼저 IP 주소를 확인해 부모 메서드 호출 여부를 결정하도록 내용을 수정한다.

Page 97: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

03 사용자 경험 개선 l 69

@Override

protected UserDetails processAutoLoginCookie(String[] cookieTokens,

HttpServletRequest request,

HttpServletResponse response) {

try {

setContext(request);

// 마지막 토큰을 가져온다.

String ipAddressToken = cookieTokens[cookieTokens.length-1];

if (!getUserIPAddress(request).equals(ipAddressToken)) {

throw new InvalidCookieException(

"Cookie IP Address did not contain a matching IP (contained '"

+ ipAddressToken + "')");

}

return super.processAutoLoginCookie(

Arrays.copyOf(cookieTokens, cookieTokens.length-1), request, response

);

} finally {

setContext(null);

}

}

이제 커스텀 RememberMeServices 구현체가 완성됐다. 남은 일은 간단한 설정을 수정하는 것뿐이

다. (추가 주석이 포함된) 이 클래스의 전체 소스 코드는 이 장에서 제공하는 소스에 들어 있다.

커스텀 RememberMeServices 구현체 설정하기

커스텀 RememberMeServices 구현체를 설정하는 작업은 두 단계로 진행된다.

먼저 dogstore-base.xml 스프링 설정 파일을 수정해 방금 완성한 클래스에 대한 스프링 빈 정의

를 새로 추가해야 한다.

<bean class="com.packtpub.springsecurity.security.IPTokenBasedRememberMeServices"

id="ipTokenBasedRememberMeServicesBean">

<property name="key"><value>jbcpPetStore</value></property>

<property name="userDetailsService" ref="userService"/>

</bean>

두 번째로는 스프링 시큐리티 XML 설정 파일의 내용을 수정해야 한다.

Page 98: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

70 l 스프링 시큐리티 3

다음과 같이 커스텀 스프링 빈을 참조하도록 <remember-me> 엘리먼트를 수정하자.

<remember-me key="jbcpPetStore" services-ref="ipTokenBasedRememberMeServicesBean"/>

마지막으로, id 어트리뷰트가 아직 없다면 <user-service> 선언에 id 어트리뷰트를 추가한다.

<user-service id="userService">

이제 웹 애플리케이션을 재시작하면 새로 추가한 IP 필터링 기능이 동작하는 것을 볼 수 있다!

remember me 쿠키는 Base64 방식으로 인코딩되기 때문에 새로 추가한 값은 쿠키의 값을 가져

온 다음 Base64 디코더를 사용해 디코딩해야 한다. 이렇게 하면 아래 값과 유사한 SPRING_SECURITY_

REMEMBER_ME_COOKIE 이름의 쿠키를 확인할 수 있다.

guest:1251695034322:776f8ad44034f77d13218a5c431b7b34:127.0.0.1 6

IP 주소는 예상한 대로 쿠키의 끝 부분에서 바로 볼 수 있다. 아울러 새로 추가한 IP 주소 앞에

있는 사용자명, 타임스탬프, MD5 해시도 각각 확인할 수 있다.

잠깐만요! remember me 쿠키 디버깅하기

remember me 쿠키를 디버깅할 때는 두 가지 어려운 점이 있다. 첫 번째 어려운 점은 쿠키

값 자체를 가져오는 것이다. 스프링 시큐리티에서는 설정된 쿠키값에 대해 로그를 남기는

로그 레벨을 일절 제공하지 않는다. 따라서 파이어폭스에서 사용할 수 있는 크리스 페드릭

의 웹 개발자 플러그인(http://chrispederick.com/work/web-developer/) 같은 브라우저

기반 툴을 사용해 쿠키값을 확인해야 한다. 이러한 브라우저 기반 개발 툴은 보통 쿠키값을

선택적으로 검사(하고 심지어 편집)할 수 있는 기능을 제공한다. 이렇게 가져온 쿠키값은

온라인 또는 오프라인 Base64 디코더를 사용해 디코딩할 수 있다(이때 유효한 Base64

인코딩 문자열이 되려면 마지막에 = 기호를 추가하는 것을 빼놓지 말아야 한다!).

IP 기반 remember me 토큰을 사용할 때는 멀티 WAN 기업 환경처럼 공유 네트워크 또는 로드

밸런싱 네트워크 인프라스트럭처상에 있을 경우 예상치 못한 동작을 할 수 있다는 점을 주의해야

한다. 하지만 대부분의 경우에는 rememeber me 기능에 IP 주소를 추가하면 사용자에 대한 보안

6   (옮긴이) 이 값은 디코딩 이후의 값으로 파이어폭스 등에서 확인한 쿠키값은 이 값의 Base64 인코딩 형태로 저장된다. 이러한

Base64 디코더 중 온라인 툴은 http://www.opinionatedgeek.com/dotnet/tools/base64decode/ 등이 있다.

Page 99: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

03 사용자 경험 개선 l 71

레이어를 추가할 수 있어서 사용자에게도 도움이 된다.

remember me 시그너처 커스터마이징하기

호기심이 있는 독자라면 remember me 폼 필드에서 기대하는 값인 spring_security_remember_me

나 쿠키 이름인 SPRING_SECURITY_REMEMBER_ME_COOKIE를 다른 값으로 변경해 스프링 시큐리티를 사용

하는 사실을 숨길 수 있는지 궁금할 것이다. <remember-me> 선언에서는 이러한 설정을 기본 제공하

지 않지만 지금은 스프링 빈을 사용해 RememberMeServices 구현체를 선언했으므로 체크박스 이름

이나 쿠키 이름을 변경하도록 속성을 더 정의할 수 있다.

<bean class="com.packtpub.springsecurity.web.custom.IPTokenBasedRememberMeServices"

id="ipTokenBasedRememberMeServicesBean">

<property name="key"><value>jbcpPetStore</value></property>

<property name="userDetailsService" ref="userService"/>

<property name="parameter" value="_remember_me"/>

<property name="cookieName" value="REMEMBER_ME"/>

</bean>

이때는 폼 필드의 이름이 여기서 선언한 parameter 매개변수 값과 일치하도록 login.jsp 페이지를

수정하는 것도 빼먹지 말아야 한다. 이쯤에서 몇 가지 설정을 추가하고 수정하는 등의 실험을 스

스로 해 보면서 지금까지 설정한 내용들을 확실히 이해하고 다음 단계로 넘어가자.

비밀번호 수정 기능 구현

이 절에서는 기본 인 메모리 UserDetailsService 인터페이스 구현체를 상속해 사용자가 비밀번호를

수정할 수 있는 기능을 제공해 보겠다. 이러한 비밀번호 수정 기능은 주로 사용자명과 비밀번호가

데이터베이스에 저장될 때 더 유용하지만 o.s.s.core.userdetails.memory.InMemoryDaoImpl 클래스를

상속해 이 기능을 구현하면 저장 방식과 관련한 내용에 얽매이지 않고 프레임워크에서 제공하는

확장 기능의 흐름과 설계에만 집중할 수 있다. 아울러 이러한 기본 내용을 더 확장해 데이터베이

스에 저장된 크리덴셜을 사용하는 내용은 4장에서 살펴보겠다.

Page 100: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

72 l 스프링 시큐리티 3

비밀번호 변경 기능을 위한 인 메모리 저장소의 확장

스프링 시큐리티 프레임워크에서 제공하는 InMemoryDaoImpl 인 메모리 크리덴셜 저장소는 간단한

맵을 사용해 사용자명 및 사용자명과 관련한 UserDetails를 저장한다. InMemoryDaoImpl 클래스에

서 사용하는 UserDetails 구현체는 o.s.s.core.userdetails.User 클래스로서 이 클래스는 스프링 시

큐리티 API 문서를 보다 보면 자주 눈에 띄는 클래스다.

이 확장 기능은 의도적으로 단순하게 설계됐으며 비밀번호 변경 전에 기존 비밀번호를 사용자

에게 요청하는 등의 주요 세부 내용들을 생략한다. 이러한 기능을 추가하는 작업은 독자의 몫으

로 남겨두겠다.

InMemoryChangePasswordDaoImpl을 이용한 InMemoryDaoImpl 상속하기

먼저 기본 클래스인 InMemoryDaoImpl 클래스를 상속하는 커스텀 클래스를 작성하고 사용자 비밀번

호를 변경할 수 있게 메서드를 추가해 보자. User는 불변 객체이므로 여기서는 기존 User 객체를 복

사한 다음 사용자가 제공한 비밀번호로 기존 비밀번호를 대체해야 한다. 나중에도 이 장의 내용

을 재사용할 수 있도록 다음과 같이 비밀번호 변경 기능을 제공하는 메서드를 정의하는 인터페이

스를 선언하자.

package com.packtpub.springsecurity.security;

// 임포트 생략

public interface IChangePassword extends UserDetailsService {

void changePassword(String username, String password);

}

다음은 인 메모리 사용자 데이터 저장소를 사용해 비밀번호 변경 기능을 제공하는 코드다.

package com.packtpub.springsecurity.security;

public class InMemoryChangePasswordDaoImpl extends InMemoryDaoImpl

implements IChangePassword {

@Override

public void changePassword(String username, String password) {

// UserDetails를 가져온다.

User userDetails = (User) getUserMap().getUser(username);

// 새로운 비밀번호를 사용해 새로운 UserDetails를 생성한다.

User newUserDetails = new User(

userDetails.getUsername(), password,

userDetails.isEnabled(), userDetails.isAccountNonExpired(),

userDetails.isCredentialsNonExpired(), userDetails.isAccountNonLocked(),

userDetails.getAuthorities());

Page 101: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

03 사용자 경험 개선 l 73

// 맵에 추가한다.

getUserMap().addUser(newUserDetails);

}

}

다행히 간단한 트릭만으로도 커스텀 클래스에서 기능을 추가할 수 있었다. 이어서 커스텀

UserDetailsService 구현체를 온라인 상점 사이트에 추가하는 데 필요한 설정 사항을 살펴보자.

InMemoryChangePasswordDaoImpl을 사용하도록 스프링 시큐리티 설정하기

새로 만든 UserDetailsService 구현체를 사용하도록 스프링 시큐리티 XML 설정 파일을 재설정해

보자. 아쉽게도 스프링 시큐리티 설정 프로세서에서 <user-service> 엘리먼트는 특별한 처리를 거

치기 때문에 이 부분은 우리가 생각하는 것보다 조금 더 어렵다. 따라서 여기서는 설정 파일을 수

정하는 대신 앞에서 선언한 <user-service> 엘리먼트를 삭제하고 새로 만든 빈을 명시적으로 선언

하는 방식을 택하기로 하겠다. 이렇게 할 경우 기존 설정은

<authentication-manager alias="authenticationManager">

<authentication-provider>

<user-service id="userService">

<user authorities="ROLE_USER" name="guest" password="guest"/>

</user-service>

</authentication-provider>

</authentication-manager>

다음과 같이 수정된다.

<authentication-manager alias="authenticationManager">

<authentication-provider user-service-ref="userService"/>

</authentication-manager>

user-service-ref 어트리뷰트는 짐작하는 대로 userService라는 id를 사용하는 스프링 빈에 대

한 레퍼런스다. 따라서 dogstore-base.xml 스프링 빈 XML 설정 파일에서는 다음과 같이 빈을 선언

해야 한다.

<bean id="userService" class="com.packtpub.springsecurity.security.

InMemoryChangePasswordDaoImpl">

<property name="userProperties">

<props>

<prop key="guest">guest,ROLE_USER</prop>

</props>

Page 102: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

74 l 스프링 시큐리티 3

</property>

</bean>

수정된 설정 파일에서는 사용자를 선언하는 구문이 <user-service> 선언 내에서 사용한 <user>

엘리먼트만큼 가독성이 좋지 못한 것을 볼 수 있다. 아쉽게도 <user> 엘리먼트는 기본 InMemory

DaoImpl 구현체를 사용할 때만 사용할 수 있고 커스텀 UserDetailsService 구현체를 사용할 때는

사용할 수 없다. 이 예제에서는 이러한 제약으로 인해 예제 진행 과정이 다소 복잡해졌지만 실제

개발 시에 사용자 정의를 설정 파일에 저장하는 작업은 그리 오래 걸리는 작업이 아니다. 이와 관

련한 자세한 사항은 스프링 시큐리티 3의 레퍼런스 문서에서 6.2절을 참고하면 콤마 구분자를 사

용한 선언 구문을 통해 사용자 정보를 제공하는 것과 관련한 상세 정보를 확인할 수 있다.

잠깐만요! 인 메모리 UserDetailsService 효과적으로 사용하기

인 메모리 UserDetailsService를 사용하는 가장 흔한 경우 중 하나는 하드코딩된 사용자

목록을 사용해 보호된 컴포넌트에 대한 단위 테스트를 작성할 때다. 단위 테스트를 작성하

는 개발자는 대개 테스트하려는 컴포넌트의 기능을 테스트하는 데 필요한 최소한의 코드

나 설정을 사용하곤 한다. 사용자와 GrantedAuthority 값을 잘 정의한 상태에서 인 메모리

UserDetailsService를 사용하면 테스트 작성자는 쉽게 제어할 수 있는 테스트 환경을 만들

수 있다.

이제 아무런 설정 관련 에러 없이 JBCP Pets 애플리케이션을 시작할 수 있을 것이다. 이어서 두

단계에 걸친 예제를 통해 지금까지 배운 비밀번호 변경 기능을 UI에 접목해 비밀번호 변경 기능을

마무리하자.

비밀번호 변경 페이지 개발하기

이 절에서는 사용자가 비밀번호를 변경할 수 있는 간단한 비밀번호 변경 페이지를 만들어 보겠다.

Page 103: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

03 사용자 경험 개선 l 75

이 페이지는 간단한 링크를 통해 My Account 페이지로 연결된다. 먼저 다음과 같이 /account/

home.jsp 파일에 링크를 추가하자.

<p>

Please find account functions below...

</p>

<ul>

<li><a href="changePassword.do">Change Password</a></li>

</ul>

이어서 /account/에 비밀번호 변경 페이지를 만들자.

changePassword.jsp의 내용 :

<?xml version="1.0" encoding="ISO-8859-1" ?>

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"

pageEncoding="ISO-8859-1"%>

<jsp:include page="../common/header.jsp">

<jsp:param name="pageTitle" value="Change Password"/>

</jsp:include>

<h1>Change Password</h1>

<form method="post">

<label for="password">New Password</label>:

<input id="password" name="password" size="20" maxlength="50"

type="password"/>

<br />

<input type="submit" value="Change Password"/>

</form>

<jsp:include page="../common/footer.jsp"/>

마지막으로 스프링 MVC 기반의 AccountController에서 비밀번호 변경 요청을 처리하게 해야 한

다(앞에서 AccountController에 대한 내용은 다루지 않았는데 이 컨트롤러는 계정 홈페이지를 처

리하는 간단한 핸들러를 포함한 컨트롤러로 이해하면 된다).

AccountController에 비밀번호 변경 핸들러 추가하기

여기서는 com.packtpub.springsecurity.web.controller.AccountController에 커스텀 UserDetails

Service에 대한 참조를 추가로 주입해 비밀번호 변경 기능을 사용해 보겠다. 스프링의 @Autowired

애노테이션을 사용하면 이 작업을 간단히 처리할 수 있다.

@Autowired

private IChangePassword changePasswordDao;

Page 104: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

76 l 스프링 시큐리티 3

아래 두 요청 핸들러 메서드는 폼의 렌더링을 책임지고 POST 방식으로 전송되는 폼을 처리한다.

@RequestMapping(value="/account/changePassword.do", method=RequestMethod.GET)

public void showChangePasswordPage() {

}

@RequestMapping(value="/account/changePassword.do", method=RequestMethod.POST)

public String submitChangePasswordPage(@RequestParam("password") String newPassword) {

Object principal =

SecurityContextHolder.getContext().getAuthentication().getPrincipal();

String username = principal.toString();

if (principal instanceof UserDetails) {

username = ((UserDetails)principal).getUsername();

}

changePasswordDao.changePassword(username, newPassword);

SecurityContextHolder.clearContext();

return "redirect:home.do";

}

이렇게 코드를 수정하고 애플리케이션을 다시 실행한 다음 사이트의 My Account 영역에서

Change Password 동작을 확인해 보자.

예제 참고사항

내용을 유심히 읽은 독자라면 이러한 비밀번호 변경 폼은 실제 애플리케이션에 사용하기에는 지

나치게 단순한 형태라고 느낄 것이다. 실제로 비밀번호 변경 기능을 구현하는 일은 대부분 이보다

훨씬 복잡하고 다음과 같은 기능들이 추가로 필요하다.

▶ 비밀번호 확인: 두 개의 별도 입력 상자를 통해 사용자가 새로 입력한 비밀번호가 제대로

입력됐는지 확인하는 기능.

▶ 기존 비밀번호 확인: 사용자가 비밀번호를 변경하기 전에 기존 비밀번호를 입력하게 하는

보안 레이어의 추가 적용(remember me 기능을 사용 중이라면 이 기능의 중요성이 더 커

진다).

▶ 비밀번호 규칙 유효성 검증: 비밀번호의 복잡도를 체크해 지나치게 단순하거나 보안에 취약

한 비밀번호를 걸러내는 기능

앞에서 기능을 테스트하는 도중에 자동 로그아웃되는 것을 볼 수 있었을 것이다. 이러한 자동 로

그아웃은 요청으로부터 사용자의 SecurityContext를 제거하는 SecurityContextHolder.clearContext()

Page 105: 스프링 시큐리티 3 : 스프링 프레임워크 기반 표준 보안 솔루션

03 사용자 경험 개선 l 77

를 호출했기 때문에 발생하며, 이때 사용자는 다시 인증을 받아야 한다. 실제 애플리케이션을 만

들 때는 이러한 정보를 사용자에게 경고를 통해 알려주거나 사용자가 다시 인증을 거치지 않아도

되게끔 우회 방식을 사용하는 게 좋다.

요약

이 장에서는 인증받은 사용자의 라이프사이클을 좀더 자세히 살펴보고 JBCP Pets 온라인 상점의

구조를 수정하는 실험을 해봤다. 또한 실제 로그인/로그아웃 기능을 추가하고 사이트에서 동일한

사용자 경험을 전달함으로써 보안 감사원들을 만족시키는 데 한 걸음 더 나아갔다. 이 장에서는

다음과 같은 내용을 배웠다.

스프링 MVC 기반의 커스텀 로그인 페이지 설정 및 사용 ▶

스프링 시큐리티 로그아웃 기능의 설정 ▶

remember me 기능 활성화 ▶

사용자의 IP 주소를 확인하는 remember me 기능의 커스터마이징 ▶

비밀번호 변경 기능 구현 ▶

UserDetailsService ▶ 와 InMemoryDaoImpl의 커스터마이징

4장에서는 데이터베이스를 사용한 인증 저장소를 살펴보고 데이터베이스에 저장된 비밀번호와

기타 민감한 데이터를 안전하게 보호하는 방법을 알아보겠다.