티스토리 뷰

728x90
반응형

웹 사이트 및 웹 애플리케이션 사용권한을 특정 클라이언트에게만 권한을 부여하고 싶을 때가 있습니다. 주로 많이 사용하는 방법 중 하나는 로그인을 도입하는 것이죠. 로그인을 할 때, 아이디와 패스워드만 있으면, 어떠한 클라이언트라도 웹 사이트 접근을 허용할 수 있습니다. 권한이 부여된 유저에게만 접근을 인가하는 방식을 보안(Security)라고 합니다. Spring에는 다양한 Security 모듈을 지원하는데, 그중 LDAP를 이용한 보안인증방법에 대하여 포스팅을 진행해보도록 하겠습니다.

Springboot LDAP용 프로젝트 다운로드

https://start.spring.io/ 에서 Spring Web 디펜던시만 추가하여, 프로젝트를 다운로드합니다.

다음으로, 보안을 적용하기 전에 앞서 간단한 문자열을 출력하는 웹 애플리케이션을 작성해보겠습니다.

웹 애플리케이션 작성하기

SpringBoot에서 Endpoint를 처리한 MVC Controller를 작성해보겠습니다. 아래 클래스에 작성된 컨트롤러는 사이트 URL에 접속하면 "Welcome to the home page!"를 출력하는 내용입니다. HomeController클래스 상단에 작성된 @RestController 어노테이션은 HTTP 응답 시, ResponseBody에 컨텍스트를 담아 반환하는 클래스임을 명시하는 것입니다. 여기서는 "/"경로에 접근할 경우, ResponseBody에 "Welcome to the home page!" 텍스트가 포함되어 응답합니다.

package com.example.authenticatingldap;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HomeController {

    @GetMapping("/")
    public String index() {
        return "Welcome to the home page!";
    }

}

HomeController를 실행할 Main클래스를 아래와 같이 작성합니다.

package com.example.authenticatingldap;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AuthenticatingLdapApplication {

   public static void main(String[] args) {
      SpringApplication.run(AuthenticatingLdapApplication.class, args);
   }

}

SpringBoot 애플리케이션을 실행하면 아래와 같이 실행 로그가 기록되고, localhost에 접근하면 앞서 작성한 문자열이 반환되는 것을 확인할 수 있습니다. 

localhost:8080에 접근 시, 접근이 인가된 유저에게만 문자열을 볼 수 있도록 LDAP를 이용하여 보안을 적용해보겠습니다.

LDAP를 이용한 보안 적용

LDAP란 Lightweight Directory Access Protocol의 약자이고, 경량 디렉터리 접근 프로토콜입니다. LDAP는 X.500이라는 Directory Access Protocol의 경량화된 버전이라고 불리며, TCP/IP Layer위에서 동작합니다. 그러므로, HTTP 프로토콜과 유사하게 Request, Reponse를 가지며, 프로토콜 Schema도 ldap://xxxx.xxxx.xx:port와 같은 형태로 URL이 정의되므로, HTTP와 유사한 점이 많습니다. HTTP는 익명의 누군가라도 Request를 전송하고, Response를 받아 정보를 받을 수 있는 반면, LDAP는 인증된 유저에 대해서만 Request에 대한 Response을 받을 수 있습니다. 그리고, HTTP은 평문(Plain Text)으로 전송하여, 패킷을 가로채면 누구나 해당 내용을 확인이 가능하지만, LDAP는 전송 데이터에 대한 Base64와 같은 인코딩이 적용되어 Binary 포맷으로 데이터를 전송합니다. 그러므로, HTTP보단 LDAP가 보안성이 더 좋겠죠? LDAP는 LDIF(LDAP Data Interchange Format) 데이터 포맷으로 데이터를 데이터베이스에 저장합니다. 데이터 저장 구성요소는 아래와 같습니다.

  • LDAP는 Tree 구조로 데이터를 저장함
  • Entry > Attribute > Type, Value > dn, cn, ou, dc, rdn (A > B는 A는 B의 부분집합)
  • Entry : Tree를 식별하는 단위
  • Attribute : Entry를 구성하는 노드 단위, Type과 Value로 구성됨
  • Type : dn, cn, ou, dc, rdn과 같은 Type이 존재함.
  • Value : Type에 대한 값을 나타냄

이러한 LDIF를 LDAP로 요청을 받고 처리하는 Directory Server가 있는데, 해당 서버에서는 데이터를 Tree 구조로 저장하므로 NoSQL의 일종으로도 볼 수 있습니다. LDAP도 HTTP Method와 같이 조회, 추가, 삭제, 수정과 같은 연산을 지원합니다. LDAP에 대해 자세히 알아보고 싶으신 분은 아래 링크 참조 바랍니다.

 

Basic LDAP Concepts

Before working with LDAP, there are a number of important concepts that should be understood. This page describes a number of important LDAP structures and ideas. Directory Servers A directory serv…

ldap.com

LDAP에 대한 개략적인 설명은 이것으로 마치고, 다시 본론으로 돌아와 SpringBoot에서 LDAP를 적용하는 방법에 대해 알아보겠습니다.

Build.gradle 파일에 LDAP사용을 위한 디펜던시를 추가해줍니다.

implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.ldap:spring-ldap-core")
implementation("org.springframework.security:spring-security-ldap")
implementation("com.unboundid:unboundid-ldapsdk")

다음으로, LDAP 설정을 위한 속성 설정 클래스 파일을 작성해줍니다.

package com.example.authenticatingldap;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .anyRequest().fullyAuthenticated()
                .and()
                .formLogin();
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .ldapAuthentication()
                .userDnPatterns("uid={0},ou=people")
                .groupSearchBase("ou=groups")
                .contextSource()
                .url("ldap://localhost:8389/dc=springframework,dc=org")
                .and()
                .passwordCompare()
                .passwordEncoder(new BCryptPasswordEncoder())
                .passwordAttribute("userPassword");
    }

}

WebSecurityConfigurerAdapter 클래스를 상속받아 LDAP를 설정합니다. SpringBoot에는 LDAP Server가 내장되어 있어, 별도로 LDAP 서버를 구축하고 생성하지 않아도 됩니다. 다음으로 웹 사이트에 접근 가능한 유저 데이터를 등록해보겠습니다. 우선 application.properties에 ldif 환경설정 내용을 작성합니다.

spring.ldap.embedded.ldif=classpath:test-server.ldif
spring.ldap.embedded.base-dn=dc=springframework,dc=org
spring.ldap.embedded.port=8389

유저 정보는 src/main/resources/test-server.ldif 에 저장합니다.

dn: dc=springframework,dc=org
objectclass: top
objectclass: domain
objectclass: extensibleObject
dc: springframework

dn: ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: groups

dn: ou=subgroups,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: subgroups

dn: ou=people,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: people

dn: ou=space cadets,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: space cadets

dn: ou=\"quoted people\",dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: "quoted people"

dn: ou=otherpeople,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: otherpeople

dn: uid=ben,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Ben Alex
sn: Alex
uid: ben
userPassword: $2a$10$c6bSeWPhg06xB1lvmaWNNe4NROmZiSpYhlocU/98HNr2MhIOiSt36

dn: uid=bob,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Bob Hamilton
sn: Hamilton
uid: bob
userPassword: bobspassword

dn: uid=joe,ou=otherpeople,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Joe Smeth
sn: Smeth
uid: joe
userPassword: joespassword

dn: cn=mouse\, jerry,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Mouse, Jerry
sn: Mouse
uid: jerry
userPassword: jerryspassword

dn: cn=slash/guy,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: slash/guy
sn: Slash
uid: slashguy
userPassword: slashguyspassword

dn: cn=quote\"guy,ou=\"quoted people\",dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: quote\"guy
sn: Quote
uid: quoteguy
userPassword: quoteguyspassword

dn: uid=space cadet,ou=space cadets,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Space Cadet
sn: Cadet
uid: space cadet
userPassword: spacecadetspassword



dn: cn=developers,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfUniqueNames
cn: developers
ou: developer
uniqueMember: uid=ben,ou=people,dc=springframework,dc=org
uniqueMember: uid=bob,ou=people,dc=springframework,dc=org

dn: cn=managers,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfUniqueNames
cn: managers
ou: manager
uniqueMember: uid=ben,ou=people,dc=springframework,dc=org
uniqueMember: cn=mouse\, jerry,ou=people,dc=springframework,dc=org

dn: cn=submanagers,ou=subgroups,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfUniqueNames
cn: submanagers
ou: submanager
uniqueMember: uid=ben,ou=people,dc=springframework,dc=org

http://localhost:8080에 접속하면, LDAP 인증 사이트로 리다이렉션 되어, 로그인을 수행해야 합니다.

Springboot에 내장된 LDAP 서버에서 출력된 인증 화면

WebSecurityConfig에서 passwordEncryt를 BCrypt방식을 사용하도록 되어 있어, test-server.ldif 파일 중 uid가 ben이고 userPassword 항목이 인코딩 된 항목이 있는데, 이는 디코딩하면 benspassword이고, LDAP 인증 사이트에서 ID는 ben, Password를 benspassword를 입력하면 최초에 보았던 welcom to the home page! 문구가 정상적으로 출력됩니다. 이로서, 웹 애플리케이션에 LDAP 보안을 적용하였습니다.

이상으로 SpringBoot에서 LDAP 보안 적용하는 법에 대해 알아보았습니다.

728x90
반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함