Shiro - это легкая структура управления разрешением с широким спектром приложений. Основное внимание в этой статье уделяется внедрению интеграции Spring Shiro и включение динамических параметров, таких как @RequiresRoles, которые поддерживаются путем расширения выражений EL Spring. Введение в Широ не находится в рамках этой статьи. Если читатели мало знают о Широ, они могут изучить соответствующую информацию на своем официальном веб -сайте. Существует также статья о InfoQ, которая предоставляет всеобъемлющее введение в Shiro, а также официально рекомендуется. Его адрес https://www.infoq.com/articles/apache-shiro.
Широ интегрирует весну
Во-первых, вам нужно добавить в ваш проект Shiro-spring-xxx.jar. Если вы используете Maven для управления своим проектом, вы можете добавить следующие зависимости в свои зависимости. Вот последняя версия 1.4.0, которую я выбрал.
<depervice> <groupid> org.apache.shiro </GroupId> <artifactid> shiro-spring </artifactid> <sersive> 1.4.0 </version> </gethyserian>
Затем вам необходимо определить Shirofilter в вашем web.xml и применить его для перехвата всех запросов, которые требуют управления разрешением, обычно настраиваются как /*. Кроме того, фильтр должен быть добавлен на переднюю часть, чтобы убедиться, что запрос сначала контролируется с помощью разрешений Широ после его появления. Соответствующий класс фильтра здесь настроен с помощью делегирования FilterProxy, который является прокси -прокси -фильтром, предоставленным к весне. Вы можете использовать фасоль в контейнере Spring Bean в качестве текущего экземпляра фильтра, и соответствующий фасоль примет фасоль, соответствующий фильтрующим имени. Таким образом, в следующей конфигурации будет искать фасоль с именем Shirofilter в контейнере бобов.
<filter> <filter-name> shirofilter </filter-name> <filter-class> org.springframework.web.filter.delegatingfilterproxy </filter-class> <init-param> <param-name> targetfilterlifecycle </param-name> <param-value> true </param-value> </init-param> </filter> <фильтровая картирование> <Filter-name> shirofilter </filter-name> <url-pattern>/*</url-pattern> </filter-картирование>
При самостоятельном использовании Shiro, вы обычно определяете org.apache.shiro.web.servlet.shirofilter, чтобы сделать что -то подобное.
Далее следует определить наш Shirofilter в контейнере бобов. Следующим образом, мы определяем ShirofilterFactoryBean, который будет производить фасоль AbstractShiroFilter Type. Через ShirofilterFactoryBean мы можем указать SecurityManager. Используемый здесь DefaultWebSecurityManager должен указать сферу. Если необходимо указать несколько сфер, это указывается через сферы. Для простоты мы используем TextConfigurationRealm на основе определения текста напрямую. Используйте LoginUrl, чтобы указать адрес входа, SucksureUrl, чтобы указать адрес, который необходимо перенаправить после успешного входа в систему, и UnauthorizedUrl, чтобы указать страницу быстрого приглашения, когда разрешения недостаточны. FilterChainDefinitions определяет взаимосвязь между URL -адресом и фильтром, который будет использоваться. Фильтр псевдоним на правой стороне равного знака является псевдонимом фильтра. Псевдоним по умолчанию определяется в классе перечисления org.apache.shiro.web.filter.mgt.defaultfilter.
<bean id="shiroFilter"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="/login.jsp"/> <property name="successUrl" value="/home.jsp"/> <property name="unauthorizedUrl" value="/unauthorized.jsp"/> <property name="unauthorizedUrl" value = "/unauthorized.jsp"/> <name = "filterChainDefinitions"> <dulch>/admin/** = Authc, Roles [admin]/logout = logout # другие адреса требуют, чтобы пользователь зашел в систему/** = Authc, logger </value> </property> </bean> <bean id = "SecurityManager"> </value> </property> </bean> <bean id = "> </propertive> </bean> <Bean Id =" ref = "realm"/> </bean> <bean id = "LifeCyclebeanpostProcessor"/>
<!-Для простоты мы будем использовать текстовую реализацию Realm здесь-> <bean id = "realm"> <name = "userDefinitions"> <dust> user1 = pass1, role1, role2 user2 = pass2, role2, role3 admin = admin, admin </value> </property> </bean> pass2, role2, role3 = admin, admin </value> </property> </bean> pass2, role2, role3 = admin, admin </value> </propetion> </bean> </bean.
Если вам необходимо использовать пользовательский фильтр в определении FilterChainDefinitions, вы можете указать пользовательский фильтр и его отношения с псевдонимом через фильтры ShirofilterFactorybean. Например, как показано ниже, мы добавили фильтр с псевдонимом Logger и указанный /** Фильтр с псевдонимом Logger в FilterChainDefinitions.
<bean id="shiroFilter"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="/login.jsp"/> <property name="successUrl" value="/home.jsp"/> <property name="unauthorizedUrl" value="/unauthorized.jsp"/> <property name="unauthorizedUrl" value = "/unauthorized.jsp"/> <name = "filters"> <util: map> <intry key = "logger"> <bean/> </intrent> </util: map> </properation> <name = "filterchaindefinitions"> <duld>/** = aTherc, admin [addry]/addrout = worgout # addraged andredes =******************** Authc, logger </value> </property> </bean>
Фактически, определение псевдонима фильтра, которое мы должны применить, также может быть определено непосредственно с помощью ShirofilterFactoryBean's SetFilters (), но непосредственно определить соответствующий фильтр, соответствующий компонентам в соответствующем контейнере из бобов. Поскольку по умолчанию Shirofilterfactorybean зарегистрирует все бобы типа фильтра в контейнере из бобов с помощью псевдоним ID в фильтрах. Следовательно, приведенное выше определение эквивалентно следующему.
<bean id="shiroFilter"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="/login.jsp"/> <property name="successUrl" value="/home.jsp"/> <property name="unauthorizedUrl" value="/unauthorized.jsp"/> <property name="unauthorizedUrl" value = "/unauthorized.jsp"/> <name = "filterChainDefinitions"> <dulch>/admin/** = Authc, Roles [admin]/logout = logout # другие адреса требуют, чтобы пользователь был вошел в систему/** = Authc, logger </value> </property> </bean> <bean id = "/>
После вышеуказанных шагов интеграция Shiro и Spring завершена. В настоящее время любой путь, который мы просили для проекта, потребует от нас входа в систему, и автоматически перейдет на путь, указанный в LoginUrl, и позвольте нам ввести имя пользователя/пароль для входа в систему. Имя пользователя/пароль, используемый при входе в систему, - это имя пользователя/пароль, который мы определили в TextConfigurationRealm. На основе нашей вышеупомянутой конфигурации вы можете использовать user1/pass1, admin/admin и т. Д. После успешного входа в систему он перейдет на адрес, указанный параметром SuccessUrl. Если мы вошли в систему с использованием user1/pass1, мы также можем попытаться получить доступ/admin/index, и в настоящее время мы перейдем к несанкционированному.jsp из -за недостаточных разрешений.
Включить поддержку на основе аннотаций
Основная интеграция требует от нас определения всех элементов управления разрешением, которые необходимо применять в фильтровании Shirofilterfactorybean. Иногда это не так гибко. Широ предоставляет нам аннотации, которые можно использовать после интеграции пружины. Это позволяет нам добавлять соответствующие аннотации в класс или метод, которые требуют контроля разрешения для определения разрешений, необходимых для доступа к классу или методу. Если он находится в классе в определении, это означает, что вызов всех методов в классе требует соответствующих разрешений (обратите внимание, что это необходимо быть внешним вызовом, который является ограничением динамического прокси). Чтобы использовать эти аннотации, нам необходимо добавить следующие два определения бобов в контейнер Spring's Bean, чтобы мы могли определить, есть ли пользователь соответствующие разрешения на основе определения аннотации во время выполнения. Это достигается с помощью механизма AOP Spring. Если вы ничего не знаете о Spring AOP, вы можете ссылаться на «Столбунную введение в Spring AOP», написанную автором. В следующих двух определениях бобов Authorizationattributesourceadvisor определяет консультант, который будет перехватывать и проверять разрешения на основе метода конфигурации аннотации, предоставленного Shiro. DefaultAdvisorautoproxycreator предоставляет функцию создания объектов прокси для класса, помеченных с аннотаций управления разрешением, предоставленными Shiro, и применения Authorizationattributesourceadvisor при перехвате целевого вызова метода. Когда запрос от пользователя перехвачен, и пользователь не имеет разрешения, отмеченного в соответствующем методе или классе, ARG.Apache.shiro.authz.AuthorizationException исключение будет добавлено.
<bean зависит от = "LifeCyclebeanpostprocessor"/> <Bean> <name = "securityManager" Ref = "SecurityManager" // </bean>
Если <aop:config/>或<aop:aspectj-autoproxy/> уже определено в нашем контейнере для бобов, то DefaultAdvisorautoproxycreator больше не может быть определен. Потому что предыдущие два случая автоматически добавят бобы, аналогичные DefaultAdvisorautoproxycreator. Для получения дополнительной информации о DefaultAdvisorautoproxycreator вы также можете обратиться к принципу автора автоматического создания прокси -объектов в Spring Aop.
Аннотации контроля разрешений, предоставленные Shiro, следующие:
Требуется обстоятельство. Пользователь должен быть аутентифицирован в текущем сеансе, то есть ему необходимо войти в систему с именем пользователя/паролем и не включает в себя автоматический логин.
Требуется, что пользователь должен быть аутентифицирован. Он может быть аутентифицирован путем входа в систему с именем пользователя/паролем в этом сеансе, или он может быть автоматически войти в систему с Mamesme.
Требуется Guest: пользователь не вошел в систему.
Требуется море: пользователь требует, чтобы указанная роль была принадлежит.
Требуется перемещения: пользователь требует указанных разрешений.
Первые три легко понять, а последние два похожи. Здесь я использую @requirespermissions в качестве примера. Во -первых, давайте изменим сферу, определенное выше, и добавим разрешения к роли. Таким образом, наш пользователь1 будет иметь разрешения на пермеру1, пермими и пермими, а пользователь2 будут иметь разрешения на Perm1, Perm3 и Perm4.
<bean id = "realm"> <name = "userdefinitions"> <duster> user1 = pass1, role1, role2 user2 = pass2, role2, role3 admin = admin, admin </value> </property> <name = "roledefinitions"> <value> role1 = perm1, perm2 = perm1, perm1 rolefinitions "> perm </value> <//value> <//value> <//value> <//value> <//value> <//value> </////eeн.
@Requirespermissions можно добавить в метод для указания разрешений, которые необходимо претендовать при вызове метода. В следующем коде мы указываем, что разрешение Perm1 должно быть одержимы при доступе /perm1. В настоящее время можно получить доступ к пользователю1 и user2.
@Requestmapping ("/perm1")@tearypermissions ("perm1") public Object разрешение1 () {return "perm1";}Если вам нужно указать, что у вас должно быть несколько разрешений одновременно для доступа к методу, вы можете указать разрешения, необходимые для указания в форме массива (при указании одного атрибута массива на аннотации вы не можете добавлять брекеты, но когда вам нужно указать несколько разрешений, необходимы брекеты). Например, следующим образом, мы указываем, что при доступе /perm1andperm4 пользователь должен иметь разрешения как PRM1, так и Perm4. В настоящее время только user2 может получить к нему доступ, потому что только он имеет perm1 и perm4 одновременно.
@Requestmapping ("/perm1andperm4")@tearypermissions ({"perm1", "perm4"}) public object perm1andperm4 () {return "perm1andperm4";}Когда в одно и то же время указано несколько разрешений, взаимосвязь между несколькими разрешениями - это отношение, то есть все разрешения, указанные в одно и то же время. Если вам нужно иметь только одно из указанных многочисленных разрешений, мы можем указать взаимосвязь между или между несколькими разрешениями через Logical = logical.or. Например, следующим образом, мы указываем, что при доступе /пермерурперм44 вам необходимо иметь только разрешения Perm1 или Perm4, чтобы как пользователь1, так и пользователь2 могли получить доступ к этому методу.
@RequestMapping ("/perm1orperm4")@tearypermissions (value = {"perm1", "perm4"}, logical = logical.or) public object perm1orperm4 () {return "perm1orperm4";}@Requirespermissions также может быть помечена в классе, указывая на то, что при обращении к методам в классе вам необходимо иметь соответствующие разрешения. Например, в следующем, мы указываем, что нам нужно иметь разрешение Perm2 на уровне класса, в то время как метод index () не указывает, что нам нужны какие -либо разрешения, но нам все еще нужно иметь разрешения, указанные на уровне класса при доступе к этому методу. В настоящее время только пользователь1 может получить к нему доступ.
@Restcontroller@requestmapping ("/foo")@tearypermissions ("perm2") public class foocontroller {@requestmapping (method = requestmethod.get) public index () {map <string> map = new hashmap <> (); map.put ("abc", 123); карта возврата; }}Когда уровни классов и методов имеют @requirespermissions, уровень метода имеет более высокий приоритет, и в настоящее время будут подтверждены только разрешения, требуемые уровнем метода. Далее, мы указываем, что разрешение Perm2 требуется на уровне класса, и на уровне метода требуется разрешение Perm3. Затем при доступе /FOO вам необходимо иметь только разрешения Perm3 для доступа к методу index (). Таким образом, в это время и пользователь1, и пользователь2 могут получить доступ /foo.
@Restcontroller @requestmapping ("/foo") @tearypermissions ("perm2") public class foocontroller {@requestmapping (method = requestmethod.get) @requirespermissions ("perm3") Index () {map <string, object> map = new hashmap <> (); map.put ("abc", 123); карта возврата; }}Однако, если мы добавим @RequiresRoles («role1») в класс в настоящее время, чтобы указать, что нам необходимо иметь Role1 Role1, то при доступе /foo мы должны указать ROLE1, указанный @Requirespermissions («perm3») на ROLE1 на методе индекса () на классе. Поскольку требуемые морожены и необходимые перемещения принадлежат к определениям разрешений различных измерений, Широ проверит их один раз во время проверки, но если у класса и метода есть аннотации одного и того же типа определения управления разрешением, определение в методе будет основано только на определении.
@Restcontroller@requestmapping ("/foo")@tearypermissions ("umm2")@tearsroles ("role1") public class foocontroller {@requestmapping (method = requestmethod.get) @requirespermissions ("perm3") public index () {map <string> map = new hashmap <> ();); map.put ("abc", 123); карта возврата; }}Несмотря на то, что пример используется только для перемещения, использование других аннотаций контроля разрешений также аналогично. Пожалуйста, используйте другие аннотации заинтересованными друзьями.
Принцип разрешений на контроль аннотаций
Разрешения, которые мы указали выше, статичны с использованием @requirespermissions. Одна из основных целей настоящей статьи - ввести метод, чтобы сделать указанные разрешения динамической, путем расширения реализации. Но прежде чем мы расширимся, мы должны знать, как это работает, то есть принцип реализации, прежде чем мы сможем расширить. Итак, давайте посмотрим, как Широ интегрирует Spring с @requirespermissions. При поддержке поддержки @Requirespermissions мы определяем следующий фасоль, который является советником, который унаследован от StaticMethodMatcherPointCutadVisor. Его логика сопоставления метода заключается в том, что до тех пор, пока класс или метод имеет несколько аннотаций управления разрешением Shiro, логика обработки после перехвата определяется соответствующими советами.
<Bean> <name = "SecurityManager" Ref = "SecurityManager"/> </bean>
Ниже приведен исходный код Authorizationattributesourceadvisor. Мы можем видеть, что в своем методе конструктора AopallianceannotationsAuthorizingMethodinterceptor определяется SetAdvice (), который основан на реализации MethodInterceptor.
Public Class Authorizationattributesourceadvisor Extends staticmethodmatcherpointcutadvisor {private Static Final Logger log = loggerFactory.getLogger (AuthorizationAttributesourceadvisor.class); Частный статический финальный класс <? Extends Annotation> [] Authz_annotation_classes = новый класс [] {tearypermissions.class, tressroles.class, tresser.class, tressguest.class, tressauthentication.class}; Защищенный SecurityManager SecurityManager = null; public Authorizationattributesourceadvisor () {setAdvice (новый aopallianceannotationsauthorizingmethodinterceptor ()); } public SecurityManager getSecurityManager () {return SecurityManager; } public void setSecurityManager (org.apache.shiro.mgt.securitymanager SecurityManager) {this.securityManager = SecurityManager; } public boolean matches (метод метода, класс TargetClass) {method m = method; if (isauthzannotationpresent (m)) {return true; } // Параметр «метода» может быть из интерфейса, который не имеет аннотации. // Проверьте, есть ли это реализация. if (targetClass! = null) {try {m = targetClass.getMethod (m.getName (), m.getParameterTypes ()); Вернуть isauthzannotationpresent (m) || isauthzannotation present (targetclass); } catch (nosuchmethodexception игнорируется) {// возвращаемое значение по умолчанию является false. Если мы не можем найти метод, то, очевидно, // нет аннотации, поэтому просто используйте возвратное значение по умолчанию. }} вернуть false; } Частный логический isAuthzannotationpresent (class <?> targetClazz) {for (class <? Extends Annotation> annclass: authz_annotation_classes) {аннотация a = annotationutils.findannotation (targetClazz, annclass); if (a! = null) {return true; }} вернуть false; } Частный логический isAuthzannotationpresent (метод метода) {for (class <? Extends Annotation> annclass: authz_annotation_classes) {аннотация a = annotationutils.findannotation (метод, annclass); if (a! = null) {return true; }} вернуть false; }}Исходный код Aopallianceannotationsauthorizingmethodinterceptor заключается в следующем. Метод Invoke интерфейса MethodInterceptor, реализованный в IT, вызывает метод Invoke Parent Class. В то же время мы должны видеть, что некоторые реализации авторизациинтоотрации Methodinterceptor были созданы в его методе конструктора. Эти реализации являются основой реализации контроля разрешений. Позже мы выберем класс реализации MorcissionAnnotationMethodinterceptor, чтобы увидеть ее конкретную логику реализации.
открытый класс Aopallianceannotationsauthorizingmethodinterceptor расширяет аннотации. // Использование специфического для пружины аннотации - Annotationutils Spring приятнее, чем процесс разрешения RAW JDK. AnnotationResolver Resolver = New SpringannotationResolver (); // Мы можем повторно использовать тот же экземпляр резолвера - он не сохраняет состояние: receptors.add (new Roleannotationmethodinterceptor (Resolver)); Interceptors.Add (новая resmissionAnnotationmethodinterceptor (resolver)); Interceptors.Add (новый пользовательский обмен MethodInterceptor (Resolver)); Interceptors.Add (New GuestAnnotationMethodinterceptor (Resolver)); SetMethodinterceptors (Interceptors); } Защищенный org.apache.shiro.aop.methodinvocation createmethodinvocation (объект Implyspecificmeficmethodinvocation) {final Methodinvocation mi = (methodInvocation) ImplySpecificMethodinVocation; вернуть новый 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 option () бросает Throwable {return mi.proecd (); } public Object getThis () {return mi.getThis (); }}; } Защищенный объект Продолжить INVOCTACE (Object Aopalliancemethodinvocation) бросает throwable {methodInvocation mi = (methodInvocation) aopalliancemethodinvocation; вернуть mi.proecd (); } public Object Invoke (MethodInvocation MethodInvocation) бросает Throwable {org.apache.shiro.aop.methodinvocation mi = createmethodinvocation (methodInvocation); вернуть Super.invoke (MI); }}Рассматривая реализацию метода Anloke Parent Class, мы наконец увидим, что основной логикой является вызвать метод Assertauthorized, и реализация этого метода (исходный код является следующим образом) заключается в определении того, поддерживает ли он настроенный анонация Methodinterceptor. При поддержке, его Assertauthorized Method будет вызван для проверки разрешения, и авторизационное анонация Methodinterceptor, в свою очередь, назовет AssertAuthorized Method of AuthorizingannotationHandler.
Защищенная void assertauthorized (methodinvocation methodinvocation) выбрасывает AuthorizationException {// Реализация по умолчанию просто гарантирует, что не являются отрицания голосов: Collection <AuthorizingAnnotationMethodinterceptor> aamis = getMethodinterceptors (); if (aamis! = null &&! aamis.isempty ()) {for (AuthorizingAnnotationMethodinterceptor aami: aamis) {if (aami.supports (methodInvocation)) {aami.asserTauthorized (methodInvocation); }}}}Далее, давайте посмотрим на разрешающий аноматметодцептор, определяемый Aopallianceannotationsauthorizingmethodinterceptor, исходный код заключается в следующем. Комбинируя исходный код Aopallianceannotationsauthorizingmethodinterceptor и исходного кода romsissionAnnotationmethodinterceptor, мы видим, что resmissionAnnotationHandler и SpringannotationResolver указаны в ressissionAnnotationmethodinterceptor. RessissionAnnotationHandler - это подкласс авторизациинхангандлер. Таким образом, наш окончательный контроль разрешений определяется ассистентной реализацией ressissionAnnotationHandler.
Public Class RemissionAnnotationMethodInterceptor расширяет AuthorizingAnnotationMethodInterceptor {public ressissionAnantationmethodinterceptor () {super (new RemissionAnnotationHandler ()); } public ormissionAnnotationMethodInterceptor (AnnotationResolver Resolver) {Super (New RemissionAnnotationHandler (), Resolver); }}Затем давайте посмотрим на реализацию Assertauthorized Method of RemissionAnnotationHandler, и полный код заключается в следующем. Из реализации мы видим, что она получит настроенное значение разрешения от аннотации, и аннотация здесь является аннотацией TrenyPermissions. Более того, при выполнении проверки разрешений мы напрямую используем текстовое значение, указанное при определении аннотации. Мы начнем отсюда, когда мы расширим его позже.
public class ormissionAnnotationHandler расширяет авторизациюнгангандлер {public ormissionAnnotationHandler () {super (tearspermissions.class); } защищенная строка [] getAnnotationValue (аннотация a) {Tevelpermissions rpannotation = (TearsPermissions) a; return rpannotation.value (); } public void assertAuthorized (аннотация a) бросает AuthorizationException {if (! (экземпляр требует перемещения)) return; Требуется perspermissions rpannotation = (требует перемещения) 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 ())) {// избегать обработки исключений неоправданно - «задержка», бросая исключение, вызывая Hasrole First Boolean HasatLeastonePermission = false; для (строковое разрешение: perms) if (gestubject (). IsperTited (разрешение))) hasatLeastOnepermission = true; // вызвать исключение, если ни одна из роли не соответствует, обратите внимание, что сообщение об исключении будет немного вводящим в заблуждение, если (! HasatLeastonepermission) getSubject (). CheckPermission (perms [0]); }}}Благодаря предыдущему введению мы знаем, что аннотация параметра Assertauthorized Method of RemissisionAnnotation проходит путем авторизации анонацииметодинтерцептора при вызове Assertauthorized метода авторизациинхандера. Исходный код выглядит следующим образом. Из исходного кода мы видим, что аннотация получена с помощью метода Getannotation.
public void assertauthorized (MethodInvocation mi) бросает AuthorizationException {try {((AuthorizingAnationHandler) gethandler ()). AssertAuthorized (Getannotation (Mi)); } catch (AuthorizationException ae) {if (ae.getCaue () == null) ae.initcause (new AuthorizationException («Не разрешено вызвать метод:» + mi.getMethod ())); бросить AE; }}Пройдя по этому направлению, мы в конечном итоге обнаружим реализацию метода Getannotation в SpringannotationResolver, которая реализуется следующим образом. Как видно из следующего кода, предпочтительно искать метод при поиске аннотаций. Если он не найден на методе, он будет искать соответствующую аннотацию из класса текущего вызова метода. Отсюда мы также можем понять, почему тот, который вступает в силу на методе, когда мы определяем тот же тип аннотации управления разрешением на классе и методе ранее, и когда он существует в одиночку, определяется, что вступает в силу.
открытый класс SpringannotationResolver реализует AnnotationResolver {public annotation getAnnotation (methodInvocation mi, class <? Extends Annotation> clazz) {method m = mi.getmethod (); Аннотация a = annotationutils.findannotation (m, clazz); if (a! = null) вернуть a; // объект метода MethodInvocation может быть методом, определенным в интерфейсе. // Однако, если аннотация существовала в реализации интерфейса (а не // сам интерфейс), оно не будет на вышеуказанном объекте метода. Вместо этого нам нужно // приобрести представление метода из TargetClass и проверить непосредственно на самой реализации //: class <?> TargetClass = mi.getThis (). GetClass (); m = classutils.get Sost Specificificmethod (M, TargetClass); a = annotationutils.findannotation (m, clazz); if (a! = null) вернуть a; // Посмотрите, имеет ли класс одинаковую аннотацию returntationutils.findannotation (mi.getThis (). GetClass (), clazz); }} Благодаря чтению исходного кода, я считаю, что читатели имеют более глубокое понимание принципа аннотаций контроля разрешений, поддерживаемых Широ после интеграции пружины. Исходный код, размещенный выше, - это лишь некоторые из основных, которые автор считает относительно основными. Если вы хотите узнать полный контент подробно, пожалуйста, прочитайте полный код самостоятельно по идеям, упомянутым автором.
После понимания этого принципа контроля разрешений на основе аннотаций читатели также могут расширяться в соответствии с фактическими потребностями бизнеса.
Расширен с использованием Spring EL Expressions
Предположим, теперь есть интерфейс, подобный следующему, который имеет метод запроса, который получает тип параметра. Давайте упростим его здесь, предполагая, что до тех пор, пока такой параметр получен и будут возвращены различные значения.
публичный интерфейс RealService {Object Query (int type); }Этот интерфейс открыт для внешнего мира. Этот метод может быть запрошен через соответствующий URL. Мы определяем соответствующий метод контроллера следующим образом:
@RequestMapping ("/service/{type}") public Object Query (@pathvariable ("type") int type) {return this.realservice.query (type);}Приведенная выше служба интерфейса имеет разрешения на тип при проведении запроса. Не каждый пользователь может использовать каждый тип для запроса, и это требует соответствующих разрешений. Следовательно, для приведенного выше метода процессора нам необходимо добавить управление разрешением и разрешения, требуемые во время управления динамическим изменением с типом параметра. Предположим, что определение каждого разрешения типа является формой запроса: тип. Например, разрешение, необходимое, когда тип = 1 - это запрос: 1, и разрешение, необходимое, когда тип = 2 - запрос: 2. Когда мы не интегрированы с пружиной, мы делаем это следующим образом:
@Requestmapping ("/service/{type}") public Query (@pathvariable ("type") int type) {securityUtils.getSubject (). CheckPermission ("Query:" + type); вернуть это. realService.query (type);}Однако после интеграции с пружиной вышеуказанные практики сильно связаны, и мы бы предпочли использовать интегрированные аннотации для контроля разрешений. Для приведенного выше сценария мы бы предпочли указать необходимые разрешения через @requirespermissions, но разрешения, определенные в @requirespermissions, являются статическим текстом и фиксированными. Это не может удовлетворить наши динамические потребности. В настоящее время вы можете подумать, что мы можем разделить метод обработки контроллера на несколько и контролировать разрешения отдельно. Например, следующее:
@RequestMapping ("/service/1")@TransePermissions ("Query: 1") Общественный объект Service1 () {return this.RealService.query (1);}@Tevelpermissions ("Query: 2")@requestMapping ("/service/2") Service2 () {возврат this.RealService.query (2);} //...@ cheardmapping ("/service/200")@Tresspermissions ("Query: 200") Service Service200 () {return this.RealService.Query (200);}Это нормально, когда диапазон значений типа относительно невелик, но если существует 200 возможных значений, таких как вышеперечисленное, будет немного трудно перечислить их, чтобы определить отдельный метод процессора и выполнить управление разрешением. Кроме того, если значение типа изменяется в будущем, мы должны добавить новый метод процессора. Таким образом, лучший способ - сделать @requirespermissions поддерживать определения динамических разрешений, сохраняя при этом поддержку статического определения. Благодаря предыдущему анализу мы знаем, что входная точка является разрешением и не предоставляет расширения для проверки разрешения. Если мы хотим расширить его, простой способ - заменить его в целом. Тем не менее, разрешения, которые нам необходимы для динамического обработки, связаны с параметрами метода, и параметры метода не могут быть получены в разрешении. По этой причине мы не можем непосредственно заменить разрешение на себя. RescissionAnnotationHandler вызывается с помощью ressisseAnantationmethodinterceptor. Параметры метода могут быть получены, когда разрешается анонация и называется в ассистентном методе его родительского класса. По этой причине наша точка расширения выбирается в классе romordingAnantationMethodinterceptor, и нам также необходимо заменить его в целом. Выражения EL Spring могут поддерживать значения параметров метода анализа. Здесь мы решаем представить Spring's El Expressions. Когда @RequiresPermissions определяет разрешения, вы можете использовать Spring EL Expressions для введения параметров метода. В то же время, чтобы принять во внимание статический текст. Вот шаблон выражения Spring EL. Для Spring's El Sempression Semplate, пожалуйста, обратитесь к этому сообщению в блоге. Мы определяем наш собственный rompressionAnnotationmethodinterceptor, наследуем его от resmissionAnnotationmethodinterceptor и переопределяем AssertAuthoried метод. Логика реализации метода относится к логике в разрешении на инанотацию, но используемое определение разрешений в @RequiresPermissions является результатом нашего использования Spring EL Expression, основанного на в настоящее время называемом методом в результате анализа разбора Context. Ниже приведено наше собственное определение реализации rompersisantationmethodinterceptor.
public class selfpermissionAnnotationmethodinterceptor расширяет rescissionAnnotationmethodinterceptor {private final spelexpressionParsparser parser = new spelexpressionParser (); Частный окончательный параметров ParametermAdeScoverer paramnamediscoverer = new DefaultParameterNamedIscoverer (); Private Final TemplateParsercontext TemplateParsercontext = новый 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. Я надеюсь, что это будет полезно для всех. If you have any questions, please leave me a message and the editor will reply to you in time. Большое спасибо за вашу поддержку сайту wulin.com!