Поскольку Vaadin не предлагает «какого-либо хорошего способа перехвата запросов навигации до создания экземпляра целевого класса навигации», я выбрал длинный и извилистый путь и предложил решение на основе пользовательского Interceptor
. Это позволило мне замкнуть запросы до того, как произойдет создание экземпляра класса представления, со следующими видимыми результатами:
Плюсы:
Безопасность - код не выполняется пользователями, не авторизованными для этого;
Производительность - поскольку я выполняю проверку подлинности ранее, все накладные расходы выполнения ненужного кода больше нет.
- Минусы:
- Необходимость использования дополнительной аннотации. Было бы здорово иметь решение для бесшовной интеграции с существующими аннотациями Shiro, приветствуются любые улучшения.
Решение
Добавление нового @ShiroSecured
аннотация для связывания с перехватчиком:
package misc.app.security;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.interceptor.InterceptorBinding;
@Inherited
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
@InterceptorBinding
public @interface ShiroSecured { }
Добавьте ShiroSecuredInterceptor
и привяжите его к ранее определенной пользовательской аннотации. Убедитесь, что метод помечен @AroundConstruct
, поэтому вызовы конструктора классов, помеченные @ShiroSecured
, перехватываются:
package misc.app.security;
import javax.interceptor.AroundConstruct;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
/**
* An interceptor for declarative security checks using the annotations from the
* {@code org.apache.shiro.authz.annotation} package.
*
*/
@ShiroSecured @Interceptor
public class ShiroSecuredInterceptor extends AnnotationsAuthorizingConstructorInterceptor {
@AroundConstruct
public Object checkAuthorization(final InvocationContext ic) throws Exception {
assertAuthorized(new InvocationContextToConstructorInvocationConverter(ic));
return ic.proceed();
}
private static class InvocationContextToConstructorInvocationConverter implements ConstructorInvocation {
private final InvocationContext context;
public InvocationContextToConstructorInvocationConverter(InvocationContext ic) {
context = ic;
}
@Override
public Object proceed() throws Throwable {
return context.proceed();
}
@Override
public Class getClazz() {
return context.getConstructor().getDeclaringClass();
}
}
}
Зарегистрируйте перехватчик в beans.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<beans bean-discovery-mode="all" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd">
<interceptors>
<class>misc.app.security.ShiroSecuredInterceptor</class>
</interceptors>
</beans>
Это суммирует основные части решения. На следующем снимке экрана перечислены все файлы классов, которые я реализовал, чтобы заставить его работать. В основном мне пришлось дублировать часть логики Широ c, относящихся к MethodInvocation
, чтобы она работала для ConstructorInvocation
. Два Error
класса являются реализацией обработки исключений маршрутизатора Vaadin для Широ AuthorizationException
и UnauthenticatedException
, создаваемых при сбое проверки подлинности.