1. 소개
이 사용방법(예제)에서는 Apache Shiro Java Security 프레임워크를 사용하여 세분화된 권한 기반 액세스 제어를 구현하는 방법을 살펴보겠습니다 .
2. 설정
우리는 Shiro에 대한 소개와 동일한 설정을 사용할 것입니다. 즉, 의존성에 shiro-core 모듈만 추가할 것입니다.
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.1</version>
</dependency>
또한 테스트 목적으로 클래스 경로의 루트에 다음 shiro.ini 파일 을 배치하여 간단한 INI Realm을 사용합니다 .
[users]
jane.admin = password, admin
john.editor = password2, editor
zoe.author = password3, author
[roles]
admin = *
editor = articles:*
author = articles:create, articles:edit
그런 다음 위의 영역으로 Shiro를 초기화합니다.
IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
SecurityManager securityManager = new DefaultSecurityManager(iniRealm);
SecurityUtils.setSecurityManager(securityManager);
3. 역할 및 권한
일반적으로 인증 및 권한 부여에 대해 이야기할 때 사용자 및 역할의 개념에 중점을 둡니다.
특히 역할은 애플리케이션 또는 서비스 사용자의 교차 클래스입니다. 따라서 특정 역할을 가진 모든 사용자는 일부 리소스 및 작업에 액세스할 수 있으며 애플리케이션 또는 서비스의 다른 부분에 대한 액세스가 제한될 수 있습니다.
역할 집합은 일반적으로 사전에 설계되며 새로운 비즈니스 요구 사항을 수용하기 위해 거의 변경되지 않습니다. 그러나 역할은 예를 들어 관리자가 동적으로 정의할 수도 있습니다.
Shiro에서는 사용자에게 특정 역할이 있는지 테스트하는 여러 가지 방법이 있습니다. 가장 간단한 방법은 hasRole 메소드를 사용하는 것입니다.
Subject subject = SecurityUtils.getSubject();
if (subject.hasRole("admin")) {
logger.info("Welcome Admin");
}
3.1. 권한
그러나 사용자에게 특정 역할이 있는지 테스트하여 권한 부여를 확인하면 문제가 있습니다. 사실 우리는 역할과 권한 사이의 관계를 하드코딩하고 있습니다. 즉, 리소스에 대한 액세스 권한을 부여하거나 취소하려면 소스 코드를 변경해야 합니다. 물론 이는 재구축 및 재배포를 의미하기도 합니다.
우리는 더 잘할 수 있습니다. 이것이 바로 권한의 개념을 소개하는 이유입니다. 권한은 누가 할 수 있는지가 아니라 우리가 승인하거나 거부 할 수 있는 소프트웨어가 할 수 있는 것을 나타냅니다 . 예를 들어 "현재 사용자 프로필 편집", "문서 승인" 또는 "새 문서 만들기"입니다.
Shiro는 권한에 대해 거의 가정하지 않습니다. 가장 간단한 경우 권한은 일반 문자열입니다.
Subject subject = SecurityUtils.getSubject();
if (subject.isPermitted("articles:create")) {
//Create a new article
}
권한 사용은 Shiro에서 전적으로 선택 사항입니다 .
3.2. 사용자에게 권한 연결
Shiro는 권한을 역할 또는 개별 사용자와 연결하는 유연한 모델을 가지고 있습니다. 그러나 이 사용방법(예제)에서 사용하는 단순 INI 영역을 비롯한 일반적인 영역은 역할에 대한 권한만 연결합니다.
따라서 Principal 로 식별되는 사용자는 여러 역할을 가지며 각 역할에는 여러 권한 이 있습니다.
예를 들어, 우리는 INI 파일에서 사용자 zoe.author가 작성자 역할을 가지고 있으며 기사:생성 및 기사:편집 권한을 부여하는 것을 볼 수 있습니다.
[users]
zoe.author = password3, author
#Other users...
[roles]
author = articles:create, articles:edit
#Other roles...
마찬가지로 다른 영역 유형(예: 기본 제공 JDBC 영역)은 권한을 역할에 연결하도록 구성할 수 있습니다.
4. 와일드카드 권한
Shiro에서 권한의 기본 구현은 다양한 권한 체계에 대한 유연한 표현인 와일드카드 권한입니다.
문자열로 Shiro에서 와일드카드 권한을 나타냅니다. 권한 문자열은 다음과 같이 콜론으로 구분된 하나 이상의 구성 요소로 구성됩니다.
articles:edit:1
문자열의 각 부분의 의미는 애플리케이션에 따라 다릅니다. Shiro는 어떤 규칙도 적용하지 않기 때문입니다. 그러나 위의 예에서 문자열을 계층 구조로 명확하게 해석할 수 있습니다.
- 우리가 노출하는 리소스 클래스(기사)
- 그러한 자원에 대한 조치(편집)
- 작업을 허용하거나 거부하려는 특정 리소스의 ID
이 3계층 구조의 resource:action:id는 Shiro 애플리케이션의 일반적인 패턴입니다. 간단하면서도 다양한 시나리오를 효과적으로 표현할 수 있기 때문입니다.
따라서 이전 예제를 다시 방문하여 이 체계를 따를 수 있습니다.
Subject subject = SecurityUtils.getSubject();
if (subject.isPermitted("articles:edit:123")) {
//Edit article with id 123
}
와일드카드 권한 문자열의 구성 요소 수 는 일반적으로 3개 구성 요소이지만 반드시 3개일 필요는 없습니다.
4.1. 권한 의미 및 인스턴스 수준 세분성
와일드카드 권한은 Shiro 권한의 또 다른 기능인 의미와 결합할 때 빛을 발합니다.
역할을 테스트할 때 정확한 멤버십을 테스트합니다. 주제에 특정 역할이 있는지 여부입니다. 즉, Shiro는 평등을 위해 역할을 테스트합니다.
반면에 권한을 테스트할 때 함축성을 테스트합니다. Subject 의 권한이 테스트 대상 권한을 의미합니까 ?
구체적으로 의미하는 바는 권한의 구현에 따라 다릅니다. 실제로 와일드카드 권한의 경우 이름에서 알 수 있듯이 와일드 구성 요소의 가능성이 있는 부분 문자열 일치를 의미합니다.
따라서 작성자 역할 에 다음 권한을 할당한다고 가정해 보겠습니다 .
[roles]
author = articles:*
그런 다음 작성자 역할을 가진 모든 사용자는 문서에 대해 가능한 모든 작업을 수행할 수 있습니다.
Subject subject = SecurityUtils.getSubject();
if (subject.isPermitted("articles:create")) {
//Create a new article
}
즉, 문자열 items:*는 첫 번째 구성 요소가 기사인 모든 와일드카드 권한과 일치합니다 .
이 체계를 사용하면 매우 구체적인 권한(주어진 ID가 있는 특정 리소스에 대한 특정 작업) 또는 기사를 편집하거나 기사에 대한 작업을 수행하는 것과 같은 광범위한 권한을 할당할 수 있습니다.
물론 성능상의 이유로 단순한 동등성 비교가 아니기 때문에 항상 가장 구체적인 권한에 대해 테스트해야 합니다 .
if (subject.isPermitted("articles:edit:1")) { //Better than "articles:*"
//Edit article
}
5. 사용자 지정 권한 구현
권한 사용자 지정에 대해 간단히 살펴보겠습니다. 와일드카드 권한은 광범위한 시나리오를 포괄하지만 응용 프로그램에 맞게 사용자 정의된 솔루션으로 대체할 수 있습니다.
경로에 대한 권한이 모든 하위 경로에 대한 권한을 의미 하도록 경로에 대한 권한을 모델링해야 한다고 가정합니다 . 실제로 작업에 대해 와일드카드 권한을 사용할 수 있지만 무시하겠습니다.
그래서 우리에게 필요한 것은 무엇입니까?
- 권한 구현 _
- 시로에게 그 사실을 알리기 위해
두 가지 포인트를 모두 달성하는 방법을 살펴보겠습니다.
5.1. 권한 구현 작성
Permission 구현 은 단일 메서드가 있는 클래스입니다 — 다음을 의미합니다 .
public class PathPermission implements Permission {
private final Path path;
public PathPermission(Path path) {
this.path = path;
}
@Override
public boolean implies(Permission p) {
if(p instanceof PathPermission) {
return ((PathPermission) p).path.startsWith(path);
}
return false;
}
}
메서드는 이것이 다른 권한 개체를 암시하는 경우 true를 반환하고 그렇지 않은 경우 false를 반환합니다 .
5.2. 구현에 대해 Shiro에게 알리기
그런 다음 Permission 구현을 Shiro에 통합하는 다양한 방법이 있지만 가장 간단한 방법은 사용자 정의 PermissionResolver를 Realm 에 주입하는 것입니다 .
IniRealm realm = new IniRealm();
Ini ini = Ini.fromResourcePath(Main.class.getResource("/com/.../shiro.ini").getPath());
realm.setIni(ini);
realm.setPermissionResolver(new PathPermissionResolver());
realm.init();
SecurityManager securityManager = new DefaultSecurityManager(realm);
PermissionResolver는 권한 의 문자열 표현을 실제 Permission 객체 로 변환하는 역할을 합니다 .
public class PathPermissionResolver implements PermissionResolver {
@Override
public Permission resolvePermission(String permissionString) {
return new PathPermission(Paths.get(permissionString));
}
}
경로 기반 권한으로 이전 shiro.ini를 수정해야 합니다 .
[roles]
admin = /
editor = /articles
author = /articles/drafts
그런 다음 경로에 대한 권한을 확인할 수 있습니다.
if(currentUser.isPermitted("/articles/drafts/new-article")) {
log.info("You can access articles");
}
여기서는 간단한 영역을 프로그래밍 방식으로 구성하고 있습니다. 일반적인 애플리케이션에서는 shiro.ini 파일이나 Spring과 같은 다른 수단을 사용하여 Shiro와 영역을 구성합니다. 실제 shiro.ini 파일에는 다음이 포함될 수 있습니다.
[main]
permissionResolver = com.baeldung.shiro.permissions.custom.PathPermissionResolver
dataSource = org.apache.shiro.jndi.JndiObjectFactory
dataSource.resourceName = java://app/jdbc/myDataSource
jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource = $dataSource
jdbcRealm.permissionResolver = $permissionResolver
6. 결론
이 기사에서는 Apache Shiro가 권한 기반 액세스 제어를 구현하는 방법을 검토했습니다.
항상 그렇듯이 이러한 모든 예제와 코드 스니펫의 구현은 GitHub 에서 사용할 수 있습니다 .