Shiro는 광범위한 응용 프로그램을 갖춘 가벼운 권한 제어 프레임 워크입니다. 이 기사의 초점은 Spring의 Shiro 통합을 소개하고 Spring의 EL 표현을 확장하여 @requiresroles와 같은 동적 매개 변수를 지원하는 것입니다. 시로에 대한 소개는이 기사의 범위 내에 있지 않습니다. 독자가 Shiro에 대해 많이 알지 못하면 공식 웹 사이트를 통해 해당 정보를 배울 수 있습니다. Shiro에 대한 포괄적 인 소개를 제공하는 InfoQ에 대한 기사가 있으며 공식적으로 권장됩니다. 주소는 https://www.infoq.com/articles/apache-shiro입니다.
Shiro는 봄을 통합합니다
먼저 프로젝트에 Shiro-Spring-XXX.jar를 추가해야합니다. Maven을 사용하여 프로젝트를 관리하는 경우 종속성에 다음 종속성을 추가 할 수 있습니다. 다음은 내가 선택한 최신 버전 1.4.0입니다.
<pectionency> <groupid> org.apache.shiro </groupid> <artifactid> Shiro-spring </artifactid> <버전> 1.4.0 </version> </fectionency>
다음으로 web.xml에서 shirofilter를 정의하고 일반적으로 /*로 구성된 권한 제어가 필요한 모든 요청을 가로 채려면 적용해야합니다. 또한, 필터를 전면에 추가하여 요청이 시작된 후 Shiro의 권한을 통해 먼저 제어되도록해야합니다. 여기서 해당 필터 클래스는 Spring에서 제공하는 필터 프록시 인 위임 필터 프록시로 구성됩니다. 스프링 콩 컨테이너의 콩을 현재 필터 인스턴스로 사용할 수 있으며 해당 콩은 필터 이름에 해당하는 콩을 가져갑니다. 따라서 다음 구성은 Bean 용기에 Shirofilter라는 콩을 찾습니다.
<filter> <filter-name> shirofilter </filter-name> <filter-class> org.springframework.web.filter.delegatingFilterProxy </filter-class> <Aten-param> <Param-name> TargetFilterLifeCycle </param-name> <Param-Value> true </param-value> </init-param> </filter> <filter-mapping> <filter-name> shirofilter </filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
Shiro를 독립적으로 사용할 때는 일반적으로 org.apache.shiro.web.servlet.shirofilter를 정의하여 비슷한 일을합니다.
다음은 Bean 용기에있는 Shirofilter를 정의하는 것입니다. 다음과 같이, 우리는 shirofilterfactorybean을 정의하여 AbstractShirofilter 유형 Bean을 생성합니다. shirofilterfactorybean을 통해 SecurityManager를 지정할 수 있습니다. 여기에 사용 된 DefaultWebsecurityManager는 영역을 지정해야합니다. 여러 영역을 지정 해야하는 경우 영역을 통해 지정됩니다. 간단하게하기 위해 텍스트 정의를 기반으로 텍스트 콘피트 레알름을 직접 사용합니다. LoginUrl을 사용하여 로그인 주소를 지정하고, 성공한 후에 리디렉션 해야하는 주소를 지정하려면 성공을 거부하고 권한이 충분하지 않은 경우 프롬프트 페이지를 지정하려면 무단으로 표시되지 않습니다. FilterChainDefinitions는 사용될 URL과 필터 간의 관계를 정의합니다. 동일 부호의 오른쪽에있는 필터 별칭은 필터 별명입니다. 기본 별칭은 열거 클래스 org.apache.shiro.web.filter.mgt.defaultFilter에 정의됩니다.
<bean id = "shirofilter"> <property name = "securitymanager"ref = "securityManager"/> <속성 이름 = "loginUrl"value = "/login.jsp"/> <property name = "successUl"value = "/home.jsp"/> <속성 이름 = "unauthorizedUrl"value = "/unauthorized.jsp"/>. value = "/unauthorized.jsp"/> <property name = "FilterChainDefinitions"> <value>/admin/** = authc, 역할 [admin]/logout = logout # 기타 주소는 사용자가 로그인 한/** = authc, authc, authc, <bean id = "beak name"> ref = "realm"/> </bean> <bean id = "lifecyclebeanpostprocessor"/>
<!-단순화를 위해 여기에서 텍스트 기반 영역 구현을 여기에서 사용합니다-> <bean id = "realm"> <property name = "userDefinitions"> <value> user1 = pass1, role1, role2 user2 = pass2, role2, role3 admin, admin </value> </property> </bean>
FilterChainDefinitions 정의에서 사용자 정의 필터를 사용해야하는 경우 ShirofilterFactoryBean의 필터를 통해 사용자 정의 필터 및 별칭 매핑 관계를 지정할 수 있습니다. 예를 들어, 아래와 같이, 우리는 alias logger가있는 필터를 추가하고 FilterChainDefinitions에서 별명 로거가있는 /** 필터를 지정했습니다.
<bean id = "shirofilter"> <property name = "securitymanager"ref = "securityManager"/> <속성 이름 = "loginUrl"value = "/login.jsp"/> <property name = "successUl"value = "/home.jsp"/> <속성 이름 = "unauthorizedUrl"value = "/unauthorized.jsp"/>. value = "/unauthorized.jsp"/> <property name = "필터"> util : map> <Entry key = "logger"> <bean/> </enterd> </util : map> </property> <property name = "filterchainDefinitions"> <value>/admin/** = authc, rologe = guder home worded on the ourseed on the ousted on word on word on the ourseed in the ourseed in </value> </property> </bean>
실제로, 우리가 적용 해야하는 필터 별명 정의는 shirofilterfactorybean의 setfilters ()에 의해 직접 정의 될 수 있지만 해당 콩 컨테이너에서 해당 필터 대응 콩을 직접 정의합니다. 기본적으로 ShirofilterFactoryBean은 필터에 ID 별칭이있는 콩 컨테이너에 모든 필터 유형 Bean을 등록합니다. 따라서 위의 정의는 다음과 같습니다.
<bean id = "shirofilter"> <property name = "securitymanager"ref = "securityManager"/> <속성 이름 = "loginUrl"value = "/login.jsp"/> <property name = "successUl"value = "/home.jsp"/> <속성 이름 = "unauthorizedUrl"value = "/unauthorized.jsp"/>. value = "/unauthorized.jsp"/> <속성 이름 = "FilterChainDefinitions"> <value>/admin/** = authc, 역할 [admin]/logout = logout # 기타 주소는 사용자가 로그인 한/** authc, authc, authc, <bean id = "logger"/>
위의 단계 후에는 Shiro와 Spring의 통합이 완료됩니다. 현재 프로젝트에 요청한 모든 경로는 로그인해야하며 LoginUrl이 지정된 경로로 자동으로 점프하고 로그인하기 위해 사용자 이름/비밀번호를 입력 할 수 있습니다. 현재 로그인을 통해 사용자 이름, 비밀번호를 통해 비밀번호를 통해 사용자 이름을 얻을 수있는 양식을 제공해야합니다. 그런 다음 Login 요청을 제출할 때 Loginurl이 지정된 주소를 제출해야합니다. 로그인 할 때 사용되는 사용자 이름/비밀번호는 TextConfigurationRealm에서 정의한 사용자 이름/비밀번호입니다. 위의 구성을 기반으로, 사용자 1/pass1, admin/admin 등을 사용할 수 있습니다. 성공적으로 로그인 한 후에는 SuccessUrl 매개 변수로 지정된 주소로 이동합니다. user1/pass1을 사용하여 로그인하면/admin/index에 액세스하려고 시도 할 수 있으며 현재 권한이 충분하지 않아서 승인되지 않은.jsp로 이동합니다.
주석 기반 지원을 활성화하십시오
기본 통합을 위해서는 URL이 ShirofilterFactoryBean의 FilterChainDefinition에 적용 해야하는 모든 권한 컨트롤을 정의해야합니다. 이것은 때때로 유연하지 않습니다. Shiro는 스프링을 통합 한 후 사용할 수있는 주석을 제공합니다. 클래스 또는 메소드에 액세스하는 데 필요한 권한을 정의하기 위해 권한 제어가 필요한 클래스 또는 메소드에 해당 주석을 추가 할 수 있습니다. 정의에서 클래스에있는 경우 클래스에서 모든 메소드를 호출하려면 해당 권한이 필요하다는 것을 의미합니다 (동적 대리의 한계 인 외부 호출이어야합니다). 이러한 주석을 사용하려면 Spring 's Bean 컨테이너에 다음 두 Bean 정의를 추가하여 사용자가 런타임에 주석 정의를 기반으로 해당 권한을 가지고 있는지 여부를 결정할 수 있습니다. 이것은 Spring의 AOP 메커니즘을 통해 달성됩니다. Spring AOP에 대해 아무것도 모른다면 저자가 작성한 저자의 "Spring AOP 소개 열"을 참조하십시오. 다음 두 Bean 정의 인 AuthorizationAttributesourceAdvisor는 고문을 정의하여 Shiro가 제공 한 주석 구성 방법을 기반으로 권한을 가로 채고 확인합니다. DefaultAdvisorAutoProxyCreator는 Shiro가 제공하는 권한 제어 주석이 표시된 클래스에 대한 프록시 객체를 작성하고 대상 메소드 호출을 가로 채울 때 인증 ATTRIBUTESOURCEADVISOR를 적용하는 기능을 제공합니다. 사용자의 요청이 가로 채고 사용자가 해당 메소드 또는 클래스에 표시된 권한이 없으면 org.apache.shiro.authz.authorizationException 예외가 발생합니다.
<bean expends-on = "LifeCycleBeanPostProcessor"/> <ean> <속성 이름 = "SecurityManager"ref = "SecurityManager"// </bean>
<aop:config/>或<aop:aspectj-autoproxy/> 이미 Bean 컨테이너에 정의되어 있다면 DefaultAdvisorAutoProxyCreator를 더 이상 정의 할 수 없습니다. 이전 두 경우는 DefaultAdvisorAutoProxyCreator와 유사한 Bean을 자동으로 추가하기 때문입니다. DefaultAdvisorAutoProxyCreator에 대한 자세한 내용은 스프링 AOP에서 자동으로 프록시 객체를 작성하는 저자의 원칙을 참조 할 수 있습니다.
Shiro가 제공하는 권한 제어 주석은 다음과 같습니다.
요구 사항 : 사용자는 현재 세션에서 인증되어야합니다. 즉, 사용자 이름/비밀번호로 로그인해야하며 RememberME 자동 로그인을 포함하지 않습니다.
요구 사항 : 사용자는 인증해야합니다. 이 세션에서 사용자 이름/비밀번호로 로그인하여 인증 할 수 있거나 RememberME로 자동 로그인 할 수 있습니다.
요구 사항 : 사용자는 로그인하지 않습니다.
요구 사항 : 사용자는 지정된 역할을 소유해야합니다.
요구 사항 관리 : 사용자는 지정된 권한이 필요합니다.
처음 세 가지는 이해하기 쉽고 마지막 두 개는 비슷합니다. 여기서 @requirespermissions를 예로 사용합니다. 먼저 위에서 정의 된 영역을 변경하고 역할에 권한을 추가합시다. 이러한 방식으로, 우리의 user1은 perm1, perm2 및 perm3에 대한 권한이 있으며, user2는 perm1, perm3 및 perm4에 대한 권한이 있습니다.
<bean id = "realm"> <property name = "userDefinitions"> <value> user1 = pass1, roble1, role2 user2 = pass2, role2, roble3 admin = admin, admin, admin </value> </property> <속성 이름 = "roledefinitions"> <balue> role> roby1 = perm2 robol1, perm3 robol3, perm4 </value> </speratial> </speraty>
@RequiresPermissions는 메소드를 호출 할 때 청구 해야하는 권한을 지정하기 위해 메소드에 추가 할 수 있습니다. 다음 코드에서는 /perm1에 액세스 할 때 PERM1의 권한을 소유해야합니다. 현재 사용자 1 및 user2에 모두 액세스 할 수 있습니다.
@requestmapping ( "/erm1")@requirepermissions ( "perm1") public object resment1 () {return "perm1";}메소드에 액세스하려면 여러 권한이 동시에 있어야한다는 것을 지정 해야하는 경우 배열 형식으로 지정 해야하는 권한을 지정할 수 있습니다 (주석에 단일 배열 속성을 지정할 때 교정기를 추가 할 수는 없지만 여러 권한을 지정해야 할 때는 브레이스가 필요합니다). 예를 들어, 다음과 같이 /erm1andperm4에 액세스 할 때 사용자는 Perm1 및 Perm4 권한이 있어야합니다. 이 시점에서는 user2만이 액세스 할 수 있습니다. 동시에 Perm1과 Perm4 만 있으므로 액세스 할 수 있습니다.
@RequestMapping ( "/perm1andperm4")@requirepermissions ({ "perm1", "perm4"}) public object perm1andperm4 () {return "perm1andperm4";}다중 권한이 동시에 지정되면 여러 권한 사이의 관계가 관계, 즉 동시에 지정된 모든 권한이 필요합니다. 액세스 할 수있는 지정된 다중 권한 중 하나만 있어야하는 경우 Logical = Logical.or를 통해 여러 권한 간의 관계를 지정할 수 있습니다. 예를 들어, 다음과 같이 /erm1oRperm4에 액세스 할 때는 user1 및 user2 모두이 메소드에 액세스 할 수 있도록 Perm1 또는 Perm4 권한 만 있으면됩니다.
@RequestMapping ( "/perm1oRperm4")@requireSperMissions (value = { "perm1", "perm4"}, logical = logical.or) public object perm1orperm4 () {return "perm1orperm4";}@requirespermissions는 클래스에 표시 될 수 있으며, 외부에서 클래스에서 메소드에 액세스 할 때 해당 권한이 필요하다는 것을 나타냅니다. 예를 들어, 다음에서는 클래스 수준에서 허가 PERM2가 필요하다는 것을 지정하지만 index () 메소드는 권한이 필요하다는 것을 지정하지 않지만이 메소드에 액세스 할 때 클래스 레벨에 지정된 권한이 있어야합니다. 현재 사용자 1 만 액세스 할 수 있습니다.
@restcontroller@requestmapping ( "/foo")@requestpermissions ( "perm2") public class foocontroller {@requestmapping (method = requestmapping (method = requestmapping.get) public object index () {map <string, object> map = new Hashmap <> (); Map.put ( "ABC", 123); 리턴 맵; }}클래스 및 메소드 레벨이 모두 @requirespermissions를 갖는 경우, 메소드 레벨은 우선 순위가 높으며 현재 메소드 레벨에 필요한 권한 만 확인됩니다. 다음과 같이, 우리는 클래스 수준에서 PERM2 권한이 필요하고 메소드 수준에서 PERM3 권한이 필요하다는 것을 지정합니다. 그런 다음 /foo에 액세스 할 때는 index () 메소드에 액세스하기 위해서는 Perm3 권한 만 있으면됩니다. 따라서 현재 user1 및 user2는 모두 액세스 /foo에 액세스 할 수 있습니다.
@restController @requestMapping ( "/foo") @requessPermissions ( "perm2") public class foocontroller {@requestmapping (@requestmapping ( @requestmapping) @requirespermissions ( "perm3") public object () {map <string, object> map = new hashmap <> (); Map.put ( "ABC", 123); 리턴 맵; }}그러나 현재 클래스에 @RequiresRoles ( "Role1")를 추가하여 역할 1을 사용해야한다는 것을 지정한 다음 /foo에 액세스 할 때 @requirespermissions ( "Perm3")가 Classe의 Index () 메소드에서 지정된 역할 1이 있어야합니다. 요구 사항과 요구 사항이 다른 차원의 권한 정의에 속하기 때문에 Shiro는 검증 중에 한 번 확인하지만 클래스와 메소드가 동일한 유형의 권한 제어 정의에 대한 주석이있는 경우 방법의 정의는 정의에 기초합니다.
@restController ( "/foo")@requessPermissions ( "perm2")@requessRoles ( "role1") 공개 클래스 foocontroller {@requestmapping (method = requestmethod.get) @requirespermissions ( "perm3") public object index () {map <string, object> map = new Hashmap <(); Map.put ( "ABC", 123); 리턴 맵; }}예제는 요구 사항 만 사용하지만 다른 권한 제어 주석의 사용도 비슷합니다. 관심있는 친구들의 다른 주석을 사용하십시오.
주석 제어 권한의 원리
위에서 지정한 권한은 @requirespermissions를 사용하여 정적입니다. 이 기사의 주요 목적 중 하나는 구현을 확장하여 지정된 권한을 동적으로 만드는 방법을 소개하는 것입니다. 그러나 확장하기 전에 확장하기 전에 어떻게 작동하는지, 즉 구현 원칙을 알아야합니다. Shiro가 Spring을 @requirespermissions와 어떻게 통합하는지 살펴 보겠습니다. @requirespermissions를 지원할 때 고문 인 다음 Bean을 정의합니다. 고문은 정적 메드 메드 메이치 포인트 Cutadvisor에서 상속됩니다. 메소드 일치 로직은 클래스 또는 메소드에 Shiro의 몇 가지 권한 제어 주석이있는 한, 가로 채기 후 프로세싱 논리는 해당 조언에 의해 지정된다는 것입니다.
<ean> <속성 이름 = "SecurityManager"ref = "SecurityManager"/> </bean>
다음은 AuthorizationAttributeSourceadvisor의 소스 코드입니다. 생성자 방법에서 AopallianceannotationsauthorizingMethodinterceptor는 MethodInterceptor의 구현을 기반으로하는 SetAdvice ()에 의해 지정됨을 알 수 있습니다.
공개 클래스 권한 AWSTRIBUTESOURCEADVISOR EXTENDS STATICEDODMATCHERPOINGCUTADVISOR {개인 정적 최종 로거 로그 = loggerFactory.getLogger (upportizationAttributeSourceAdvisor.class); 개인 정적 최종 클래스 <? 주석을 확장합니다> [] authz_annotation_classes = new class [] {requireSpermissions.class, requessRoles.class, requessUser.class, requireguest.class, requireAuthentication.class}; 보호 된 SecurityManager SecurityManager = NULL; public AuthorizationAttributesourceadvisor () {setAdvice (new aopallianceannotationsauthorizingMethodinterceptor ()); } public SecurityManager getSecurityManager () {return SecurityManager; } public void setsecurityManager (org.apache.shiro.mgt.securityManager SecurityManager) {this.securityManager = SecurityManager; } public boolean matches (메소드 메소드, 클래스 targetclass) {메소드 m = 메소드; if (isauthzannotationpresent (m)) {return true; } // '메소드'매개 변수는 주석이없는 인터페이스에서 나올 수 있습니다. // 구현에이를 가지고 있는지 확인하십시오. if (targetClass! = null) {try {m = targetclass.getMethod (m.getName (), M.GetParameterTypes ()); 반환 IsauthzAnnotationPresent (M) || isauthzannotationpresent (target class); } catch (nosuchmethodexception 무시) {// 기본 반환 값은 false입니다. 메소드를 찾을 수 없다면 분명히 // 주석이 없으므로 기본 리턴 값을 사용하십시오. }} 거짓을 반환합니다. } private boolean isauthzAnnotationPresent (class <?> targetClazz) {for (class <? extends annotation> annclass : authz_annotation_classes) {annotation a = AnnotationUtils.FindAntation (TargetClazz, Annclass); if (a! = null) {return true; }} 거짓을 반환합니다. } private boolean isauthzannotationPresent (method method) {for (class <? extends annotation> annclass : authz_annotation_classes) {annotation a = annotationUtils.findAntation (method, annclass); if (a! = null) {return true; }} 거짓을 반환합니다. }}aopallianceannotationsauthorizingmethodinterceptor의 소스 코드는 다음과 같습니다. IT에서 구현 된 메소드 인턴 셉터 인터페이스의 호출 메소드는 상위 클래스의 호출 메소드를 호출합니다. 동시에, 우리는 일부 승인 메노 테이션 메토드 interceptor 구현이 생성자 방법으로 생성되었음을 확인해야합니다. 이러한 구현은 권한 제어 구현의 핵심입니다. 나중에, 우리는 특정 구현 로직을보기 위해 ApermissionAntationMethodinterceptor 구현 클래스를 선택합니다.
공개 클래스 AOPALLIANCEANNOTATIONSAUSTORIZINGMETHODINGERCEPTOR는 주석을 확장합니다. 메소드 interceptor {public aopallianceannotationsauxtorizing -methodinterceptor () {list <successizingMethodinterceptor> interceptor = new arraylist <새로운 어레이리스트 <인증 interceptor interceptor (5); // Spring -Specific Annotation Resolver 사용 - Spring의 AnnotationUtils는 // raw jdk 해상도 프로세스보다 좋습니다. AnnotationResolver Resolver = 새로운 SpringAnnotationResolver (); // 동일한 RESOLVER 인스턴스를 재사용 할 수 있습니다 - 상태를 유지하지 않습니다 : interceptors.add (새로운 roleannotationMethodinterceptor (Resolver)); interceptors.add (새로운 repmissionAntationMethodinterceptor (Resolver)); interceptors.add (새로운 userAnnotationMethodinterceptor (Resolver)); interceptors.add (새로운 게이트 antationmethodinterceptor (Resolver)); SetMethodinterceptors (인터셉터); } protected org.apache.shiro.aop.methodinvocation createmethodinvocation (object emptspecificmethodinvocation) {Final MethodInvocation mi = (methodInvocation) ImpSpecificMethodInvocation; return new org.apache.shiro.aop.methodinvocation () {public method getMethod () {return mi.getMethod (); } public Object [] getArguments () {return mi.getArguments (); } public String toString () {return "메소드 호출 [" + mi.getMethod () + "]; } public Object Proceed () 던지기 가능 {return mi.proceed (); } public object getThis () {return mi.getThis (); }}; } Protected Object allyInvocation (Object AopalliancemethodInvocation) 던지기 가능 {methodInvocation mi = (MethodInvocation) aopalliancemethodinvocation; return mi.proceed (); } public Object Invoke (MethodInvocation MethodInvocation) 던지기 가능 {org.apache.shiro.aop.methodinvocation mi = createmethodinvocation (methodinvocation); return super.invoke (mi); }}상위 클래스의 호출 방법의 구현을 살펴보면, 핵심 논리는 AsserTauxterized 메소드를 호출하는 것이며,이 방법 (소스 코드는 다음과 같음)이 구성된 승인 메트 메트로 린 셉터가 현재 방법의 권한 검증을 지원하는지 여부를 판단하는 것인지 확인하는 것입니다 (소스 코드는 다음과 같습니다)은 (클래스 또는 방법에 대한 지원되는 주석 또는 방법을 판단함으로써) 결정하는 것입니다. 지원되면 AssertAuxtorized 메소드가 권한 검증을 위해 요구 될 것이며, 승인 메노 테이션 메토드 interceptor는 AssertAuthorized 방법을 호출 할 수 있습니다.
보호 된 void AsserTauthorized (MethodInvocation MethodInvocation)는 인증 exception {// 기본 구현 단지 거부 투표가 캐스트되지 않도록합니다. collection <AuthorizationAnnotationMethodinterceptor> aamis = getMethodinterceptors (); if (aamis! = null &&! aamis.isempty ())) {for (authoringAntationMethodinterceptor aami : aamis) {if (aami.supports (methodinvocation)) {aami.assertauthorized (methodInvocation); }}}}다음으로, AopallianCeannotationsAuxtorizingMethodinterceptor에 의해 정의 된 AbsissionAntationMethodinterceptor를 되돌아 보자. 소스 코드는 다음과 같습니다. aopallianceannotationsauthorizingmethodinterceptor의 소스 코드와 AbsissionAntationMethodinterceptor의 소스 코드를 결합하면 AbsmissionAntationHandler와 SpringAnnotationResolver가 AbsmissionAntationMethodinceptor에 지정되어 있음을 알 수 있습니다. ApprimissionAntationHandler는 AuthormationAntationHandler의 서브 클래스입니다. 따라서 최종 권한 제어는 AssertAuxtorized 구현에 의해 결정됩니다.
공개 클래스 허가 관리 methodinterceptor는 승인 관리 methodinterceptor {public arbissionAntationMethodinterceptor () {super (new repsismentAntationHandler ()); } public ismissionAntationMethodinterceptor (AnnotationResolver Resolver) {super (new ismissionAntationHandler (), Resolver); }}다음으로 AssertAuthorized 메소드 구현을 살펴 보겠습니다. 완전한 코드는 다음과 같습니다. 구현에서 우리는 주석으로 구성된 권한 값을 얻을 수 있으며 여기에 주석이 필요에 따라 주석이 있습니다. 또한 권한 확인을 수행 할 때 주석을 정의 할 때 지정된 텍스트 값을 직접 사용합니다. 나중에 확장하면 여기에서 시작하겠습니다.
Public Class armissionAntationHandler는 enlogrizingAnnotationHandler {public repmissionAntationHandler () {SUPER (requessPermissions.class); } protected string [] getAnnotationValue (주석 a) {requireSpermissions rpannotation = (요구 사항) a; return rpannotation.value (); } 공개 void AsserTauxtorized (주석 a) 인증 exception {if (! (instancesof requestMissions)) 리턴; 요구 사항 rpannotation = (요구 사항) a; 문자열 [] perms = getAnnotationValue (a); 대상 주제 = getSubject (); if (perms.length == 1) {giversity.checkpermission (perms [0]); 반품; } if (logical.and.equals (rpannotation.logical ())) {getSubject (). checkpermissions (perms); 반품; } if (logical.or.equals (rpannotation.logical ())) {// 예외를 처리하지 않으면 불필요하게 "지연"을 호출하여 예외를 던지는 "지연"= 거짓; for (문자열 권한 : perms) if (getSubject (). ispermitted (hasperment)) hasatleastonepermission = true; // 역할이 일치하지 않으면 예외를 유발합니다. 예외 메시지는 (! hasatleastOnePermission) getSubject (). checkPermission (perms [0]); }}}이전 소개를 통해 ASSERTATIONATIONTATIONATIONADLER의 ASSERTATIORED 메소드 매개 변수의 주석은 ASSERTATIONATIONADINGHINGHINDLER의 ASSERTATIONTATIONTEMETHODINGINGINGERCEPTOR를 호출 할 때 인증 한 메트로 토드 interceptor를 통과 시킨다는 것을 알고 있습니다. 소스 코드는 다음과 같습니다. 소스 코드에서, 우리는 getannotation 방법을 통해 주석이 얻어진다는 것을 알 수 있습니다.
공개 void AsserTauthorized (MethodInvocation Mi)는 인증 exception {try {((AuthorizingAntationHandler) gethandler ()). AsserTauthorized (getAnnotation (mi)); } catch (upportizationException ae) {if (ae.getCause () == null) ae.initCause ( "new AuthorizationException ("호출 할 권한이 없음 : " + mi.getMethod ())); ae를 던지십시오. }}이 방향을 따라 걸어 가면 결국 SpringAnnotationResolver의 getAnnotation 방법 구현을 찾을 수 있습니다. 다음 코드에서 볼 수 있듯이 주석을 찾을 때 방법을 찾는 것이 선호됩니다. 메소드에서 찾을 수없는 경우 현재 메소드 호출 클래스에서 해당 주석을 찾습니다. 여기에서 클래스와 메소드에 대해 동일한 유형의 권한 제어 주석을 정의 할 때, 그리고 단독으로 존재하는 경우 정의 된 것이 적용되는 이유도 알 수 있습니다.
공개 클래스 SpringAnnotationResolver는 주석 제고기 {public annotation getAnnotation (methodInvocation mi, class <? extends annotation> clazz) {method m = mi.getMethod (); 주석 a = AnnotationUtils.FindAntation (M, Clazz); if (a! = null) a; // MethodInvocation의 메소드 객체는 인터페이스에 정의 된 메소드 일 수 있습니다. // 그러나 인터페이스의 구현에 주석이 존재하는 경우 (// 인터페이스 자체가 아님) 위의 메소드 객체에 있지 않습니다. 대신 // 대상 클래스에서 메소드 표현을 획득하고 // 구현 자체에서 직접 확인해야합니다. class <?> targetclass = mi.getthis (). getClass (); m = classutils.get mostspecificmethod (m, targetclass); a = AnnotationUtils.FindAntation (M, Clazz); if (a! = null) a; // 클래스에 동일한 주석이 동일한지 확인하십시오. }} 위의 소스 코드 판독 값을 통해 독자들은 Spring을 통합 한 후 Shiro가 지원하는 권한 제어 주석의 원칙에 대해 더 깊이 이해하고 있다고 생각합니다. 위에 게시 된 소스 코드는 저자가 생각하는 핵심 코드 중 일부일뿐입니다. 완전한 내용을 자세히 알고 싶다면 저자가 언급 한 아이디어에 따라 직접 완전한 코드를 읽으십시오.
주석에 기초 하여이 권한 제어 원칙을 이해 한 후에도 독자는 실제 비즈니스 요구에 따라 그에 따라 확장 할 수 있습니다.
스프링 엘 표현을 사용하여 확장
이제 다음과 같은 인터페이스가 있다고 가정합니다. 여기에는 매개 변수 유형을 수신하는 쿼리 메소드가 있습니다. 그러한 매개 변수가 수신되고 다른 값이 반환 될 것이라고 가정하면 여기에서 단순화합시다.
공개 인터페이스 realService {객체 쿼리 (int type); }이 인터페이스는 외부 세계에 열려 있습니다. 이 방법은 해당 URL을 통해 요청할 수 있습니다. 해당 컨트롤러 방법을 다음과 같이 정의합니다.
@RequestMapping ( "/service/{type}") public Object Query (@pathVariable ( "type") int type) {return this.realservice.query (type);}위의 인터페이스 서비스에는 쿼리를 수행 할 때 유형에 대한 권한이 있습니다. 모든 사용자가 각 유형을 사용하여 쿼리를 할 수있는 것은 아니며 해당 권한이 필요합니다. 따라서 위의 프로세서 방법의 경우 권한 제어와 매개 변수 유형과 동적으로 제어 변경 중에 필요한 권한을 추가해야합니다. 유형의 각 권한의 정의가 쿼리의 형태라고 가정 해보십시오 : type. 예를 들어, type = 1이 쿼리 일 때 필요한 권한은 다음과 같습니다. Spring과 통합되지 않으면 다음과 같이 수행합니다.
@requestmapping ( "/service/{type}") public object query (@pathvariable ( "type") int type) {securityUtils.getSubject (). checkPermission ( "query :" + type); realservice.query (type);} replay this.그러나 Spring과의 통합 후 위의 관행은 매우 결합되어 있으며 통합 주석을 사용하여 권한을 제어합니다. 위의 시나리오의 경우 @requirespermissions를 통해 필요한 권한을 지정하지만 @requirespermissions에 정의 된 권한은 정적 텍스트이며 수정됩니다. 우리의 역동적 인 요구를 충족시킬 수는 없습니다. 현재 컨트롤러 처리 방법을 여러로 분할하고 권한을 별도로 제어 할 수 있다고 생각할 수 있습니다. 예를 들어 다음은 다음과 같습니다.
@RequestMapping ( "/service/1")@requestpermissions ( "query : 1") public object service1 () {return this.realservice.query (1);}@requerypermissions ( "query : 2")@requestmapping ( "/service/2") public object service2 () {return return this.realservice.query (2);} //..이는 유형의 값 범위가 비교적 작을 때 괜찮지 만 위와 같은 200 개의 가능한 값이 있으면 별도의 프로세서 방법을 정의하고 권한 제어를 수행하기 위해 철저하게 열거하는 것이 약간 번거 롭습니다. 또한 향후 유형의 값이 변경되면 새 프로세서 방법을 추가해야합니다. 따라서 가장 좋은 방법은 @requirespermission이 동적 권한 정의를 지원하면서 정적 정의 지원을 유지하는 것입니다. 이전 분석을 통해 진입 점은 AbsmissionAntationAndler이며 권한 검증에 대한 확장 기능을 제공하지 않습니다. 우리가 그것을 확장하고 싶다면 간단한 방법은 전체적으로 교체하는 것입니다. 그러나 동적으로 처리하는 데 필요한 권한은 메소드 매개 변수와 관련이 있으며 AbsissionAntationHandler에서 메소드 매개 변수를 얻을 수 없습니다. 이러한 이유로, 우리는 AbsisionAntationAndler를 직접 교체 할 수 없습니다. AbsissionAntationHandler는 hamissionAntationMethodinterceptor에 의해 호출됩니다. 메소드 매개 변수는 PerizentationAntationHanderceptor의 부모 클래스의 AsserTauxtorized 메소드에서 권한이없는 방법으로 호출 될 때 얻을 수 있습니다. 이러한 이유로, 당사의 확장 지점은 AbsmissionAntationMethodinterceptor 클래스에서 선택되며 전체적으로 교체해야합니다. Spring의 EL 표현식은 구문 분석 방법 매개 변수 값을 지원할 수 있습니다. 여기서 우리는 Spring의 El 표현을 소개하기로 선택합니다. @requirespermissions가 권한을 정의하면 Spring EL 표현식을 사용하여 메소드 매개 변수를 도입 할 수 있습니다. 동시에 정적 텍스트를 고려하기 위해. 다음은 Spring El 표현식 템플릿입니다. Spring의 EL 표현식 템플릿의 경우이 블로그 게시물을 참조하십시오. 우리는 우리 자신의 권한 항공사를 정의하고, 허가 관리 methodinterceptor에서 상속 받고, AsserTauthoried 메소드를 무시합니다. 이 메소드의 구현 로직은 ApermissionAntationHandler의 논리를 지칭하지만 사용 된 @requirespermissions의 권한 정의는 EvaluationContext를 구문 분석 한 결과로 현재 호출되는 메소드를 기반으로 Spring EL 표현식을 사용한 결과입니다. 다음은 권한이없는 사람의 정의입니다.
공공 클래스 SelfpermissionAntationMethodinterceptor 확장 당연한 메트로 드 린있어서 {private final spelexpressionparser parser = new spelexpressionparser (); Private Final ParameterNamedScoverer Paramnamedscoverer = New DefaultParameterNamedscoverer (); 개인 최종 templateparsercontext templateparsercontext = new TemplateParserContext (); public SelfPermissionAnnotationMethodInterceptor(AnnotationResolver resolver) { super(resolver); } @Override public void assertAuthorized(MethodInvocation mi) throws AuthorizationException { Annotation annotation = super.getAnnotation(mi); RequiresPermissions permAnnotation = (RequiresPermissions) annotation; String[] perms = permAnnotation.value(); EvaluationContext evaluationContext = new MethodBasedEvaluationContext(null, mi.getMethod(), mi.getArguments(), paramNameDiscoverer); for (int i=0; i<perms.length; i++) { Expression expression = this.parser.parseExpression(perms[i], templateParserContext); //Replace the original permission definition with the permission definition parsed by Spring EL expression perms[i] = expression.getValue(evaluationContext, String.class); } Subject subject = getSubject(); if (perms.length == 1) { subject.checkPermission(perms[0]); 반품; } if (Logical.AND.equals(permAnnotation.logical())) { getSubject().checkPermissions(perms); 반품; } if (Logical.OR.equals(permAnnotation.logical())) { // Avoid processing exceptions unnecessarily - "delay" throwing the exception by calling hasRole first boolean hasAtLeastOnePermission = false; for (String permission : perms) if (getSubject().isPermitted(permission)) hasAtLeastOnePermission = true; // Cause the exception if none of the role match, note that the exception message will be a bit misleading if (!hasAtLeastOnePermission) getSubject().checkPermission(perms[0]); }}}定义了自己的PermissionAnnotationMethodInterceptor后,我们需要替换原来的PermissionAnnotationMethodInterceptor为我们自己的PermissionAnnotationMethodInterceptor。根据前面介绍的Shiro整合Spring后使用@RequiresPermissions等注解的原理我们知道PermissionAnnotationMethodInterceptor是由AopAllianceAnnotationsAuthorizingMethodInterceptor指定的,而后者又是由AuthorizationAttributeSourceAdvisor指定的。为此我们需要在定义AuthorizationAttributeSourceAdvisor时通过显示定义AopAllianceAnnotationsAuthorizingMethodInterceptor的方式显示的定义其中的AuthorizingAnnotationMethodInterceptor,然后把自带的PermissionAnnotationMethodInterceptor替换为我们自定义的SelfAuthorizingAnnotationMethodInterceptor。替换后的定义如下:
<bean> <property name="securityManager" ref="securityManager"/> <property name="advice"> <bean> <property name="methodInterceptors"> <util:list> <bean c:resolver-ref="springAnnotationResolver"/> <!-- 使用自定义的PermissionAnnotationMethodInterceptor --> <bean c:resolver-ref="springAnnotationResolver"/> <bean c:resolver-ref="springAnnotationResolver"/> <bean c:resolver-ref="springAnnotationResolver"/> <bean c:resolver-ref="springAnnotationResolver"/> </util:list> </property> </bean> </property></bean><bean id="springAnnotationResolver"/>
为了演示前面示例的动态的权限,我们把角色与权限的关系调整如下,让role1、role2和role3分别拥有query:1、query:2和query:3的权限。此时user1将拥有query:1和query:2的权限。
<bean id="realm"> <property name="userDefinitions"> <value> user1=pass1,role1,role2 user2=pass2,role2,role3 admin=admin,admin </value> </property> <property name="roleDefinitions"> <value> role1=perm1,perm2,query:1 role2=perm1,perm3,query:2 role3=perm3,perm4,query:3 </value> </property></bean>
此时@RequiresPermissions中指定权限时就可以使用Spring EL表达式支持的语法了。因为我们在定义SelfPermissionAnnotationMethodInterceptor时已经指定了应用基于模板的表达式解析,此时权限中定义的文本都将作为文本解析,动态的部分默认需要使用#{前缀和}后缀包起来(这个前缀和后缀是可以指定的,但是默认就好)。在动态部分中可以使用#前缀引用变量,基于方法的表达式解析中可以使用参数名或p参数索引的形式引用方法参数。所以上面我们需要动态的权限的query方法的@RequiresPermissions定义如下。
@RequestMapping("/service/{type}")@RequiresPermissions("query:#{#type}")public Object query(@PathVariable("type") int type) { return this.realService.query(type);}这样user1在访问/service/1和/service/2是OK的,但是在访问/service/3和/service/300时会提示没有权限,因为user1没有query:3和query:300的权限。
요약
The above is a detailed explanation of Spring integrating Shiro and expanding the use of EL expressions introduced by the editor. 모든 사람에게 도움이되기를 바랍니다. 궁금한 점이 있으면 메시지를 남겨 주시면 편집자가 제 시간에 답장을 드리겠습니다. Wulin.com 웹 사이트를 지원해 주셔서 대단히 감사합니다!