Shiroは、幅広いアプリケーションを備えた軽量許可制御フレームワークです。この記事の焦点は、SpringのShiroの統合を導入し、SpringのEL表現を拡張することで @RequireRolosなどの動的なパラメーターをサポートできるようにすることです。 Shiroの紹介は、この記事の範囲内ではありません。読者がシロについてあまり知らない場合、彼らは公式ウェブサイトを通じて対応する情報を学ぶことができます。 Shiroの包括的な紹介を提供するInfoQに関する記事もあり、公式にも推奨されています。そのアドレスはhttps://www.infoq.com/articles/apache-shiroです。
シロは春を統合します
まず、プロジェクトにshiro-spring-xxx.jarを追加する必要があります。 Mavenを使用してプロジェクトを管理している場合は、依存関係に次の依存関係を追加できます。これが私が選択した最新バージョン1.4.0です。
<Dependency> groupId> org.apache.shiro </groupid> <artifactid> shiro-spring </artifactid> <バージョン> 1.4.0 </version> </dependency>
次に、web.xmlでshirofilterを定義し、それを適用して、通常は /*として構成されている許可制御を必要とするすべての要求を傍受する必要があります。さらに、フィルターを前面に追加して、リクエストが最初にシロのアクセス許可を介して制御されるようにする必要があります。ここのフィルターの対応するクラスは、Springが提供するフィルタープロキシであるDelegatingFilterProxyで構成されています。 Spring BeanコンテナのBeanを現在のフィルターインスタンスとして使用でき、対応するBeanはフィルター名に対応するBeanを使用できます。したがって、次の構成では、BeanコンテナにShirofilterという名前の豆を探します。
<filter> <filter-name> shirofilter </filter-name> <filter-class> org.springframework.web.filter.delegatingfilterproxy </filter-class> <init-param> <param> <param> <param> <param> <param> <param-> <param-value> <filter-name> shirofilter </filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
Shiroを個別に使用する場合、通常、org.apache.shiro.web.servlet.shirofilterを定義して、同様のことを行います。
次は、Beanコンテナにシロフィルターを定義することです。次のように、抽象的なシロフィルタータイプのBeanを生成するShirofilterFactorybeanを定義します。 ShirofilterFactoryBeanを介して、SecurityManagerを指定できます。ここで使用されるDefaultWebseCurityManagerは、領域を指定する必要があります。複数の領域を指定する必要がある場合は、レルムを通じて指定されます。簡単にするために、テキスト定義に直接基づいてTextConfigurationRealmを使用します。 loginurlを使用してログインアドレスを指定し、successurlを指定してログインが成功した後にリダイレクトする必要があるアドレスを指定し、許可が不十分な場合にプロンプトページを指定するためのUnauthorizedurlを指定します。 FilterChainDefinitions使用するURLとフィルターの関係を定義します。等記号の右側にあるフィルターエイリアスは、フィルターエイリアスです。デフォルトのエイリアスは、Enumerationクラスorg.apache.shiro.web.filter.mgt.defaultfilterで定義されています。
<bean id = "shirofilter"> <プロパティ名= "securitymanager" ref = "securitymanager"/> <プロパティ名= "loginurl" value = "/> <プロパティ名=" successurl "value ="/home.jsp "/> <プロパティ名=" value = "/unauthorized.jsp"/> <property name = "filterchaindefinitions"> <balue>/admin/** = authc、authc、authc、logout = logout = logout#otherアドレスは、ユーザーがログインしていることを要求しています。 ref = "realm"/> </bean> <bean id = "lifecyclebeanpostprocessor"/>
<! - 簡単にするために、ここでテキストベースのレルム実装を使用します - > <bean id = "realm"> <property name = "userdefinitions"> <value> user1 = ass1、role1、role2 user2 = pass2、role2、chole3 admin = admin、admin </value> </property> </bean>
FilterChainDefinitions定義でカスタムフィルターを使用する必要がある場合は、ShirofilterFactoryBeanのフィルターを介してカスタムフィルターとそのエイリアスマッピング関係を指定できます。たとえば、以下に示すように、Alias Loggerを備えたフィルターを追加し、フィルターチェーン設定にエイリアスロガーを備えた /**フィルターを指定しました。
<bean id = "shirofilter"> <プロパティ名= "securitymanager" ref = "securitymanager"/> <プロパティ名= "loginurl" value = "/> <プロパティ名=" successurl "value ="/home.jsp "/> <プロパティ名=" value = "/unauthorized.jsp"/> <プロパティ名= "フィルター"> <util:map> <entry key = "logger"> <bean/> </entr> </util:map> </property name = "filterchaindefinitions"> <balue>/admin/** = authc、auth = logout#logout#logout authc、logger </value> </property> </bean>
実際、適用する必要があるフィルターエイリアス定義は、ShirofilterFactorybeanのSetFilters()によって直接定義することもできますが、対応するBeanコンテナの対応するフィルターを直接定義します。デフォルトでは、ShirofilterFactoryBeanは、フィルターのIDエイリアスを使用して、Beanコンテナにすべてのフィルタータイプの豆を登録します。したがって、上記の定義は以下と同等です。
<bean id = "shirofilter"> <プロパティ名= "securitymanager" ref = "securitymanager"/> <プロパティ名= "loginurl" value = "/> <プロパティ名=" successurl "value ="/home.jsp "/> <プロパティ名=" value = "/unauthorized.jsp"/> <Property name = "filterchaindefinitions"> <balue>/admin/** = authc、authc、lows [admin]/logout = logout = logout#otherアドレスは、ユーザーがログインしている必要があります。
上記の手順の後、シロとスプリングの統合が完了します。この時点で、プロジェクトに要求したパスは、ログインする必要があり、自動的にログニャー/パスワードで指定されたパスにジャンプし、ログインしてログインします。この時点で、ユーザー名、パスワードを介してユーザー名を取得し、パスワードを介してパスワードを取得し、その後、ログイン要求を送信する場合は、loginurlの要求を提出する必要があります。ログインするときに使用されるユーザー名/パスワードは、TextConfigurationRealmで定義したユーザー名/パスワードです。上記の構成に基づいて、user1/pass1、admin/adminなどを使用できます。正常にログインした後、Successurlパラメーターで指定されたアドレスにジャンプします。 user1/pass1を使用してログインしている場合、アクセス/管理/インデックスを試みることもできます。この場合、許可が不十分なため、unauthorized.jspにジャンプします。
注釈ベースのサポートを有効にします
基本的な統合では、URLがShirofilterFactoryBeanのフィルター決定に適用する必要があるすべての許可制御を定義する必要があります。これは時々それほど柔軟ではありません。 Shiroは、Springを統合した後に使用できる注釈を提供します。これにより、クラスまたはメソッドにアクセスするために必要なアクセス許可を定義するために許可制御を必要とするクラスまたはメソッドに対応する注釈を追加できます。定義のクラスにある場合、クラス内のすべてのメソッドを呼び出すには、対応するアクセス許可が必要であることを意味します(外部呼び出しが必要であることに注意してください。これは動的プロキシの制限です)。これらのアノテーションを使用するには、スプリングのBeanコンテナに次の2つのBean定義を追加して、ユーザーが実行時に注釈定義に基づいて対応するアクセス許可を持っているかどうかを判断できるようにする必要があります。これは、SpringのAOPメカニズムを通じて達成されます。 Spring AOPについて何も知らない場合は、著者が書いた著者の「Spring AOP Introduction column」を参照できます。次の2つのBeanの定義であるAutharizationAttributesOurCeadVisorは、Advisorを定義します。これは、Shiroが提供する注釈設定方法に基づいて許可を傍受および検証します。 DefaultAdvisorautoproxycreatorは、ターゲットメソッドコールを傍受するときに、許可制御注釈が付いたクラスのプロキシオブジェクトを作成し、authorizationAttributesourceadvisorを適用する機能を提供します。ユーザーからのリクエストが傍受され、ユーザーが対応するメソッドまたはクラスに許可がマークされていない場合、org.apache.shiro.authz.authorizationexception例外がスローされます。
<Bean Depens-on = "lifecyclebeanpostprocessor"/> <bean> <プロパティname = "securitymanager" ref = "securitymanager" // </bean>
<aop:config/>或<aop:aspectj-autoproxy/>がbeanコンテナで既に定義されている場合、defaultadvisorautoproxycreatorはもはや定義できません。前の2つのケースは、defaultadvisorautoproxycreatorと同様に豆を自動的に追加するためです。 DefaultAdvisorautoproxycreatorの詳細については、Spring AOPでプロキシオブジェクトを自動的に作成するという著者の原則を参照することもできます。
Shiroが提供する許可制御注釈は次のとおりです。
必要性:ユーザーは現在のセッションで認証される必要があります。つまり、ユーザー名/パスワードでログインする必要があり、rememberme自動ログインは含まれていません。
exeSuser:ユーザーは認証される必要があります。このセッションでユーザー名/パスワードでログインすることで認証することも、remembermeで自動的にログインすることもできます。
rebyesguest:ユーザーはログインしていません。
requelesroles:ユーザーは、指定された役割を所有する必要があります。
requirespermissions:ユーザーは指定されたアクセス許可を必要とします。
最初の3つは理解しやすいですが、最後の2つは似ています。ここでは、例として@RequireSperMissionsを使用します。まず、上記の領域を変更し、役割に権限を追加しましょう。このようにして、user1はperm1、perm2、およびperm3へのpermissionsを持ち、user2はperm1、perm3、およびperm4にpermissionsを持ちます。
<bean id = "realm"> <プロパティ名= "userdefinitions"> <value> user1 = pass1、role1、pass2 = pass2、role2、role3 admin = admin、admin </value> </property> <プロパティ名= "roledefinitions"> <balue> role1 = perm1、perm2 = perm3 = perm3
@RequireSperMissionsは、メソッドを呼び出すときに請求する必要があるアクセス許可を指定する方法に追加できます。次のコードでは、 /perm1にアクセスするときにperm1の許可を所有する必要があることを指定します。この時点で、user1とuser2の両方にアクセスできます。
@RequestMapping( "/perm1")@requirespermissions( "perm1")public object permission1(){return "perm1";}メソッドにアクセスするために複数のアクセス許可が同時に必要であることを指定する必要がある場合は、配列の形式で指定する必要があるアクセス許可を指定できます(注釈に単一の配列属性を指定する場合、ブレースを追加できませんが、複数の許可を指定する必要がある場合、ブレースが必要です)。たとえば、次のように、 /perm1andperm4にアクセスするときに、ユーザーはperm1とperm4の両方の許可を持っている必要があることを指定します。現時点では、perm1とperm4が同時にあるため、ユーザー2のみがアクセスできます。
@RequestMapping( "/perm1andperm4")@rebyespermissions({"perm1"、 "perm4"})public object perm1andperm4(){return "perm1andperm4";}複数の権限が同時に指定されている場合、複数の権限間の関係は関係です。つまり、同時に指定されたすべてのアクセス許可が必要です。指定された複数の権限のいずれかがアクセスできるようにする必要がある場合は、opical = olgical.orを使用して複数の権限間の関係を指定できます。たとえば、次のように、 /perm1orperm4にアクセスするときは、user1とuser2の両方がこのメソッドにアクセスできるように、perm1またはperm4 permissionsが必要であることのみが必要であることを指定します。
@RequestMapping( "/perm1orperm4")@requirespermissions(value = {"perm1"、 "perm4"}、logical = ofical.or)public object perm1orperm4(){return "perm1orperm4";}@RequiresPermissionsはクラスでもマークすることができ、クラスでメソッドにアクセスする場合、対応する権限が必要であることを示します。たとえば、以下では、クラスレベルで許可perm2を必要とする必要があることを指定しますが、index()メソッドではアクセス許可が必要であることを指定していませんが、このメソッドにアクセスする際にクラスレベルで指定する権限を指定する必要があります。現時点では、ユーザーのみがアクセスできます。
@retscontroller@requestmapping( "/foo")@requirespermissions( "perm2")public class foocontroller {@requestmapping(method = requestmethod.get)public object index(){map <string、object> map = new hashmap <>(); map.put( "ABC"、123);マップを返します。 }}クラスレベルとメソッドレベルの両方が@RequireSperMissionsの場合、メソッドレベルの優先度が高く、メソッドレベルで必要な権限のみが現時点で検証されます。次のように、クラスレベルでPerm2許可が必要であり、Perm3許可がメソッドレベルで必要であることを指定します。次に、 /fooにアクセスするときは、index()メソッドにアクセスするためにperm3許可が必要です。したがって、この時点で、user1とuser2の両方が /fooにアクセスできます。
@retycontroller @requestmapping( "/foo") @requirespermissions( "perm2")public class foocontroller {@requestmapping(method = requestmethod.get)@requireSpermissions( "perm3")public object index(){Map <String、Object> Map = new Hashmap <>(); map.put( "ABC"、123);マップを返します。 }}ただし、現時点でクラスに@RequireSroles( "Role1")を追加して役割の役割1を持つ必要があることを指定する場合、 /fooにアクセスするときは、@RequireSperMissions( "perm3")でleast( "perm3")によって指定されている必要があります。必要性の要件とexpermissionsは異なる次元の許可定義に属しているため、Shiroは検証中にそれらを1回チェックしますが、クラスとメソッドの両方が同じタイプの許可制御定義の注釈を持っている場合、メソッドの定義は定義に基づいています。
@retycontroller@requestmapping( "/foo")@requirespermissions( "perm2")@requiresroles( "chole1")public class foocontroller {@requestmapping(method = requestmethod.get)@requirespermissions( "perm3")public object index(){map <<string> object> object> new hashmap <> map.put( "ABC"、123);マップを返します。 }}この例ではexeSpermissionsのみを使用しますが、他の許可制御解決の使用も同様です。興味のある友達から他の注釈を使用してください。
注釈制御権限の原理
上記で指定した権限は、@requirespermissionsを使用して静的です。この記事の主な目的の1つは、実装を拡張して指定された権限を動的にする方法を導入することです。しかし、拡張する前に、拡張する前に、それがどのように機能するか、つまり実装の原則を知る必要があります。それでは、ShiroがSpringと@RequiresPermissionsをどのように統合するかを見てみましょう。 @RequireSperMissionsのサポートを有効にするとき、stateMethodMatcherPointCutadvisorから継承されるアドバイザーである次のBeanを定義します。ロジックに一致するメソッドは、クラスまたはメソッドがシロのいくつかの許可制御注釈を持っている限り、傍受後の処理ロジックが対応するアドバイスによって指定されることです。
<bean> <プロパティ名= "SecurityManager" Ref = "SecurityManager"/> </bean>
以下は、承認のソースコードです。コンストラクターの方法では、AopallianceanNotationsAuthorizingMethodododiterceptorが、MethodEnterceptorの実装に基づいたSetAdvice()によって指定されていることがわかります。
パブリッククラスautherizationAttributeSourCeadVisor拡張stateCMethodMatcherPointCutadvisor {private static final logger log = loggerFactory.getLogger(authisizationAttributesourceadvisor.class);プライベート静的最終クラス<? annotation> [] authz_annotation_classes = new class [] {remasespermissions.class、requiresRoles.class、requiresuser.class、requiresguest.class、requesauthentication.class}; Protected SecurityManager SecurityManager = null; public AutherizationAttributeSourCeadVisor(){setAdvice(new aopallianceannotationsauthorizingMethodInterceptor()); } public SecurityManager getSecurityManager(){return securitymanager; } public void setSecurityManager(org.apache.shiro.mgt.securitymanager securitymanager){this.securitymanager = securitymanager; }パブリックブールマッチ(メソッドメソッド、クラスターゲットクラス){メソッドM =メソッド; if(isauthzannotationpresent(m)){return true; } //「メソッド」パラメーターは、注釈がないインターフェイスからのものである可能性があります。 //実装にあるかどうかを確認してください。 if(targetclass!= null){try {m = targetclass.getMethod(m.getName()、m.getParametertypes()); Isauthzannotationpresent(m)||を返しますIsauthzannotationpresent(ターゲットクラス); } catch(nosuchmethodexceptionは無視されます){//デフォルトの返品値はfalseです。メソッドが見つからない場合は、明らかに//注釈はありませんので、デフォルトの返品値を使用してください。 }} falseを返します。 } private boolean isauthzannotationpresent(class <?> targetclazz){for(class <?extends annotation> annclass:authz_annotation_classes){annotation a = annotationtils.findannotation(TargetClazz、Annclass); if(a!= null){return true; }} falseを返します。 } private boolean isauthzannotationpresent(method method){for(class <?extends annotation> annclass:authz_annotation_classes){annotation a = annotationutils.findannotation(method、annclass); if(a!= null){return true; }} falseを返します。 }}aopallianceannotationsのソースコードは次のとおりです。 ITに実装されているMethodEnterceptorインターフェイスのInvokeメソッドは、親クラスのInvokeメソッドを呼び出します。同時に、AnnotationMethodInterceptorの実装がそのコンストラクター法で作成されていることを確認する必要があります。これらの実装は、許可制御を実装するコアです。その後、PermissionAnnotationMethodInterceptorの実装クラスを選択して、特定の実装ロジックを確認します。
パブリッククラスAOPALLIANCEANNOTATIONSAUTHORIZINGMETHODODINTERCEPTOR拡張アノテーションの拡張AntoRizingMethododInterceptor MethodInterceptor {public aopallianceannotationsauthorizingmethodInterceptor(){list <authingnotationmethododInterceptor> interpectors = new Arraylist <aurnotationmethodationmethodationmethodationmethodationmethodationmethodationmethodationmethodation //スプリング固有のアノテーションリゾルバーを使用します-SpringのAnnotationUtilsは// raw JDK解像度プロセスよりも優れています。 annotationResolver Resolver = new SpringAnnotationResolver(); //同じResolverインスタンスを再利用できます - それは状態を保持しません:Interceptors.Add(new RoleanNotationMethodInterceptor(Resolver)); Interceptors.Add(新しいPermissionAnnotationMethodInterceptor(Resolver)); interceptors.add(new userannotationMethodInterceptor(Resolver)); interceptors.add(new gutesAnnotationmethodInterceptor(リゾルバー)); setMethodInterceptors(インターセプター); } protected org.apache.shiro.aop.methodinvocation createmethodinvocation(object emplspecmethodinvocation){final methodinvocation mi =(methodinvocation)ImplspecificMethodinvocation; return new org.apache.shiro.aop.methodinvocation(){public method getMethod(){return mi.getMethod(); } public Object [] getArguments(){return mi.getarguments(); } public string toString(){return "method invocation [" + mi.getMethod() + "]"; } public object ofter()throws throwable {return mi.proceed(); } public Object getthis(){return mi.getthis(); }}; }保護されたオブジェクトContinueInvocation(オブジェクトaopalliancemethodinvocation)スロースロー可能{methodinvocation mi =(methodinvocation)aopalliancemethodinvocation; return mi.proceed(); } public Object invoke(MethodInvocation MethodInvocation)Throws throwable {org.apache.shiro.aop.methodinvocation mi = createmethodinvocation(methodinvocation); Super.Invoke(MI)を返します。 }}親クラスのInvokeメソッドの実装を調べることにより、最終的にコアロジックはASSERTAUTHORIZEDメソッドを呼び出すことであり、この方法の実装(ソースコードは次のとおりです)が、構成されたANDOTATIONMETHODINTERCEPTORが現在の方法の許可検証をサポートするかどうかを判断することを確認します(それがサポートされているクラスまたは方法で支持されるかどうかを判断することによって)。サポートされると、そのassertAuthorizedメソッドは許可確認のために呼び出され、AuthizingAnnotationMethodInterceptorは、AndotationAnnotationHandlerのassertAuthorized Methodを呼び出します。
保護されたvoid assertAuthorized(methodinvocation methodinvocation)authorizationexception {//デフォルトの実装は、拒否投票がキャストされないことを保証します:collection <authisizingannotationmethodInterceptor> aamis = getMethodInterceptors(); if(aamis!= null &&!aamis.isempty()){for(authisingannotationmethodInterceptor aami:aamis){if(aami.supports(methodinvocation)){aami.assertauthorized(methodinvocation); }}}}次に、AOPALLIANCEANNOTATIONS AUTORIZINGMETHODODINTERCEPTORによって定義されたPermissionAnnotationMethoDodInterceptorを振り返ってみましょう。ソースコードは次のとおりです。 AOPALLIANCEANNOTATIONSのソースコードを組み合わせて、MethododitedInterceptorのソースコードを組み合わせて、PermissionAnnotationHandlerとSpringAnnotationResolverがPermissionAnnotationMethodInterceptorで指定されていることがわかります。 PermissionAnnotationHandlerは、AnturizingAnnotationHandlerのサブクラスです。したがって、私たちの最終的な許可制御は、PermissionAnnotationHandlerの主張された実装によって決定されます。
パブリッククラスPermissionAnnotationMethodInterceptor extends AuthisingAnnotationMethodInterceptor {publiciments AnnotationmethodInterceptor(){super(new PermissionAnnotationHandler()); } publicmissionAnnotationMethodInterceptor(AnnotationResolver Resolver){super(new PermissionAnnotationHandler()、Resolver); }}次に、PermissionAnnotationHandlerのAssertAuthorizedメソッドの実装を見てみましょう。完全なコードは次のとおりです。実装から、注釈から構成された許可値が得られることがわかり、ここでの注釈は必要な充電注釈です。さらに、許可確認を実行するときは、注釈を定義するときに指定されたテキスト値を直接使用します。後で拡張すると、ここから始めます。
Public Class PermissionAnnotationHandlerはAuthisizingAnnotationHandler {public PermissionAnnotationHandler(){super(requirespermissions.class); } protected string [] getAnnotationValue(annotation a){requirespermissions rpannotation =(requirespermissions)a; rpannotation.value()を返します。 } public void assertAuthorized(annotation a)authorizationexception {if(!(a intance of requirespermissions))return; requirespermissions rpannotation =(requirespermissions)a; string [] perms = getAnnotationValue(a);サブジェクト件名= getSubject(); if(perms.length == 1){subject.checkpermission(perms [0]);戻る; } if(logical.and.equals(rpannotation.logical())){getsubject()。checkpermissions(perms);戻る; } if(logical.or.equals(rpannotation.logical())){//処理を避けてください。 for(文字列許可:perms)if(getsubject()。ispermitted(permission))hasatleastonepermission = true; //ロールが一致しない場合、例外を引き起こします。例外メッセージは、(!hasatleastonepermission)getSubject()。checkpermission(perms [0]); }}}前の紹介を通して、AndatationMethodInterceptorを承認する際にAndationMethoDodInterceptorを承認することにより、AssertaNotationHandlerのAssertAuthorizedメソッドパラメーターの注釈が渡されることを知っています。ソースコードは次のとおりです。ソースコードから、AnnotationがGetAnnotationメソッドを通じて取得されることがわかります。
public void assertAuthorized(methodinvocation mi)throws authorizationexception {((authingannotationhandler)gethandler())。 } catch(autherizationexception ae){if(ae.getCause()== null)ae.initcause(new AuthorizationException( "Invoke Method:" + mi.getmethod()); AEを投げる; }}この方向に沿って歩くと、最終的には、次のように実装されているSpringAnnotationResolverのGetAnnotationメソッドの実装が見つかります。次のコードからわかるように、注釈を探すときにこの方法を探すことが望ましい。メソッドに掲載されていない場合、現在のメソッド呼び出しのクラスから対応する注釈を探します。ここから、クラスと方法で同じタイプの許可制御注釈を定義したときにメソッドに有効になる理由、およびそれが単独で存在する場合、定義されたものが有効になる理由もわかります。
パブリッククラスSpringAnnotationResolverはAnnotationResolverを実装します{public annotation getAnnotation(MethodInvocation Mi、class <?extends annotation> clazz){Method M = Mi.getMethod();注釈a = annotationutils.findannotation(m、clazz); if(a!= null)return a; // MethodInvocationのメソッドオブジェクトは、インターフェイスで定義されるメソッドである可能性があります。 //ただし、インターフェイスの実装(//インターフェイス自体ではない)に注釈が存在する場合、上記のメソッドオブジェクトにはありません。代わりに、// TargetClassからメソッド表現を取得し、//実装自体を直接確認する必要があります:class <?> targetclass = mi.getthis()。getClass(); m = classutils.getmistspecificmethod(m、targetclass); a = annotationutils.findannotation(m、clazz); if(a!= null)return a; //クラスが同じアノテーションを持っているかどうかを確認しますannotationutils.findannotation(mi.getthis()。getclass()、clazz); }}上記のソースコードの読み取りを通して、読者は春を統合した後、シロがサポートする許可制御注釈の原則をより深く理解していると思います。上に掲載されているソースコードは、著者が比較的コアだと思うコアのコードの一部にすぎません。完全なコンテンツを詳細に知りたい場合は、著者が言及したアイデアに沿って完全なコードを自分で読んでください。
注釈に基づいたこの許可制御の原則を理解した後、読者は実際のビジネスニーズに応じてそれに応じて拡張することもできます。
Spring El Expressionsを使用して拡張されました
次のようなインターフェイスがあると仮定します。これには、パラメータータイプを受信するクエリメソッドがあります。このようなパラメーターが受信され、異なる値が返される限り、ここで簡素化しましょう。
パブリックインターフェイスRealService {Object Query(int Type); }このインターフェイスは、外の世界に開かれています。この方法は、対応するURLを介して要求できます。対応するコントローラー方法を次のように定義します。
@RequestMapping( "/service/{type}")public object query(@pathvariable( "type")intタイプ){return this.realservice.query(type);}上記のインターフェイスサービスには、クエリを実行するときにタイプの権限があります。すべてのユーザーが各タイプを使用してクエリを使用できるわけではなく、対応する権限が必要です。したがって、上記のプロセッサメソッドの場合、許可制御を追加する必要があり、コントロール中に必要なパラメータータイプを動的に変更する必要があります。タイプの各許可の定義がクエリの形式であると仮定します:タイプ。たとえば、type = 1がクエリ:1である場合に必要な許可、およびtype = 2がクエリ:2の場合に必要な許可。春と統合されていない場合、次のようにこれを行います。
@RequestMapping( "/service/{type}")public object query(@pathvariable( "type")intタイプ){securityutils.getSubject()。checkpermission( "query:" + type); this.realservice.query(type);}ただし、Springとの統合の後、上記のプラクティスは非常に結合されており、統合された注釈を使用して権限を制御したいと考えています。上記のシナリオでは、@RequiresPermissionsを介して必要なアクセス許可を指定したいが、@RequiresPermissionsで定義されている権限は静的テキストで固定されています。それは私たちの動的なニーズを満たすことができません。現時点では、コントローラー処理方法を複数に分割し、アクセス許可を個別に制御できると考えるかもしれません。たとえば、以下は次のとおりです。
@RequestMapping( "/service/1")@requirespermissions( "query:1")public object service1(){return this.realservice.query(1);}@requesspermissions( "query:2")@requestmapping( "/service/2")public object service2(){ this.realservice.query(2);} //...@ requestmapping( "/service/200")@requirespermissions( "query:200")public object service200(){return this.realservice.query(200);}これは、タイプの値の範囲が比較的小さい場合は問題ありませんが、上記のような200の可能な値がある場合、個別のプロセッサメソッドを定義して許可制御を実行するために徹底的に列挙することは少し面倒です。さらに、将来のタイプの値が変更された場合、新しいプロセッサメソッドを追加する必要があります。したがって、最良の方法は、静的定義のサポートを維持しながら、@RequiresPermissionsをサポートする動的な許可定義をサポートすることです。以前の分析を通じて、エントリポイントはPermissionAnnotationHandlerであり、許可確認のための拡張機能を提供しないことがわかります。拡張したい場合は、簡単な方法は全体として置き換えることです。ただし、動的に処理するために必要な権限はメソッドパラメーターに関連しており、メソッドパラメーターはPermissionAnnotationHandlerで取得できません。このため、PermissionAnnotationHandlerを直接交換することはできません。 PermissionAnnotationHandlerは、PermissionAnnotationMethodInterceptorによって呼び出されます。メソッドパラメーターは、PermissionAnnotationHandlerがその親クラスAuthisingAnnotationMethodInterceptorのassertAuthorized Methodで呼び出される場合に取得できます。このため、拡張ポイントはPermissionAnnotationMethodInterceptorクラスで選択されており、全体として置き換える必要があります。 SpringのEL式は、解析方法のパラメーター値をサポートできます。ここでは、SpringのEL表現を紹介することを選択します。 @RequiresPermissionsが権限を定義する場合、Spring EL Expressionsを使用してメソッドパラメーターを導入できます。同時に、静的テキストを考慮するため。これがスプリングエルエクスプレッションテンプレートです。 SpringのEL Expressionテンプレートについては、このブログ投稿を参照してください。私たちは、自分のPermissionAnnotationMethodInterceptorを定義し、PermissionAnnotationMethodInterceptorから継承し、AssertAuthoriedメソッドをオーバーライドします。メソッドの実装ロジックは、PermissionAnnotationHandlerのロジックを指しますが、使用される@RequiresPermissionsの許可定義は、evaluationContextを解析する結果として現在呼ばれる方法に基づいたSpring EL式の使用の結果です。以下は、PermissionAnnotationMethodInterceptorの実装の独自の定義です。
Public Class selfpermissionAnnotationMethododInterceptor拡張extends PermissionNotationMethodInterceptor {private final spelexpressionParser parser = new spelexpressionParser();プライベート最終的なparameternamediscoverer paramnamediscoverer = new defaultParameternamedIscoverer();プライベート最終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のウェブサイトへのご支援ありがとうございます!