주의) 이글은 미천한 영어 실력으로 인해 다음의 원문을 의역과 오역을 통해 작성된 글입니다. 거기에 주관적인 의견이 중간중간 섞여 있으므로 Shiro가 이러이러한 느낌이구나 정도만 참고해 주시면 감사하겠습니다.
누가 Shiro 소리를 내었는가? Shiro는 무엇인가 말이야
Apache Shiro는 인증, 권한관리, 암호화, 세션관리를 수행하는 강력한 Java Security(보안) 프레임워크입니다. Shiro는 일본어로 城(성,catle)을 뜻하며 '시로' 라고 발음됩니다. 즉 외부 침입으로부터 보호를 위해 만들어진 성이라는 의미를 담고 있는게 아닌가 싶습니다.
왜 일본어인 shiro를 채택했는지 확실하지는 않지만 Shiro의 개발자 대표의 취미가 일본어 공부임을 생각해볼때 어느정도 납득(?)이 되지 않나 싶습니다.
Apache Shiro의 등장 배경
shiro는 2003년 jSecurity 라는 프로젝트로 시작되어 2019년 현재 16년이라는 시간동안 발전해왔습니다. 2003년 당시에는 Java진영에 사용할만한 베스트 프랙티스가 없었으니 jSecurity 프로젝트는 JAVA 진영 개발자들에게는 반가운 소식이었을것입니다.
당시에 EJB라는 강력한 솔루션이 있었으나 EJB는 컨테이너에 종속적이기 때문에 모든 Java 응용프로그램의 솔루션이 될 수 없었을것입니다. 또한 당시에는 구글갓 같은 정보를 제공해줄만한 사이트가 없었기에 암호화 같은 고급 기술은 전문가가 아닌 이상 사용하기 힘들었을것입니다. 그러한 이유로 보안은 물론 암호화마저 깔끔한 API로 손쉽게 사용할 수 있도록 해주는 Shiro는 고마운 솔루션이 아닐 수 없습니다.
Shiro의 기능
shiro는 크게 다음의 기능을 제공합니다. 이는 어떤 어플리케이션에서든 대부분 공통적으로 요구하는 기능들입니다.
-
인증 - 보통 로그인 이라는 신원증명 기능을 제공합니다.
-
권한 부여 - 접근을 제어합니다.
-
암호화 - 중요한 데이터를 보호하거나 숨깁니다.
-
세션관리 - 사용자별 세션을 관리합니다.
Shiro의 장점
사용이 쉬움 - 최대한 API를 사용하기 쉽도록 설계(이건 누구 생각이야?)
유연성 - 특정 환경이나 컨테이너(톰캣같은..)에 종속되지 않습니다.
웹환경 지원 - 명령행에서 실행하거나 스윙을 이용하는 독립형 java 어플리케이션뿐만 아니라 웹환경도 지원합니다. (즉 JSP/Servlet 환경도 지원함)
지원 - Shiro는 꾸준히 업데이트 되고 있습니다.
확장성 - 공식 홈페이지에는 Pluggable이라고 되어 있는데, Spring 프레임웍은 물론 이 밖에도 여러가지 환경과 통합될 수 있도록 지원합니다.
Shiro에 핵심 개념 : Subject, SecurityManage, Realm
여기서부터는 Shiro 프레임워크에 대한 간략한 내용입니다. 제목에서 언급했듯이 Shiro에는 크게 중요한 세 가지 개념이 있으며 Java의 API답게 Subject, SecurityManage, Realm 이라는 인터페이스로 제공됩니다.
Subject
응용프로그램의 보안에서 가장 중요하면서도 흔하게 고려해야 할점은 "현재 응용프로그램에 접속된 사용자(주체)가 누구인가?" 일것입니다. Shiro에서는 Subject를 현재 shiro의 세계에 접속한 사용자를 나타내는 API로 사용합니다. Subject는 주제라고 해석할수도 있지만 어릴때 영어를 공부했다면 5형식을 외웠을 당시에 S는 subject를 뜻하며, 주어, 주체를 뜻한다라는 사실을 떠올릴 수 있습니다.
이는 Servlet/Jsp 환경에서 Session(현재 접속중인 주체인 브라우저) 와 비슷한 개념이라 할 수 있습니다. 비슷한 개념이라 말했지만 shiro를 웹 환경에서 사용하는 경우 shiro는 서블릿 컨테이너(예를 들어 톰캣)에서 제공하는 javax.servlet.HttpSession 인스턴스를 기반으로 Subject를 만듭니다. (물론 HttpSession = Subject 라는것이 아니라 Subject가 세션을 가지고 있는 개념입니다.)
코드1. Subject를 얻는 방법은 다음과 같습니다.
import org.apache.shiro.subject.Subject;
import org.apache.shiro.SecurityUtils;
...
Subject currentUser = SecurityUtils.getSubject(); |
cs |
Subject를 획득했다면 이제 이 Subject를 통해 로그인, 로그아웃을 수행함은 물론이고 세션 접근이나 권한 확인 등 Shiro에서의 거의 대두분의 정보를 얻을 수 있습니다. 또한 어플리케이션 코드 내에서는 어디서나 Subject를 얻어 보안과 관련된 작업을 수행할 수 있습니다.
SecurityManager
Subject가 현재 접속한 사용자에 대응되는 녀석이라면 SecurityManager는 어플리케이션에 접속해 있는 모든 사용자를 관리하는 역할을 합니다. Shiro의 핵심 기반이라 할 수 있겠습니다.
질문! 그럼 SecurityManager를 설정하는 방법은? 어쨌든 Spring을 사용하려면 Spring의 핵심인 DI/IOC 컨테이너를 설정해야 하는것과 마찬가지로 Shiro를 사용하려면 SecurityManager를 설정해야 합니다. 그러나 SecurityManager를 설정하는 방법은 어플리케이션의 환경에 따라 다릅니다. 예를 들어 웹 어플리케이션 환경이라면 spring에서 DispatcherServlet을 등록하듯이 Shiro ServletFilter를 web.xml에 등록해야 합니다.
독립형 java 어플리케이션이라면 다른 방법으로 구성해야 합니다. 또한 SecurityManager는 특별한 경우가 아니라면 어플리케이션 당 하나(singleton)만 생성하는것이 거의 대부분이며, Java 코드, Spring XML, YAML, .properties 파일 등)을 사용하여 구성할 수 있습니다.
보통 여러 환경에서 공통적으로 사용할 수 있는 INI(Initialization)파일을 사용할 수 있도록 제공합니다. INI파일은 단순 텍스트 파일이고 속성이름=값 형식이기 때문에 읽기에도 쉽고 특정 환경에 종속적이지 않기때문에 여러 환경에서 쉽게 사용할 수 있기 때문입니다.
예를 들어 아래와 같이 설정을 구성할 수 있습니다.
코드2. INI로 Shiro 설정 구성하기
[main]
cm = org.apache.shiro.authc.credential.HashedCredentialsMatcher
cm.hashAlgorithm = SHA-512
cm.hashIterations = 1024
# Base64 encoding (less text):
cm.storedCredentialsHexEncoded = false
iniRealm.credentialsMatcher = $cm
[users]
jdoe = TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJpcyByZWFzb2
asmith = IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbXNoZWQsIG5vdCB |
cs |
(이거 뭐 .properties랑 똑같은거 아냐? 라고 할수도 있겠지만 아주 조금 다릅니다. 예를 들어 [main] 섹션을 보면 '[ ]'는 섹션을 나타냅니다. 즉 속성들이 무엇을 의미하는지 세션으로 구분지을 수 있습니다는 차이가 있습니다.)
코드2는 SecurityManager 설정 예제입니다. [main] 섹션과 [users] 섹션이 있다는것을 눈치 채셨을것입니다.(그렇다고 믿고 있습니다)
일단 [main] 섹션은 SecurityManager 객체나 shiro 프레임웍에서 사용되는 여러가지 구성요소 개체(나중에 배우겠지만 Realm 같은)들을 구성하는 섹션입니다. 현재 코드2에는 두개의 개체만을 구성하고 있습니다.
코드2-1. 자세한 설명을 위해 코드2에서 [main] 섹션의 코드만 긁어왔습니다.
[main]
cm = org.apache.shiro.authc.credential.HashedCredentialsMatcher
cm.hashAlgorithm = SHA-512
cm.hashIterations = 1024
# Base64 encoding (less text):
cm.storedCredentialsHexEncoded = false |
cs |
자세히 보면 cm에 HashedCredentialsMatcher 개체를 할당하고 아래 설정들은 cm에 대한 설정들임을 알 수 있습니다. 마치 객체를 생성하고 setXXX() 메서드를 통해 값을 설정하는것 같다. 그렇다 shiro가 이 INI를 읽게 되면 실제 그런식으로 초기화를 진행합니다.
아직 끝이 아닙니다. [main] 섹션의 가장 아래를 봅니다.
iniRealm.credentialsMatcher = $cm |
cs |
iniRealm은 INI를 이용하여 SecurityManager가 동작할때 사용되는 개체입니다. 아무튼 INI를 이용한 SecurityManager의 credentialsMatcher에 앞서 설정해둔 HashedCredentialsMatcher를 할당하고 있습니다. $cm은 앞서 설정한 cm을 가리킵니다.(마치 c언어에서 포인터에 *을 붙일때와 안붙일때의 차이점같습니다.)
마지막으로 [users] 섹션은 그냥 테스트용 사용자 계정 정의입니다. jdoe, asmith 사용자를 정의했습니다. 값으로 할당된것은 암호입니다.
[users]
jdoe = TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJpcyByZWFzb2
asmith = IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbXNoZWQsIG5vdCB |
cs |
코드3. shiro.ini 설정파일 로딩하는 방법
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.util.Factory;
//1. Load the INI configuration (classpath상에 위치한 INI 설정파일 로드)
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//2. Create the SecurityManager (SecurityManager 생성)
SecurityManager securityManager = factory.getInstance();
//3. Make it accessible (SecurityManager에 접근 가능하도록 설정)
SecurityUtils.setSecurityManager(securityManager); |
cs |
코드3 예제는 ini 설정파일을 로드하고 로드한 설정파일을 이용해 SecurityManager에 설정하고 생성하는 3단계 프로세스입니다.
코드를 보면 알겠지만 팩토리 메서드패턴을 통해 생성합니다는 것을 알 수 있습니다.
팩토리 메서드 패턴을 이용하여 SecurityManager 인스턴스를 생성한 이유는... 앞서 설명했지만 디자인패턴이 이 글의 범위는 아니므로 모르겠다면 곰곰히 생각해보도록 합니다.
Realm
이제 마지막으로 세번째 핵심 개념인 Realm입니다. Realm은 shiro를 적용한 어플리케이션과 저장된 사용자 데이터 사이의 연결고리(너와 나의?) 역할을 하는 녀석입니다.
생각해봅시다. 로그인 기능을 구현하고자 합니다. 그럼 당연하게도 브라우저에서 입력한 정보(아디, 비번같은)와 실제 DB에서 조회하는 데이터가 일치하는지 검증해야합니다. 즉 DB에서 데이터를 조회해야 하는것입니다.
그러나 잘 생각해보면 꼭 DB가 대상이 되지는 않습니다. AD 연동인경우 AD서버가 대상이 되기도 하고 테라코타 같은 클러스터링 서버 캐쉬 내부의 정보가 조회 대상이 될 수 있습니다니다.
그러한 의미에서 Realm은 우리가 흔히 사용하는 로그인 전용 DAO 객체라 할 수 있습니다. 따라서 shiro는 여러가지 연동을 제공하기 위해 LDAP이나 ,JDBC, INI같은 파일 등 여러가지 데이터소스와 연동할 수 있는 Realm을 제공합니다. 당연하게도 Shiro 어플리케이션에서 Realm는 하나 이상 설정해주어야 합니다. 만약 원하는 Realm이 필요한 경우에는 직접 Realm를 구현하고 이를 등록하여 사용하면 됩니다.
코드4. LDAP을 연동하는 경우 INI 설정 예
[main]
ldapRealm = org.apache.shiro.realm.ldap.JndiLdapRealm
ldapRealm.userDnTemplate = uid={0},ou=users,dc=mycompany,dc=com
ldapRealm.contextFactory.url = ldap://ldapHost:389
ldapRealm.contextFactory.authenticationMechanism = DIGEST-MD5 |
cs |
전반적으로 Shiro에 대해 살펴보았다. 이제 shiro를 어떤식으로 사용하는지 간략한 개념을 알아보자
다음글
'자바[Java]' 카테고리의 다른 글
[Logback] Logback이란? log4J의 후속작 로그백(Logback) 살펴보기 및 비교 (0) | 2019.04.02 |
---|---|
[Apache Shiro] 아파치 시로 인증, 권한 관리 등의 기능에 대한 간략 개념 (2) | 2019.03.22 |
[JAVA] java에서 JSON 데이터 다루기. google의 json-simple 사용 방법 (5) | 2019.03.17 |
[JAVA] 메서드 오버라이딩(Method Overriding)시에 throws문 규칙에 대해 (1) | 2019.01.22 |
[JAVA] H2 DB에 JDBC 연결시 예외 unexpected status 16777216 또는 Could not load requested class 해결 방법 (3) | 2018.07.25 |