이전 글에서 인증 서버를 로컬에서 실행 시켜봤다.

로그인 화면은 확인 했지만 계정이 존재하지 않기 때문에 로그인이 되지는 않았다.

그리고 HTTPS 로 연결해야 SSO를 제대로 사용할 수 있다고 했다.

 

아래 이미지는 이전 글에서 봤던 로그인 페이지이다.

현재는 로그인이 불가능하며, 노란색 박스를 보면 HTTPS 관련 경고 문구가 있다.

이번 글에서는

  • 로컬 SSL 키 저장소를 생성해서 HTTPS로 인증 서버를 구현해보자.

위 내용에 대해서 작성하겠다.

 

먼저 로그인 화면에서 보여지는 HTTPS 관련 에러 문구를 지워보도록 하자.

 

첫번째!!!!

우선 터미널을 실행시키고 cas-server/src/main/resources/etc/cas 디렉토리로 이동하자.

그리고 다음 명령어를 실행하자.

keytool -genkey -keyalg RSA -alias thekeystore -keystore thekeystore -storepass changeit -validity 360 -keysize 2048

그리고 아래와 같이 입력한다.

** 입력 시 주의사항 **

이름과 성, 조직 이름단위에 대한 정보를 입력할 때에는 인증 서버의 domain을 입력하는 것이 다.

localhost로 테스트 할 것이라면 localhost를 사용해야 한다.

만약 cas.server.com 으로 테스트 하려면 cas.server.com 을 입력하고 hosts 파일(윈도우즈 기준) 을 수정해서 사용하면 된다.

이 작업을 수행하지 않으면 SSL 핸드 쉐이크 중에 오류가 발생할 수 있다!!

 

위 내용을 요약하면 현재 디렉토리에 이름thekeystore 이고,  Passwordchangeit 인 키 저장소를 생성한다.

 

두번째!!!!

다음 단계는 생성 된 저장소를 클라이언트 응용 프로그램에서 사용할 수 있도록 .crt 형식으로 export 하는 것이다.

동일한 디렉토리에서 다음 명령을 실행하여 생성 된 thekeystore 파일을 keystore.crt 로 export 하자.

암호는 동일하게 하자.

keytool -export -alias thekeystore -file thekeystore.crt -keystore thekeystore

디렉토리에 thekeystore.crt 파일이 생성된 것을 확인할 수 있다.

 

이제 export 된 thekeystore.crt를 Java cacerts 키 저장소로 import 하자.

 

아래와 같이 입력하자.

keytool -import -alias thekeystore -storepass changeit -file thekeystore.crt -keystore "C:\Program Files\Java\jdk1.8.0_151\jre\lib\security\cacerts"

 

이 때, Java cacerts 키 저장소의 위치는 클라이언트 Application에서 사용되는 JRE와 동일해야 한다.

 

확인을 해보자.

빨간색 박스로 표시한 파일은 인증서를 import 하기 전 원본 cacerts 파일이다.

파란색 박스로 표시한 파일은 인증서를 import 한 복사본 cacerts 파일이다.

 

몇번 더 추가해보면 알겠지만 파일 크기가 증가한다. 인증서가 추가되는 것이다.

다시 말하면 원본 cacerts 파일을 복사하지 않고 새 파일로 추가하게 되면 해당 JRE를 사용하는 다른 Application이 SSL 핸드쉐이크를 할 때 문제가 생길 수 있다는 말이다.

 

프로젝트를 thekeystore 키 저장소가 있다.

 

그리고 application.properties 에 key 정보를 입력하자.

 

그리고 다시 실행해보자.

 

https://localhost:8443/cas/login

 

이전 글에서 봤던 로그인 화면이랑 조금 달라졌다.

HTTPS 관련 경고 문구가 사라졌다. 그리고 URL을 확인하면 HTTPS 가 적용된 것을 확인할 수 있다.

 

 

추가로..

 

로컬에 설치 되어 있는 Tomcat을 사용할 경우 다음과 같이 적용하면 된다.

 

먼저 설치된 Tomcat 디렉토리 위치에서 conf 디렉토리로 이동한다.

그곳에 위치한 server.xml 파일을 다음과 같이 수정한다.

 

keystoreFile : 이 곳은 생성한 keystore의 위치를 입력하자.

keystorePass : keystore 생성 시 입력했던 암호를 입력하자.

 

그리고 나서 다음과 같이 주석을 하자.

그리고 실행 설정을 추가하자.

**주의 사항**

Windows에서는 application.properties 파일의 server.context-path 설정이 적용되지 않았다.

때문에 Tomcat 설정의 DEPLOYMENT 탭에서 Application context를 /cas로 설정해도, 'https://localhost:8443/cas/login'으로 접근이 가능했다.

하지만 MacOS에서는 두개 모두 적용이 되어서 'https://localhost:8443/cas/cas/login' 으로만 접근이 가능했다.

 

 

다음 글에서는 계정을 추가하는 방법에 대해서 쓰겠다.

SSO CAS Server 샘플 프로젝트를 만들어 보자.

 

https://www.baeldung.com/spring-security-cas-sso

 

CAS SSO With Spring Security | Baeldung

Learn how to integrate the Central Authentication Service (CAS) with Spring Security.

www.baeldung.com

Single Signe On 서비스를 위해서는 최소한 두 개의 프로젝트가 필요하다.

첫번째는 cas-server라고 하는 Spring 기반의 인증 서버이다.

두번째는 하나 이상의 클라이언트 이다.

 

이번 장에서는 인증 서버에 대한 기본적인 설치 및 실행 방법에 대해서만 설명할 것이다.

추가적인 커스터마이징에 대해서는 추후에 쓰도록 하겠다.

 

CAS Server 설치하기

기본적인 CAS 인증서버를 구성하는 것은 매우 간단하다.

결론만 말하자면, github에서 소스를 받아서 실행만 하면 인증서버가 실행된다.

 

Cas Server 프로젝트 받기

git clone https://github.com/apereo/cas-overlay-template.git cas-server

위에 적어놓은 것처럼 github 에서 바로 clone 해서 사용해도 되지만, 난 zip 파일을 다운받았다.

다운로드 하기 위해서는 아래 링크로 이동해서 진행하자.

https://github.com/apereo/cas-overlay-template

 

그리고 나는 maven을 더 잘 알고 있고 사용하기 편하기 때문에 master 브랜치가 아닌 5.1을 선택했다.

 

원래는 5.3을 선택 했었는데, 의존성을 추가하는 과정에서 exclusion 해야 하는 이슈가 있어서 5.1로 변경했다.

 

5.1 버전

소스 코드를 다운로드 받았고 이제 실행을 하면 되는데 그전에 소스가 어떻게 되어 있는지 확인해보자.

github에 있는 소스를 확인해보면 디렉토리가 두 개가 있는 것을 확인할 수 있다.

그리고 이제 Intellij로 다운로드 받은 소스를 열어보자.

 

다운로드 받은 CAS 인증 서버.

프로젝트를 실행하고 maven reimport(보통 pom.xml 파일이 있으면 maven 프로젝트로 설정된다.) 를 하게 되면 'overlays' 라는 github 사이트에서 확인할 수 없었던 디렉토리(녹색 박스)가 생겨난 것을 확인할 수 있다.

 

Maven Overlay

서버는 Maven(Gradle) War Overlay 스타일을 사용하기 때문에 소스만 다운받으면 바로 실행이 가능하다.

 

War Overlay를 간단하게 설명하면

Overlay는 여러 Web Application에서 공통 리소스를 공유하는데 사용된다.

war 프로젝트의 종속성들은 war 프로젝트 자체에 Overlay 된 artifact를 제외하고 WEB-INF/lib 에 수집된다.

쉽게 이야기 하면...의존성에 있는 lib 들과 공통 리소스를 가져와서 사용하는 것이다.

물론 같은 classpath에 같은 파일이라면 현재 프로젝트가 우선시 된다.

이정도만 알아도 CAS 인증서버를 실행하기에 충분하다.

아래 링크를 가면 자세한 설명이 있다. 난 이곳을 참고했다.

http://maven.apache.org/plugins/maven-war-plugin/overlays.html

 

Apache Maven WAR Plugin – Overlays

Overlays Overlays are used to share common resources across multiple web applications. The dependencies of a WAR project are collected in WEB-INF/lib, except for WAR artifacts which are overlayed on the WAR project itself. Overlays at a glance To demonstra

maven.apache.org

CAS Server 설정하기

의존성 추가

이제 pom.xml에 의존성을 추가 하도록 하자.

추가 될 의존성들은 JSON 구성을 통해서 서비스(Application) 등록을 가능하게 해준다.

<dependency>
    <groupId>org.apereo.cas</groupId>
    <artifactId>cas-server-support-json-service-registry</artifactId>
    <version>${cas.version}</version>
</dependency>
<dependency>
    <groupId>org.apereo.cas</groupId>
    <artifactId>cas-server-support-jdbc</artifactId>
    <version>${cas.version}</version>
</dependency>
<dependency>
    <groupId>org.apereo.cas</groupId>
    <artifactId>cas-server-support-jdbc-drivers</artifactId>
    <version>${cas.version}</version>
</dependency>

프로젝트 설정 & SSL 적용

다음은 Application의 포트를 변경하는 등의 설정과 SSL 키 저장소 경로를 설정하는 작업을 해보자.

  1. CAS Server 프로젝트에 'src/main/resources' 폴더를 생성하자.
  2. CAS Server 프로젝트에 'etc' 폴더를 위에서 생성했던 'src/main/resources' 폴더로 복사하자.
  3. 'src/main/resources' 에 application.properties 파일을 생성하고 기본값을 작성해보자.

폴더를 생성하고 application.properties 를 생성했다.

Cas 인증서버 실행

터미널에서 빌드실행을 해보자.

maven에 대해서 어느정도 지식이 있다는 가정하에 진행 하도록 하겠다.

$ mvn package
$ java -jar target/cas.war

실행이 잘 되었다면 브라우져에서 확인해보자.

 

localhost:8443/cas/login 으로 접속하면 위의 화면을 확인할 수 있다.

Port 8443과 Context-path /cas 는 application.properties 에서 설정했다.

 

노란색 박스안에 뭔가 위험하다는 것을 표시하는 메시지가 있는데 해석하면

"지금 너의 인증 서버는 HTTPS가 적용되어 있지 않아서 Single Sign On이 제대로 동작하지 않을거야...SSO가 잘 동작하기 위해서는 반드시 HTTPS를 적용해야 할거야!!!!!"

라고 적혀 있다.

 

안전한 데이터를 주고 받기 위해서는 HTTPS가 반드시 적용되어 있어야 한다는 것은 아마 알고 있을 것이다.

특히나 SSO는 인증과 관련된 데이터를 주고 받는 일이기 때문에 반드시 HTTPS가 정상적으로 적용되어 있어야 한다.

실제로 HTTPS가 잘 못 적용되어 있을 경우에는 Single Sign On(단일 로그인) 은 문제 없이 테스트가 되었지만,

Single Sign Out(단인 로그아웃)은 제대로 동작하지 않았다.

 

현재 로그인 페이지에서는 로그인이 불가능하다.

너무 당연하게도 ID가 없기 때문이다.

 

다음 장에서는 HTTPS를 적용하는 방법과 테스트를 위한 계정을 만들고, 로그인 해보도록 하겠다.

Single Sign On (& Single Sign Out)

CAS를 이용한 SSO 기능구형에 대한 기록을 블로그에 남기기로 했다.

한글로 되어 있는 블로그를 찾기 힘들어서 다른 분들에게 조금이나마 도움이 되었으면 하는 마음에 남긴다.

 

SSO를 왜 써야 할까?

 

우선 시나리오를 만들어보자.


시나리오 : 

웹 솔루션 서비스를 제공하는 회사를 하나 만들었다고 생각해보자.

 

처음에는 하나의 제품만을 서비스 했다.

서비스가 성공적이었고 또 다른 제품을 만들어 서비스 하게 되었다.

서비스가 또 성공적이었고 또 다른 제품을 만들어 서비스 하게 되었다.

서비스가 또또 성공적....

 

이렇게 여러 제품을 만들어 서비스도 잘 되고 있는데 사용자들이 불만을 갖기 시작한다.

"어째서 케넨소프트의 서비스는 한 회사의 제품이고 아이디와 비밀번호가 같은데 매번 로그인 해야하죠?? 너무 불편해요!! (좋아요 23412)"

 

그렇다.

한 회사의 제품이고 아이디와 비밀번호가 같은데 매번 로그인을 수행하는 것은 사용자 입장에서는 참으로 비효율 적이고 불편한 일이다.

로그인 뿐만 아니라 그렇게 각각 로그인 했을 경우 로그아웃도 각각 해줘야 하기 때문에 보안적으로도 취약하다.

그리고 

비밀번호 암호화 알고리즘을 변경해야 하는 일이 생겼다고 생각해보자.

모든 서비스 담당자들은 본인이 담당하고 있는 제품들의 로그인 부분을 수정해야 한다.

로그인 암호화 방식을 변경하는 일이기 때문에 출시 일정을 정해야 하는데 쉽지가 않다.

각자 일정이 잡혀 있기 때문이다.

 

그리고는 생각했다. 

로그인을 한곳에서 하면 어떨까??


위에 적은 시나리오 처럼 여러가지 애플리케이션을 서비스 하게 되면 반드시 필요한 것이 바로

통합인증 이다.

 

SSO 란?

SSO 란 여러가지 애플리케이션 서비스를 하나의 인증서버를 통해서 인증 할 수 있는 기술(또는 프로토콜)을 뜻한다.

 

SSO 장점

  • 한 번의 로그인으로 여러 서비스를 추가적인 로그인 작업을 하지 않고 사용 가능하기 때문에 사용자의 편의성이 향상된다.

  • 로그인 로직이 변경되거나 암호화 방식을 변경해야 할 경우 제품마다 적용할 필요 없이 인증 서버만 수정하면 되기 때문에 효율적이다.

  • 하나의 서비스에서 로그아웃을 할 경우에 다른 서비스들 역시 로그아웃(세션만료) 시킬 수 있기 때문에 보안적인 이점이 있다.

SSO 구현

SSO는 기술 또는 프로토콜을 뜻하고 구현 방법에는 몇가지가 있는데,

나는 SpringSecurity와 CAS(https://www.apereo.org/projects/cas) 를 이용했고 이를 설명할 것이다. 

 

Spring Security와 CAS를 이용한 SSO 에 대한 튜토리얼은 아래 링크를 통해 따라할 수 있다.

 https://www.baeldung.com/spring-security-cas-sso

 

CAS SSO With Spring Security | Baeldung

Learn how to integrate the Central Authentication Service (CAS) with Spring Security.

www.baeldung.com

튜토리얼대로 잘 따라하면 문제 없이 튜토리얼을 실행시킬 수 있을 것이다.

인증 서버 프로젝트, 클라이언트 프로젝트를 각각 생성해서 테스트 해보는 내용이다.

하지만 언제나 그렇듯... 튜토리얼은 튜토리얼이다.

여러가지 요구사항에 대응하고, 여러가지 변수에 대처하기 위해서는 Spring과 Spring Security에 대한 어느정도 지식이 필요하다.

그렇다고 해서 Spring Security에 대한 모든 것을 알아야 하는 것은 아니므로, 어느정도 기반지식이 있다면 SSO 를 구현하면서 함께 습득할 수 있다.

 

Spring & CAS 동작 순서

여기에서는 요약만 하고 다음 글에서 소스와 함께 설명하겠다.

또한 Proxy에 대한 내용은 포함시키지 않았다.

  1. 사용자가 인증이 필요하지 않는 Public 페이지에 접근한다. (이 때는 CAS가 어떤 동작을 취하지 않는다.)

  2. 로그인 하지 않은 사용자가 인증이 필요한 페이지에 접근을 하게 되면,
    Spring Security의 ExceptionTranslationFilterAccessDeniedException 또는 AuthenticationException을 탐지한다.

  3. 인증이 완료되지 않은 사용자가 secured page에 접근하는 경우에는 AuthenticationException 을 throw 하게 되는데, ExceptionTranslationFilter 는 이 경우에 AuthenticationEntryPoint 에 설정되어 있는 메소드를 호출하게 된다.
    CAS를 사용할 경우에는 CasAuthenticationEntryPoint 클래스가 사용된다.
  4. CasAuthenticationEntryPoint는 사용자 브라우져를 Cas 인증 서버로 redirect 시킨다.
    redirect 시에 'service' parameter 를 함께 전달하는데, 이것은 현재(당신의) Application의 Spring Security Service로의 callback URL이다.
    예)https://cas.server.com/cas/login?service=https://my.application.com/login/cas
  5. CAS 인증서버로 redirection 되면 사용자 이름과 비밀번호를 묻는 화면이 나온다.
    인증서버는 사용자 이름과 비밀번호의 유효성 여부를 결정하기 위해 PasswordHandler 또는 AuthenticationHandler(CAS 3.0을 사용할 경우)를 사용한다. 
    (사용자가 이전에 같은 세션 쿠키로 로그인 되어 있다면 다시 로그인 하라는 화면은 표시되지 않는다.)
  6. 로그인에 성공하면 CAS는 사용자의 브라우저를 원래 서비스로 redirection 한다.
    redirect 시에 서비스 티켓을 의미 하는 'ticket' Parameter 를 함께 전달한다.
    예)https://my.application.com/login/cas?ticket=ST-0-ER94xMjmn6qha35CQRoz
  7. Application(client) 서버에서는 CasAuthenticationFilter 가 '/login/cas' 로의 요청을 처리하게 되어있다.
    처리 필터는 서비스 티켓임을 나타내는 UsernamePasswordAuthenticationToken을 생성한다. 이때 Authentication 의 Principal은 CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER("_cas_stateful_")가 되고  credentials은 CAS 인증 서버에서 받은 'ticket'(서비스티켓)이 값이 된다.
    이 인증 요청은 AuthenticationManager에 전달된다.
  8. AuthenticationManager의 구현체는 ProviderManager이며 AuthenticationProvider(CasAuthenticationProvider)로 차례로 구성된다.
    CasAuthenticationProvider는 CAS 고유의 주체인 CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER 및 CasAuthenticationToken(나중에 설명)을 포함하는 UsernamePasswordAuthenticationToken에만 응답한다.
  9. CasAuthenticationProviderTicketValidator의 구현체를 통해 서비스 티켓의 유효성을 검사한다. 일반적으로 CAS 클라이언트의 라이브러리에 포함 된 클래스 중 하나인 Cas20ServicetTicketValdator를 사용하는데, Application이 프록시 티켓의 유효성을 검사해야 하는 경우에는 Cas20ProxyTicketValidator가 사용된다.
    TicketValidator는 서비스 티켓의 유효성을 검사하기 위해 CAS 인증 서버에 HTTPS 요청을 보낸다.
    예)https://cas.server.com/p3/serviceValidate?ticket=ST-1-exZxTmzeBMMcLHd9sJm2-kkt0929&service=https://my.application.com/login/cas
  10. CAS 인증서버에는 유효성 검사 요청을 받게 되고, 요청에 포함된 서비스 티켓이 CAS 서버에서 발급한 서비스 URL과 일치하면 CAS는 사용자 이름을 포함하는 XML형식의 success 응답을 한다.
  11. Application의 Cas20TicketValidator는 CAS 서버에서 받은 XML을 분석한 뒤,
    사용자 이름(필수)을 포함한 TicketResponse(Assertion) 를 CasAuthenticationProvider로 리턴한다.
  12. CasAuthenticationProvider는 Assertion에 포함 된 사용자에게 적용되는 GrantedAuthority 를 가져오기 위해 AuthenticationUserDetilasService에 요청한다.
  13. 문제가 없는 경우 CasAuthenticationProvider는 TicketResponse 및 GrantedAuthority에 포함 된 세부 정보를 포함하는 CasAuthenticationToken을 생성한다.
  14. 그리고 CasAuthenticationFilter로 돌아가서 생성된 CasAuthenticationToken을 Security Context에 저장한다.
  15. 사용자의 브라우저는 AuthenticationException을 발생시켰던 원래 페이지로 redirection 된다.

 

위에 설명한 15가지 순서가 Spring Security와 Cas가 동작하는 순서이다.

각각의 설명은 Application 프로젝트에 대해 글을 작성 할 때 자세하게 다룰 것이다.

 

Spring Security와 CAS 를 사용한 SSO 에 대한 설명은 여기까지만 하고 

실제로 구현해보도록 하자.

+ Recent posts