관리 메뉴

Just Do it

Part 7) Spring Web Security를 이용한 로그인 처리 (Ch 34 & 35 & 36 & 37) 본문

신입 개발자가 되기 위해 공부했던 독학 자료들/코드로 배우는 스프링 웹프로젝트

Part 7) Spring Web Security를 이용한 로그인 처리 (Ch 34 & 35 & 36 & 37)

Seojoo21 2022. 3. 14. 17:40

* 카테고리의 글은 <코드로 배우는 스프링  프로젝트에서  파트별로 진행하는 프로젝트의 흐름을 보기 위해  장의 내용을 간단히 요약한 것이다.

출처코드로 배우는 스프링  프로젝트 개정판구멍가게 코딩단남가람북스 

 

 

Ch34 스프링 시큐리티를 JSP에서 사용하기

- JDBC와 약간의 쿼리를 이용하는 것만으로도 데이터베이스를 이용해서 스프링 시큐리티를 사용할 수 있음에도 불구하고, 굳이 CustomUserDetailsService와 같이 별도의 인증/권한 체크를 하는 가장 큰 이유는 JSP 등에서 단순히 사용자의 아이디(스프링 시큐리티에서는 username)정도가 아닌 사용자의 이름이나 이메일과 같은 추가적인 정보를 이용하기 위해서입니다. 

 

- 이 장의 시작 부분에서 이미 pom.xml에 스프링 시큐리티를 이용할 때 사용하는 태그라이브러리를 추가해 두었으므로, JSP에서 이를 활용하는 방법에 대해 알아보자.

**pom.xml에 추가한 스프링 시큐리티를 이용할 때 사용하는 태그라이브러리

<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-taglibs -->
	<dependency>
	    <groupId>org.springframework.security</groupId>
	    <artifactId>spring-security-taglibs</artifactId>
	    <version>5.0.6.RELEASE</version>
	</dependency>

 

34.1 JSP에서 로그인한 사용자 정보 보여주기 

- '/sample/admin'과 같은 경로는 로그인한 사용자만이 접근할 수 있고, 만일 권한이 적당하지 않으면 볼 수 없는 페이지이므로 로그인한 사용자가 접근했을 때에는 해당 사용자의 여러 정보를 보여줄 필요가 있다.

- 스프링 시큐리티와 관련된 정보를 출력하거나 사용하려면 JSP 상단에 스프링 시큐리티 관련 태그 라이브러리의 사용을 선언하고, <sec:authentication> 태그와 principal이라는 이름의 속성을 사용한다.

 

*admin.jsp

- <sec:authentication property="principal" />를 이용했을 때 의미하는 것은 UserDetailsService 에서 반환된 객체이다.

- 즉 CustomUserDetailsService를 이용했다면 loadUserByUsername()에서 반환된 CustomUser 객체가 된다.

- 이 사실을 이해하면 'principal'이 CustomUser를 의미하므로 'principal.member'는 CustomUser 객체의 getMember()를 호출한다는 것을 알 수 있다.

 

- 브라우저에서 사용자가 로그인을 한 후에 보는 admin.jsp에는 다음과 같은 정보들이 출력된다. 

 

34.2 표현식을 이용하는 동적 화면 구성 

- 경우에 따라서는 특정한 페이지에서 로그인한 사용자의 경우에는 특정한 내용을 보여주고, 그렇지 않은 경우에는 다른 내용을 보여주는 경우가 있다. 

- 예제는 '/sample/all'이 이런 경우에 해당되는데 이때 유용한 것이 스프링 시큐리티의 표현식(expression)이다. 

- 스프링 시큐리티의 표현식은 security-context.xml에서도 사용된다. 

- 스프링 시큐리티에서 주로 사용되는 표현식은 아래와 같다. 

 

표현식 설명 
hasRole([role])
hasAuthority([authority])
해당 권한이 있으면 true 
hasAnyRole([role,role2])
hasAnyAuthority([authority])
여러 권한들 중에서 하나라도 해당하는 권한이 있으면 true
principal 현재 사용자 정보를 의미 
permitAll 모든 사용자에게 허용 
denyAll 모든 사용자에게 거부 
isAnonymous() 익명의 사용자의 경우(로그인을 하지 않은 경우도 해당)
isAuthenticated() 인증된 사용자면 true
isFullyAuthenticated() Remember-me로 인증된 것이 아닌 인증된 사용자인 경우 true 

 

- 표현식은 거의 대부분 true/false를 리턴하기 때문에 조건문을 사용하는 것처럼 사용된다.

- '/sample/all'의 JSP 페이지를 이용해서 사용자의 로그인 상태에 따라 다른 화면을 구성해 보면 아래와 같은 형태가 된다.

 

*all.jsp

로그인하지 않은 사용자
로그인한 사용자

 

Ch35 자동 로그인(remember-me)

- 최근의 웹페이지들은 '자동 로그인'이나 '로그인 기억하기'라는 이름으로 한 번 로그인하면 일정 시간 동안 다시 로그인을 하지 않아도 되는 기능을 가지고 있다. 영어로는 'remember-me'라고 표현하는데 이 기능은 거의 대부분 쿠키(Cookie)를 이용해서 구현된다. 국내에서도 많은 사이트에서 이 기능을 제공하고 있다.

- 스프링 시큐리티의 경우 'remember-me' 기능을 메모리상에서 처리하거나, 데이터베이스를 이용하는 형태로 약간의 설정만으로 구현이 가능하다.

- security-context.xml에는 <security:remember-me> 태그를 이용해서 기능을 구현한다.

- <security:remember-me>에는 아래와 같이 여러 속성값을 지정할 수 있다. 여러 속성 중에서 주로 사용되는 속성은 다음과 같다.

 

* key: 쿠키에 사용되는 값을 암호화하기 위한 키(key)값

*.data-source-ref: DataSource를 지정하고 테이블을 이용해서 기존 로그인 정보를 기록(옵션)

* remember-me-cookie: 브라우저에 보관되는 쿠키의 이름을 지정한다. 기본값은 'remember-me'이다.

* remember-me-parameter: 웹 화면에서 로그인할 때 'remember-me'는 대부분 체크박스를 이용해서 처리한다. 이때 체크박스 태그는 name 속성을 의미한다.

* token-validity-seconds: 쿠키의 유효시간을 지정한다. 

 

35.1 데이터베이스를 이용하는 자동 로그인 

- 자동 로그인 기능을 처리하는 방식 중에서 가장 많이 사용되는 방식은 로그인이 되었던 정보를 데이터베이스를 이용해서 기록해 두었다가 사용자의 재방문 시 세션에 정보가 없으면 데이터베이스를 조회해서 사용하는 방식이다.

- 서버의 메모리상에만 데이터를 저장하는 방식보다 좋은 점은 데이터베이스에 정보가 공유되기 때문에 좀 더 안정적으로 운영이 가능하다는 점이다.

 

- 스프링 시큐리티에서 'remember-me' 기능 역시 JDBC를 이용하는 경우처럼 지정된 이름의 테이블을 생성하면 지정된 SQL문이 실행되면서 이를 처리하는 방식과 직접 구현하는 방식이 있다.

- 생성된 테이블은 로그인을 유지하는데 필요한 정보를 보관하는 용도일 뿐이므로, 커스터마이징 하기 보다는 지정된 형식의 테이블을 생성한다.

 

- 스프링 시큐리티의 공식 문서에 나오는 로그인 정보를 유지하는 테이블은 아래와 같은 스크립트로 구성된다.

create table persistent_logins(
    username varchar(64) not null,
    series varchar(64) primary key,
    token varchar(64) not null,
    last_used timestamp not null);

- 테이블을 생성하는 스크립트는 특정한 데이터베이스에 맞게 테이블 이름과 칼럼명을 제외한 칼럼의 타입 등을 적당히 조정해서 사용하면 된다. 오라클에서는 varchar를 그대로 이용하거나 varchar2로 변경해서 사용하면 된다.

 

- 자동 로그인에서 데이터베이스를 이용하는 설정은 별도의 설정 없이 security-context.xml에서 data-source-ref만을 지정하면 된다.

 

35.1.1 로그인 화면에 자동 로그인 설정 

- 자동 로그인은 로그인 화면에서 선택해서 처리되므로, 체크박스의 형태로 구현하고 <input> 태그의 name 속성값을 'remember-me' 로 준다. 

- 프로젝트를 실행하고 'Remember-me'를 체크한 후 브라우저에서 쿠키를 조사해 보면 자동으로 'remember-me'라는 이름의 쿠키가 생긴 것을 확인할 수 있다. 

- 데이터베이스의 'persistence_logins' 테이블에도 사용자가 로그인한 정보가 남아 있는 것을 볼 수 있다.

- 'remember-me'이름으로 생성된 쿠키는 유효기간이 있으므로, 사용자는 브라우저를 완전히 종료한 후에 다시 '/sample/admin'과 같이 로그인이 필요한 페이지에 접근해보면 정상적으로 로그인 되는 것을 확인할 수 있다. 

- 브라우저를 종료한 후 '/sample/admin'을 호출했을 때 브라우저가 보내는 정보를 확인해 보면 'remember-me' 쿠키의 정보가 전송되는 것을 볼 수 있다. 

 

35.1.2 로그아웃 시 쿠키 삭제 

- 자동 로그인 기능을 이용하는 경우에 사용자가 로그아웃을 하면 기존과 달리 자동 로그인에 사용하는 쿠키도 삭제해 주도록 쿠키를 삭제하는 항목을 security-context.xml에 지정한다. 

- 별도의 설정이 없었다면 자동 로그인에서 사용한 쿠키의 이름은 'remember-me'였을 것이고, Tomcat을 통해서 실행되고 있었다면 WAS가 발행하는 쿠키의 이름은 'JESSION_ID' 이다. Tomcat 등이 발행하는 쿠키는 굳이 지정할 필요는 없지만 관련된 모든 쿠키를 같이 삭제해 주는 것이 좋다. 

 

Ch36 Java 설정을 이용하는 경우의 스프링 시큐리티 설정 (Skip)

Ch37 어노테이션을 이용하는 스프링 시큐리티 설정

- XML이나 Java 설정을 이용해서 스프링 시큐리티를 설정하고 사용하는 방식도 좋지만 매번 필요한 URL에 따라서 설정을 변경하는 일은 번거로운 일이다. 

- 스프링 시큐리티 역시 다른 기능들처럼 어노테이션을 이용해서 필요한 설정을 추가할 수 있다. 사용되는 어노테이션은 주로 @Secured와 @PreAuthorize, @PostAuthorize이다.

 

* @Secured: 스프링 시큐리티 초기부터 사용되었고, ()안에 'ROLE_ADMIN'과 같은 문자열 혹은 문자열 배열을 이용한다. 

* @PreAuthorize, @PostAuthorize: 3버전부터 지원되며, ()안에 표현식을 사용할 수 있으므로 최근에는 더 많이 사용된다. 

 

- 예제를 위해서 앞의 XML을 이용하는 예제에서 사용했던 SampleController에 간단한 메서드와 설정을 아래와 같이 추가한다. 

- domember2()에는 @PreAuthorize를 이용해서 표현식으로 'hasAnyRole'을 사용해서 체크하고, doAdmin2()는 @Secured를 이용해서 처리한다. 

- @Secured에는 단순히 값(value)만을 추가할 수 있으므로 여러 개를 사용할 때에는 배열로 표현한다. 

- 주의할 사항은 컨트롤러에서 사용하는 스프링 시큐리티의 어노테이션을 활성화하기 위해서는 security-context.xml이 아닌 스프링 MVC의 설정을 담당하는 servlet-context.xml에 관련 설정이 추가된다는 점이다. 

 

- 우선 servlet-context.xml에 security 네임스페이스를 추가한다. 

- XML에 스프링 시큐리티의 네임스페이스가 추가될 때 5.0버전으로 추가되는 것은 4.2버전으로 낮추거나 버전 정보를 지워야 에러가 없이  작동한다. 

- 추가된 security 네임스페이스를 이용해서 global-method-security를 지정한다.

- 어노테이션은 기본으로 'disabled'되어 있으므로 'enabled'로 설정한다. 

<security:global-method-security pre-post-annotations="enabled" secured-annotations="enabled" />