Shiro ist ein leichter Rahmen für Berechtigungssteuerung mit einer Vielzahl von Anwendungen. Der Schwerpunkt dieses Artikels liegt auf der Einführung der Integration von Shiro durch Spring und der Ermöglichung dynamischer Parameter wie @Requiresroles, die durch Erweiterung der El -Ausdrücke von Spring unterstützt werden. Die Einführung in Shiro liegt nicht im Rahmen dieses Artikels. Wenn die Leser nicht viel über Shiro wissen, können sie die entsprechenden Informationen über ihre offizielle Website lernen. Es gibt auch einen Artikel über Infoq, der eine umfassende Einführung in Shiro bietet und offiziell empfohlen wird. Die Adresse lautet https://www.infoq.com/articles/apache-shiro.
Shiro integriert den Frühling
Zuerst müssen Sie Ihrem Projekt Shiro-Spring-Xxx.jar hinzufügen. Wenn Sie Maven verwenden, um Ihr Projekt zu verwalten, können Sie Ihren Abhängigkeiten die folgenden Abhängigkeiten hinzufügen. Hier ist die neueste Version 1.4.0, die ich ausgewählt habe.
<Depopenty> <gruppe> org.apache.shiro </Groupid> <artifactId> shiro-pring </artifactId> <version> 1.4.0 </Version> </abhängig>
Als nächstes müssen Sie einen Shirofilter in Ihrem web.xml definieren und anwenden, um alle Anforderungen abzufangen, für die die Berechtigungssteuerung erforderlich ist, die normalerweise als /*konfiguriert sind. Darüber hinaus muss der Filter an die Front hinzugefügt werden, um sicherzustellen, dass die Anfrage nach der Einführung zunächst durch Shiros Berechtigungen gesteuert wird. Die entsprechende Filterklasse ist hier mit Delegatingfilterproxy konfiguriert, bei der ein Filterproxy von Frühling bereitgestellt wird. Sie können eine Bohne im Federbohnenbehälter als aktuelle Filterinstanz verwenden, und die entsprechende Bohne nimmt die Bohne entsprechend dem Filternamen. Die folgende Konfiguration sucht also nach einer Bohne namens Shirofilter im Bohnenbehälter.
<Filter> <filter-name> shirofilter </filter-name> <filterklasse> org.springframework.web.filter.delegatingFilterProxy </filter-classe> <init-param> <param-name> targetFilterLifecycle </param-name> <param-value </param-valuue> </init-param> </filter> <Filtermapping> <Filter-name> shirofilter </filter-name> <URL-Muster>/*</url-mustert> </filtermapping>
Wenn Sie Shiro unabhängig verwenden, definieren Sie normalerweise einen org.apache.shiro.web.servlet.shirofilter, um etwas Ähnliches zu tun.
Als nächstes definieren unser Shirofilter im Bohnenbehälter. Wie folgt definieren wir eine Shirofilterfaktorikbean, die eine Bean von AbstractShirofilter -Typ erzeugt. Durch ShirofilterfactoryBean können wir einen SecurityManager angeben. Der hier verwendete Standardwebsicherheitsinstitut muss ein Reich angeben. Wenn mehrere Bereiche angegeben werden müssen, wird er durch Bereiche angegeben. Der Einfachheit halber verwenden wir TextConfigurationRealm direkt auf der Textdefinition. Verwenden Sie Loginurl, um die Anmeldeadresse, Successurl, anzugeben, um die Adresse anzugeben, die nach dem erfolgreichen Anmeldung umgeleitet werden muss, und nicht autorisiert, um die Eingabeaufforderung aufzugeben, wenn die Berechtigungen nicht ausreichend sind. Filterchaindefinitions definiert die Beziehung zwischen der URL und dem zu verwendenden Filter. Der Filter -Alias auf der rechten Seite des gleichen Vorzeichens ist der Filteralias. Der Standard -Alias ist in der Aufzählung der Aufzählungsklasse org.apache.shiro.web.filter.mgt.DefaultFilter definiert.
<bean id = "shirofilter"> <Eigenschaft name = "SecurityManager" Ref = "SecurityManager"/> <Eigenschaft name = "loginurl" value = "/login.jsp"/> <Eigenschaft name = "successUrl" value = "/home.jsp"/> <Property name = "unauthorizedurl" unauthoredurl "/unauthoried.jsp"/> <Sbotschaft "/phamitoredurl"/unauthorized.jsp "/>; value = "/unauthorized.jsp"/> <Eigenschaft name = "filterChainDeFinitions"> <wert>/admin/** = authc, Rollen [admin]/logout = logout # Andere Adressen erfordern, dass der Benutzer in/** = Authc, Logger </value> </Eigenschaft angemeldet hat. ref = "realm"/> </bean> <bean id = "LifecycleBeanPostProcessor"/>
<!-Der Einfachheit halber werden wir hier eine textbasierte Realm-Implementierung verwenden-> <bean id = "realm"> <Eigenschaft name = "userDefinitions"> <wert> user1 = pass1, rollen2 user2 user2 = pass2, rollen2, rollen3 admin = admin, admin, admin, admin </value> </property> </ban> </ban>
Wenn Sie einen benutzerdefinierten Filter in der Definition filterchaindefinitions verwenden müssen, können Sie den benutzerdefinierten Filter und seine Alias -Mapping -Beziehung über die Filter von ShirofilterFactoryBean angeben. Zum Beispiel haben wir, wie unten gezeigt, einen Filter mit Alias Logger hinzugefügt und in FilterchaindeFinitions mit Alias Logger angegeben /** Filter.
<bean id = "shirofilter"> <Eigenschaft name = "SecurityManager" Ref = "SecurityManager"/> <Eigenschaft name = "loginurl" value = "/login.jsp"/> <Eigenschaft name = "successUrl" value = "/home.jsp"/> <Property name = "unauthorizedurl" unauthoredurl "/unauthoried.jsp"/> <Sbotschaft "/phamitoredurl"/unauthorized.jsp "/>; value = "/unauthorized.jsp"/> <Eigenschaft name = "filters"> <util: map> <Eintragsschlüssel = "logger"> <bean/> </Eintrag> </util: map> </property> <Eigenschaft name = "filterchainDefinitions"> <Valchen>/admin/*** = admin. </value> </property> </bean>
Tatsächlich kann die von uns angewandte Filter -Alias -Definition auch direkt durch ShirofilterFactoryBean -SetFilters () definiert werden, jedoch direkt den entsprechenden Filter entsprechenden Bohnen im entsprechenden Bohnenbehälter definieren. Da ShirofilterFactoryBean standardmäßig alle Filterbohnen im Bohnenbehälter mit ihrem ID -Alias in Filtern registriert. Daher entspricht die obige Definition den folgenden.
<bean id = "shirofilter"> <Eigenschaft name = "SecurityManager" Ref = "SecurityManager"/> <Eigenschaft name = "loginurl" value = "/login.jsp"/> <Eigenschaft name = "successUrl" value = "/home.jsp"/> <Property name = "unauthorizedurl" unauthoredurl "/unauthoried.jsp"/> <Sbotschaft "/phamitoredurl"/unauthorized.jsp "/>; value = "/unauthorized.jsp"/> <Eigenschaft name = "filterchaNDeFinitions"> <wert>/admin/** = authc, Rollen [admin]/logout = logout # Andere Adressen erfordern, dass der Benutzer in/** = authc, logger </value> </property> </bean> <lean id = "logger"/>/> </bean> <lean id = "" logger "/>/> </teen> <-
Nach den obigen Schritten ist die Integration von Shiro und Frühling abgeschlossen. Zu diesem Zeitpunkt verlangt jeder Pfad, den wir für das Projekt angefordert haben, und springt automatisch zu dem von Loginurl angegebenen Pfad und lassen Sie uns den Benutzernamen/das Kennwort eingeben. Zu diesem Zeitpunkt sollten wir ein Formular bereitstellen, um den Benutzernamen durch Benutzername, Passwort, über das Passwort zu erhalten, und dann muss die Anfrage übernommen. Der Benutzername/Passwort, der beim Anmelden verwendet wird, ist der Benutzername/Passwort, das wir in TextConfigurationRealm definiert haben. Basierend auf unserer oben genannten Konfiguration können Sie User1/PASS1, Admin/Admin usw. verwenden. Nach erfolgreichem Anmelden wird es zu der von dem SuccessURL -Parameter angegebenen Adresse springen. Wenn wir User1/PASS1 angemeldet sind, können wir auch versuchen, auf/admin/Index zuzugreifen, und zu diesem Zeitpunkt werden wir aufgrund unzureichender Berechtigungen auf nicht autorisierte.jsp springen.
Aktivierungsbasierte Unterstützung aktivieren
Grundlegende Integration erfordert, dass wir alle Berechtigungskontrollen definieren, die die URL in den Filterchainedefinitionen von ShirofilterFactoryBean gelten muss. Das ist manchmal nicht so flexibel. Shiro liefert uns Anmerkungen, die nach der Integration des Frühlings verwendet werden können. Es ermöglicht uns, der Klasse oder Methode entsprechende Anmerkungen hinzuzufügen, für die die Berechtigungssteuerung erforderlich ist, um die für den Zugriff auf Klasse oder Methode erforderlichen Berechtigungen zu definieren. Wenn es sich in der Definition in der Klasse befindet, bedeutet dies, dass das Aufrufen aller Methoden in der Klasse entsprechende Berechtigungen erfordert (beachten Sie, dass es sich um einen externen Aufruf handelt, der die Einschränkung des dynamischen Proxy ist). Um diese Anmerkungen zu verwenden, müssen wir den folgenden zwei Bean -Definitionen dem Bean -Container von Spring hinzufügen, damit wir feststellen können, ob der Benutzer die entsprechenden Berechtigungen auf der Grundlage der Annotationsdefinition zur Laufzeit hat. Dies wird durch den AOP -Mechanismus des Frühlings erreicht. Wenn Sie nichts über Frühlings -AOP wissen, können Sie sich auf die vom Autor verfasste "Spring AOP -Einführungspalte" des Autors beziehen. Die folgenden zwei Bean -Definitionen, AuthorizationAttributesourceadvisor, definiert einen Berater, der die Berechtigungen anhand der von Shiro bereitgestellten Annotationskonfigurationsmethode abfängt und bestätigt. DefaultAdvisorAutoproxyCreator bietet die Funktion, Proxy -Objekte für die von Shiro bereitgestellte Klassenanmerkungen zu erstellen, und die Anwendung von AutorizationAttributesourceadvisor beim Abfangen des Zielmethodeaufrufs. Wenn eine Anfrage des Benutzers abgefangen wird und der Benutzer keine Berechtigung für die entsprechende Methode oder Klasse hat, wird eine org.apache.shiro.authz.authorizationException -Ausnahme ausgeworfen.
<bean haps-on = "LifecycleBeanPostProcessor"/> <bean> <Property name = "SecurityManager" Ref = "SecurityManager" // </bean>
Wenn <aop:config/>或<aop:aspectj-autoproxy/> in unserem Bean-Container bereits definiert ist, kann der Standard-VisorautoproxyCreator nicht mehr definiert werden. Da die beiden vorherigen Fälle automatisch Bohnen hinzugefügt werden, ähnlich wie bei DefaultAdvisorautoproxyCreator. Weitere Informationen zum Standard -VisorautoproxyCreator erhalten Sie auch auf das Prinzip des Autors, um im Frühjahr AOP automatisch Proxy -Objekte zu erstellen.
Die von Shiro bereitgestellten Annotationen zur Berechtigung sind wie folgt:
ErforderlichAuthentication: Der Benutzer muss in der aktuellen Sitzung authentifiziert werden, dh er muss sich mit dem Benutzernamen/dem Kennwort anmelden, und schließt sich nicht die automatische Anmeldung von Rememberme ein.
Fordert: Der Benutzer muss authentifiziert werden. Es kann authentifiziert werden, indem Sie sich in dieser Sitzung mit dem Benutzernamen/Passwort anmelden, oder es kann automatisch mit RemalMe angemeldet werden.
Benötigt GUEST: Der Benutzer ist nicht angemeldet.
Erfordernis: Der Benutzer verlangt, dass die angegebene Rolle im Besitz ist.
Erforderliche Ermordungen: Der Benutzer benötigt die angegebenen Berechtigungen.
Die ersten drei sind leicht zu verstehen, während die letzten beiden ähnlich sind. Hier verwende ich @RequireSpermissions als Beispiel. Ändern wir zunächst den oben definierten Bereich und fügen Sie Berechtigungen zur Rolle hinzu. Auf diese Weise verfügt unser Benutzer1 über die Berechtigungen von 1, Perm2 und Perm3, und User2 verfügt über die Berechtigungen von 1, Perm3 und Perm4.
<bean id = "realm"> <Eigenschaft name = "userDefinitions"> <wert> user1 = pass1, rollen1, rollen2 user2 = pass2, rollen2, rollen3 admin = admin, admin </value> </achtzeit </Property> </bean>
@RequirePermissions können zu einer Methode hinzugefügt werden, um die Berechtigungen anzugeben, die bei der Aufruf der Methode beansprucht werden müssen. Im folgenden Code geben wir an, dass die Erlaubnis von Perm1 beim Zugriff /Perm1 besessen sein muss. Zu diesem Zeitpunkt können sowohl User1 als auch User2 zugegriffen werden.
@RequestMapping ("/perm1")@fordersPermissions ("Perm1") öffentliches Objekt Erlaubnis1 () {return "Perm1";}Wenn Sie angeben müssen, dass Sie gleichzeitig über mehrere Berechtigungen verfügen müssen, um auf eine Methode zuzugreifen, können Sie die Berechtigungen angeben, die Sie in Form eines Arrays angeben müssen (wenn Sie ein einzelnes Array -Attribut für die Annotation angeben, können Sie keine Klammern hinzufügen, aber wenn Sie Multiple -Berechtigungen angeben müssen, sind Schnellschnitte erforderlich). Beispielsweise geben wir wie folgt an, dass der Benutzer beim Zugriff auf /perm1andperm4 sowohl perm1- als auch perm4 -Berechtigungen haben muss. Zu diesem Zeitpunkt kann nur User2 darauf zugreifen, da nur es gleichzeitig Perm1 und Perm4 hat.
@RequestMapping ("/perm1andperm4")@fordersPermissions ({"Perm1", "Perm4"}) öffentliches Objekt Perm1andperm4 () {return "Perm1andperm4";}Wenn gleichzeitig mehrere Berechtigungen angegeben werden, ist die Beziehung zwischen mehreren Berechtigungen die Beziehung, dh alle gleichzeitig angegebenen Berechtigungen sind erforderlich. Wenn Sie nur eine der angegebenen mehreren Berechtigungen haben müssen, um zugänglich zu sein, können wir die Beziehung zwischen oder zwischen mehreren Berechtigungen über logical = logical.or angeben. Beispielsweise geben wir wie folgt an, dass Sie beim Zugriff auf /perm1orperm4 nur PERM1- oder PERM4 -Berechtigungen haben müssen, damit sowohl User1 als auch User2 auf diese Methode zugreifen können.
@RequestMapping ("/perm1orperm4")@fordersPermissions (value = {"Perm1", "Perm4"}, logical = logical.or) öffentliches Objekt Perm1orperm4 () {return "perm1orperm4";}@RequireSpermissions können auch in der Klasse markiert werden, was darauf hinweist, dass Sie beim Zugriff auf Methoden in der Klasse extern entsprechende Berechtigungen haben müssen. Im Folgenden geben wir beispielsweise an, dass wir auf der Klassenebene die Berechtigte über eine Erlaubnis haben müssen, während die Index () -Methode nicht angibt, dass wir Berechtigungen benötigen, aber wir müssen bei der Klassenebene auf dieser Methode weiterhin Berechtigungen angegeben haben. Zu diesem Zeitpunkt kann nur User1 darauf zugreifen.
@RastController@requestMapping ("/foo")@fordersPermissions ("Perm2") öffentliche Klasse foocontroller {@RequestMapping (methode = requestMethod.get) public Object index () {map <String, Objekt> map = new Hashmap <> (); map.put ("ABC", 123); Rückgabekarte; }}Wenn sowohl Klassen- als auch Methodenstufen @RequireSpermissionen haben, hat die Methodenebene eine höhere Priorität, und es werden nur Berechtigungen, die von der Methodenebene erforderlich sind, zu diesem Zeitpunkt überprüft. Wie folgt geben wir an, dass die PERM2 -Berechtigung auf Klassenebene und die PERM3 -Berechtigung auf Methodenebene erforderlich ist. Wenn Sie dann auf /foo zugreifen, müssen Sie nur Perm3 -Berechtigungen haben, um auf die Index () -Methode zuzugreifen. Zu diesem Zeitpunkt können sowohl User1 als auch User2 zugreifen /foo zugreifen.
@RastController @requestMapping ("/foo") @fordersPermissions ("Perm2") öffentliche Klasse fooconTroller {@RequestMapping (methode = requestMethod.get) @RequireSpermissions ("Perm3") public Object Index () {map <string, Object> map = new HaSHmap <> (); map.put ("ABC", 123); Rückgabekarte; }}Wenn wir zu diesem Zeitpunkt jedoch @RequiresRoles ("Rollen1") zum Klasse hinzufügen, um festzustellen, dass wir Rollenrolle haben müssen, müssen wir beim Zugriff auf /foo die von @RequireSPermissions ("Perm3") auf der Rollenung1 angegebene Rolle1 auf der Index () -Methode auf der Klasse haben. Da Forderung und Erfordernisse für die Erlaubnisdefinitionen verschiedener Dimensionen gehören, überprüft Shiro sie einmal während der Überprüfung. Wenn sowohl die Klasse als auch die Methode Anmerkungen derselben Art von Berechtigungskontrolldefinition haben, basiert die Definition in der Methode nur auf der Definition.
@RestController@RequestMapping("/foo")@RequiresPermissions("perm2")@RequiresRoles("role1")public class FooController { @RequestMapping(method=RequestMethod.GET) @RequiresPermissions("perm3") public Object index() { Map<String, Object> map = new HashMap<>(); map.put ("ABC", 123); Rückgabekarte; }}Obwohl das Beispiel nur für die Verwendung von Vorliegen verwendet wird, ist auch die Verwendung anderer Annotationen zur Berechnungskontrolle ähnlich. Bitte verwenden Sie andere Anmerkungen von interessierten Freunden.
Prinzip der Annotationskontrollberechtigungen
Die oben angegebenen Berechtigungen sind mit @Requirespermissions statisch. Einer der Hauptzwecke dieses Artikels ist die Einführung einer Methode, um die angegebenen Berechtigungen durch Erweiterung der Implementierung dynamisch zu gestalten. Bevor wir jedoch erweitern, müssen wir wissen, wie es funktioniert, das Implementierungsprinzip, bevor wir expandieren können. Schauen wir uns also an, wie Shiro den Frühling mit @Requirespermissions integriert. Bei der Unterstützung von Unterstützung für @RequireSPermissions definieren wir die folgende Bean, die ein Berater ist, der von staticMethodMatcherPointCutAdvisor geerbt wird. Die method -Matching -Logik ist, dass die Verarbeitungslogik nach dem Abfangen durch die entsprechenden Ratschläge angegeben wird, solange die Klasse oder Methode über mehrere Berechtigungssteuerungsanmerkungen verfügt.
<bean> <Property name = "SecurityManager" Ref = "SecurityManager"/> </bean>
Das Folgende ist der Quellcode der Autorisierungattributsourceadvisor. Wir können sehen, dass AopallianceAnnotationsAuthorizingMethodinterceptor von setAdvice () in seiner Konstruktormethode festgelegt wird, die auf der Implementierung von MethodInterceptor basiert.
public class AuthorizationAttributesourceAdvisor erweitert staticMethodMatcherPointCutAdvisor {private statische endgültige Logger log = loggerfactory.getLogger (AutorizationAttributesourceadvisor.class); private statische endgültige Klasse <? erweitert Annotation> [] authz_annotation_classes = new class [] {fordersPermissions.class, fordersroles.class, fordersuser.class, fordertguest.class, forderAuthentication.class}; geschützte SecurityManager SecurityManager = NULL; public AuthorizationAttributesourceadvisor () {setAdvice (neue aopallianceAnnotationsAuthorizingMethodinterceptor ()); } public SecurityManager GetEcurityManager () {Return SecurityManager; } public void setSecurityManager (org.apache.shiro.mgt.securityManager SecurityManager) {this.securityManager = SecurityManager; } public boolesche Übereinstimmungen (Methode Methode, Klasse Zielklasse) {Methode M = Methode; if (isautauthzannotationPresent (m)) {return true; } // Der Parameter "Methode" kann von einer Schnittstelle stammen, die nicht über die Annotation verfügt. // Überprüfen Sie, ob die Implementierung sie hat. if (targetClass! Return IsautauthzannotationPresent (m) || IsauthzannotationPresent (Zielklasse); } catch (NoSuchMethodException ignoriert) {// Standard -Rückgabewert ist falsch. Wenn wir die Methode nicht finden können, gibt es offensichtlich // keine Annotation. Verwenden Sie also einfach den Standard -Rückgabewert. }} return false; } private boolean isAuthzannotationPresent (Klasse <?> targetClazz) {für (Klasse <? Erweitert Annotation> AnnClass: Authz_annotation_classes) {Annotation a = AnnotationUtils.findannotation (TargetClazz, Annclass); if (a! = null) {return true; }} return false; } private boolean isauthzannotationPresent (Methode Methode) {für (Klasse <? Erweitert Annotation> AnnClass: Authz_annotation_classes) {Annotation a = AnnotationUtils.findannotation (Methode, Annclass); if (a! = null) {return true; }} return false; }}Der Quellcode von AopallianceAnnotationsAuthorizingMethodinterceptor ist wie folgt. Die in IT implementierte Invoke -Methode der MethodInceptor -Schnittstelle ruft die Invoke -Methode der übergeordneten Klasse auf. Gleichzeitig müssen wir feststellen, dass einige autorisierende AnnotationMethodinterceptor -Implementierungen in seiner Konstruktor -Methode erstellt wurden. Diese Implementierungen sind der Kern der Implementierung der Berechtigungssteuerung. Später werden wir die Implementierungsklasse für die Implementierung von Erlaubnisnotierungen auswählen, um die spezifische Implementierungslogik anzuzeigen.
public class aopallianceAnnotationsAuthorizingMethodinterceptor erweitert AnnotationenAuthorizingMethodinterceptor implementiert MethodInterceptor {public AopallianceAnnotationsAuthorizingMethodinterceptor () {LISTANISINISIONAnnotationMethodinterceptor> Interceptors = New ArrayList <autorisierte ANDIGINISIONSAnnotationMethodinterceTor>; // Verwenden Sie einen federspezifischen Annotation Resolver - Spring Annotationutils ist schöner als der Prozess // RAW JDK -Auflösungsprozess. AnnotationResolver Resolver = new SpringAnnotationResolver (); // Wir können die gleiche Resolver -Instanz wiederverwenden - sie behält keinen Zustand: interceptors.add (neue roleAnnotationMethodinterceptor (Resolver)); Interceptors.Add (neue BesitzernotationMethodinterceptor (Resolver)); interceptors.add (New UserAnnotationMethodinterceptor (Resolver)); interceptors.add (New GuestannotationMethodinterceptor (Resolver)); setMethodinterceptors (Interceptors); } protected org.apache.shiro.aop.Methodinvocation CreateMethodinVocation (Objekt implspecificMethodinVocation) {endgültig methodInvocation mi = (methodeInvocation) implspecificMethodinvocation; return new org.apache.shiro.aop.methodinvocation () {public methode getMethod () {return mi.getMethod (); } public Object [] getArgumente () {return mi.getarguments (); } public String toString () {return "Methode Invocation [" + mi.getMethod () + "]"; } public Object fore () wirft Throwable {return mi.procece (); } public Object Getthis () {return mi.gthis (); }}; } geschütztes Objekt ContiniNVocation (Objekt aopallianCemethodInvocation) wirft Throwable {methodInvocation mi = (methodInvocation) aopalliancemethodinvocation aus; return mi.procece (); } public Object Invoke (methodInvocation methodInvocation) löscht Throwable {org.apache.shiro.aop.MethodInvocation mi = CreateMethodinVocation (methodInvocation); return Super.invoke (MI); }}Indem wir die Implementierung der Invoke -Methode der übergeordneten Klasse betrachten, werden wir schließlich feststellen, dass die Kernlogik darin besteht, die AssertAuthorisierte Methode aufzurufen, und die Implementierung dieser Methode (Quellcode lautet wie folgt), ob die konfigurierte Autorik -AnnotationMethodinterceTor -Unterstützung die Berechtigungsverifizierung der aktuellen Methode unterstützt (mit der Beurteilung, ob es sich um die unterstützte Annotation oder Methode handelt). Bei der Unterstützung wird seine AssertAuthorisierte Methode zur Überprüfung der Berechtigte gefordert, und der autorisierende AnnotationMethodinterceptor wird die AssertAuthorisierte Methode zur autorisierenden AnnotationHandler anrufen.
Protected void AssertAuthorized (MethodInvocation methodInvocation) löst die Autorisierung von Ausnahme aus (// Standardimplementierung stellt nur sicher, dass keine Ablehnung von Stimmen abgegeben werden: Sammlung <autorikingAnnotationMethodinterceptor> aamis = getMethodinterceptors (); if (aamis! }}}}
Schauen wir uns als nächstes auf den von AopallianceAnnotationsAuthorizingMethodinterceptor definierten MermissionAnnotationMethodinterceptor zurück, der Quellcode lautet wie folgt. Kombinieren Sie den Quellcode des AopallianceAnnotationsAuthorizingMethodinterceptor und des Quellcode für die ErlaubnisannotationMethodinterceptor, dass die BerechtigungsAnnotationHandler und SpringAnnotationResolver als PermissionAnnotationMethodinterceptor spezifiziert werden. MermissionAnnotationHandler ist eine Unterklasse von AutorikingAnnotationHandler. Unsere endgültige Berechtigungskontrolle wird daher durch die AssertAuthorisierte Umsetzung der ErlaubnisanannotationHandler bestimmt.
public class bewerbenAnnotationMethodInterceptor erweitert die autorisierungsemThodinterceptor {public mericmingAnnotationMethodinterceptor () {Super (neue BesitzernotationHandler ()); } public bewerbenAnnotationMethodInterceptor (AnnotationResolver Resolver) {Super (neue BewerbungAnnotationHandler (), Resolver); }}Schauen wir uns als nächstes die Implementierung der AssertAuthorized -Methode der Immobilien -AnnotationHandler an, und der vollständige Code lautet wie folgt. Aus der Implementierung können wir feststellen, dass der konfigurierte Berechtigungswert aus der Annotation erhält, und die Annotation ist die Annotation von Anforderungsversorgungen. Darüber hinaus verwenden wir bei der Durchführung einer Berechtigungsüberprüfung direkt den Textwert, der beim Definieren der Annotation angegeben ist. Wir werden von hier aus beginnen, wenn wir es später erweitern.
public class bewerbenAnnotationHandler erweitert die autorisierungsAnnotationHandler {public bewerbenAnnotationHandler () {super (fordersPermissions.class); } Protected String [] getAnnotationValue (Annotation a) {fordersPermissions rpannotation = (fordersPermissions) a; return rpannotation.Value (); } public void assertAuthorized (Annotation a) löst die Autorisierung aus. Erfordernisse Rpannotation = (fordersPermissions) a; String [] Perms = getAnnotationValue (a); Subjekt = getUntject (); if (perms.length == 1) {Subjekt.CheckPermission (Perms [0]); zurückkehren; } if (logical.and.equals (rpannotation.logical ()) {getUbject (). CheckPermissions (Perms); zurückkehren; } if (logical.or.equals (rpannotation.logical ())) {// Vermeiden Sie es unnötig, Ausnahmen zu verarbeiten - "Verzögerung", indem Sie die Ausnahme durch den Aufruf von HasRole erster boolean hasatleastonePermission = false; Für (String -Berechtigung: Perms) if (getUntject (). isspermitted (Erlaubnis)) hasatleastonepermission = true; // Die Ausnahme bewirken, wenn keiner der Rolle übereinstimmt, beachten Sie, dass die Ausnahmemeldung ein wenig irreführend ist, wenn (! HasatleastonePermission) getUbject (). CheckPermission (Perms [0]); }}}Durch die vorherige Einführung wissen wir, dass die Annotation des Parameters der AssertAuthorized -Methode für die ErlaubnisnotationHandler übergeben wird, indem AnendotationMethodinterceptor beim Aufrufen der AssertAuthorisierten Methode zur Autorisierung von AnnotationHandler aufgerufen wird. Der Quellcode ist wie folgt. Aus dem Quellcode können wir sehen, dass Annotation durch die Getannotationsmethode erhalten wird.
public void assertAuthorized (methodInvocation mi) löst die Autorisierung ausgebildet {try {((autorizingAnnotationHandler) getHandler ()). } catch (AuthorizationException ae) {if (ae.getCause () == null) ae.initcause (neue AutorisierungException ("nicht autorisiert, die Methode aufzurufen:" + mi.getMethod ()); Ae werfen; }}Wenn wir diese Richtung entlang gehen, werden wir schließlich die Implementierung der GetAnnotation -Methode von SpringannotationResolver finden, die wie folgt implementiert wird. Wie aus dem folgenden Code hervorgeht, wird es vorgezogen, bei der Suche nach Anmerkungen nach der Methode zu suchen. Wenn es nicht auf der Methode zu finden ist, wird nach der entsprechenden Annotation aus der Klasse des aktuellen Methodenaufrufs gesucht. Von hier aus können wir auch sehen, warum diejenige, die die Methode wirkt, wenn wir die gleiche Art von Berechtigungssteuerungsannotation auf Klassen und Methode definieren, und wenn sie alleine existiert, wird derjenige wirksam.
öffentliche Klasse SpringAnnotationResolver implementiert AnnotationResolver {öffentliche Annotation getAnnotation (methodInvocation mi, Klasse <? Erweitert Annotation> Clazz) {Methode m = mi.getMethod (); Annotation a = AnnotationUtils.Findannotation (M, Clazz); if (a! = null) return a; // Das Method -Objekt des MethodInvocation könnte eine in einer Schnittstelle definierte Methode sein. // Wenn jedoch die Annotation in der Implementierung der Schnittstelle vorhanden ist (und nicht // in der Schnittstelle selbst), befindet sie sich nicht auf dem obigen Methodenobjekt. Stattdessen müssen wir die Methodendarstellung aus der Zielklasse erwerben und direkt in der Implementierung selbst überprüfen: Klasse <?> TargetClass = mi.gthis (). GetClass (); m = clasutils.get meistespezifischeMethod (M, targetClass); a = AnnotationUtils.findannotation (M, Clazz); if (a! = null) return a; // Siehe, ob die Klasse die gleiche AnnotationsrückgabeannotationUtils.findannotation (mi.Gethis (). GetClass (), clazz) hat; }} Ich glaube, dass die Leser durch das oben genannte Quellcode -Lesen ein tieferes Verständnis des Prinzips der Berechtigungskontrollanmerkungen haben, die Shiro nach der Integration des Frühlings unterstützt haben. Der oben veröffentlichte Quellcode ist nur einige der Kern, von denen der Autor für relativ zentral hält. Wenn Sie den vollständigen Inhalt im Detail kennen möchten, lesen Sie bitte den vollständigen Code selbst entlang der vom Autor genannten Ideen.
Nach dem Verständnis dieses Prinzips der Berechtigungskontrolle, die auf Anmerkungen basieren, können die Leser auch entsprechend entsprechend den tatsächlichen Geschäftsbedürfnissen erweitern.
Erweitert mit Feder -El -Ausdrücken
Angenommen, es gibt jetzt eine Schnittstelle wie die folgende, die eine Abfragemethode enthält, die einen Parametertyp empfängt. Lassen Sie es uns hier vereinfachen, vorausgesetzt, solange ein solcher Parameter empfangen wird und verschiedene Werte zurückgegeben werden.
öffentliche Schnittstelle RealService {Object Abfrage (int type); }Diese Schnittstelle ist offen für die Außenwelt. Diese Methode kann über die entsprechende URL angefordert werden. Wir definieren die entsprechende Controller -Methode wie folgt:
@RequestMapping ("/service/{type}") öffentlicher Objektabfrage (@PathVariable ("Typ") int type) {return this.realService.Query (Typ);}Der obige Schnittstellendienst verfügt über Berechtigungen für Typ bei der Durchführung von Abfragen. Nicht jeder Benutzer kann jeden Typ zum Abfragen verwenden, und es erfordert entsprechende Berechtigungen. Daher müssen wir für die obige Prozessormethode die Berechtigungsregelung hinzufügen und die während der Steuerung erforderlichen Berechtigungen dynamisch mit dem Parametertyp ändern. Nehmen wir an, dass die Definition jeder Erlaubnis des Typs die Form der Abfrage ist: Typ. Beispielsweise ist die Erlaubnis, die bei Typ = 1 erforderlich ist: 1, und die Berechtigung, die bei Typ = 2 erforderlich ist, ist Abfrage: 2. Wenn wir nicht in den Frühling integriert sind, tun wir dies wie folgt:
@RequestMapping ("/service/{type}") öffentliches Objektabfrage (@PathVariable ("Typ") int type) {SecurityUtils.getSubject (). CheckPermission ("Abfrage:" + Typ); Geben Sie dies zurück.Nach der Integration in den Frühling sind die oben genannten Praktiken jedoch stark gekoppelt, und wir würden die integrierten Annotationen eher zur Kontrolle der Berechtigungen verwenden. Für das obige Szenario möchten wir lieber die erforderlichen Berechtigungen über @RequireSpermissions angeben, aber die in @RequireSPermissions definierten Berechtigungen sind statischer Text und festgelegt. Es kann unsere dynamischen Bedürfnisse nicht erfüllen. Zu diesem Zeitpunkt denken Sie vielleicht, dass wir die Controller -Verarbeitungsmethode in mehrere Teilen und die Berechtigungen separat aufteilen können. Zum Beispiel ist Folgendes:
@RequestMapping ("/service/1")@fordersPermissions ("Abfrage: 1") öffentlicher Objekt Service1 () {return this.realService.Query (1);}@fordersPermissions ("Abfrage: 2")@requestmapping ("/service/2") öffentliche Objekt -Dienstleistungen 2 () {{{{return zurücksend this.realService.Query (2);} //...@ Requestmapping ("/Service/200")@fordersPermissions ("Abfrage: 200") öffentlicher Objekt Service200 () {return this.realService.Query (200);}Dies ist in Ordnung, wenn der Wertebereich des Typs relativ gering ist. Wenn es jedoch 200 mögliche Werte wie die oben genannten gibt, ist es etwas problematisch, sie ausführlich zu zählen, um eine separate Prozessormethode zu definieren und die Berechtigungsregelung durchzuführen. Wenn der Wert des Typs in Zukunft ändert, müssen wir außerdem eine neue Prozessormethode hinzufügen. Der beste Weg besteht darin, @RequireSpermissions dynamische Berechtigungsdefinitionen zu unterstützen und gleichzeitig die statische Definitionsunterstützung beizubehalten. Durch die vorherige Analyse wissen wir, dass der Einstiegspunkt für die Erlaubnisnotationshandler ist und keine Erweiterungen zur Überprüfung der Berechtigte bereitgestellt wird. Wenn wir es erweitern wollen, besteht die einfache Möglichkeit darin, es als Ganzes zu ersetzen. Die Berechtigungen, die wir benötigen, um dynamisch dynamisch zu verarbeiten, hängen jedoch mit Methodenparametern zusammen, und die Methodenparameter können nicht in der BerechtigungsAnnotationHandler erhalten werden. Aus diesem Grund können wir die BerechtigungsannotationHandler nicht direkt ersetzen. PermissionAnnotationHandler wird von permissionAnnotationMethodinterceptor genannt. Methodenparameter können erhalten werden, wenn die BerechtigungsannotationHandler in der assertAuthorisierten Methode seiner übergeordneten Klasse autorisierungsnotationMethodinterceptor aufgerufen wird. Aus diesem Grund wird unser Erweiterungspunkt in der ErlaubnisnotationMethodinterceptor -Klasse ausgewählt, und wir müssen ihn auch als Ganzes ersetzen. Die EL -Ausdrücke von Spring können Parameterwerte der Parsingmethode unterstützen. Hier stellen wir uns für die El -Ausdrucksformen von Spring ein. Wenn @RequirePermissions Berechtigungen definieren, können Sie Spring EL -Ausdrücke verwenden, um Methodenparameter einzuführen. Gleichzeitig, um den statischen Text zu berücksichtigen. Hier ist eine Frühlings -EL -Ausdrucksvorlage. Für die EL -Ausdrucksvorlage von Spring finden Sie in diesem Blog -Beitrag. Wir definieren unsere eigene ErlaubnisannotationMethodinterceptor, erben ihn von der ErlaubnisAnnotationMethodinterceptor und überschreiben die Methode der AssertAuthorie. Die Implementierungslogik der Methode bezieht sich auf die Logik in Bezug auf die Bewerbung, aber die Berechtigungsdefinition bei verwendeten @RequireSpermissionen ist das Ergebnis unserer Verwendung des Spring EL -Ausdrucks basierend auf der derzeit als Ergebnis der Analyse des EvaluationContext bezeichneten Methode. Das Folgende ist unsere eigene Definition der Implementierung von PermissionAnnotationMethodinterceptor.
öffentliche Klasse SelftrainmissionAnnotationMethodInterceptor erweitert die ErlaubnisAnnotationMethodinterceptor {private endgültige SpelexpressionParser Parser = new SpelexpressionParser (); private endgültige parameteramedIscoverer paramnamediscoverer = new DefaultParameTernAMEDISCoverer (); private endgültige 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]); zurückkehren; } if (Logical.AND.equals(permAnnotation.logical())) { getSubject().checkPermissions(perms); zurückkehren; } 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的权限。
Zusammenfassen
The above is a detailed explanation of Spring integrating Shiro and expanding the use of EL expressions introduced by the editor. Ich hoffe, es wird für alle hilfreich sein. Wenn Sie Fragen haben, hinterlassen Sie mir bitte eine Nachricht und der Editor wird Ihnen rechtzeitig antworten. Vielen Dank für Ihre Unterstützung auf der Wulin.com -Website!