在 Vaadin 14 Web 应用程序中发生视图 class 实例化之前,如何拦截检查授权的请求?
How can I intercept a request to check authorization before the view class instantiation occurs in a Vaadin 14 web app?
我正在尝试将 Apache Shiro's Annotation-based Authorization (authz) 集成到 Vaadin 14 网络应用程序中。我研究了一些关于这个主题的公开解决方案:
- ILAY - authorization for Vaadin;
- Secure Router Navigation(来自为 Vaadin 应用程序设置 Spring 安全性教程)。
他们似乎都使用相同的解决方案,设置一个 VaadinServiceInitListener
to add a BeforeEnterEvent
必须实现所有 authz 逻辑的侦听器。
我认为这种解决方案会出现一个问题:
Since Vaadin views are built from the class constructor, the target view class as already been instantiated when reaching the BeforeEnterEvent
authz validation, which means that the view is processed whether or not the requester has authorization to access it.
如何在实例化 View
类 之前拦截请求,以便我可以使用 Shiro 的注释进行适当的 authz 检查?
在导航目标 class 实例化之前,目前没有任何好的拦截导航请求的方法。
作为一般解决方法,您应该确保您没有在 class 的构造函数中执行任何可能具有安全隐患的操作,而是使用 onAttach
等方法,因为它们将被调用仅在 BeforeEnterEvent
被调度后且仅当没有侦听器阻止导航时。
最近通过 issue #4595 在核心框架中解决了这个问题。尚未决定在哪个版本的 Vaadin 中引入更改,因为它们在某些情况下会影响向后兼容性。
由于 Vaadin 不提供 "any good way of intercepting navigation requests before the navigation target class is instantiated",我走了漫长而曲折的道路,想出了一个基于自定义 Interceptor
的 解决方案。这允许我在视图 class 实例化发生之前短路请求,结果如下:
优点:
安全性 - 未经授权的用户不会执行代码;
性能 - 由于我之前执行了 authz 验证,因此 运行 不必要代码的所有开销都消失了。
- 缺点:
- 需要使用额外的注解。如果有一个解决方案可以与现有的 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
注释的方法,这样对 classes 的构造函数的调用被 @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>
这总结了解决方案的主要部分。以下屏幕截图列出了我为使其工作而实施的所有 class 文件。基本上我不得不 复制 Shiro's logic related to MethodInvocation
to make it work for ConstructorInvocation
. The two Error
classes are implementation of Vaadin's Router Exception Handling for Shiro's AuthorizationException
and UnauthenticatedException
的一部分,当 authz 验证失败时抛出。
我正在尝试将 Apache Shiro's Annotation-based Authorization (authz) 集成到 Vaadin 14 网络应用程序中。我研究了一些关于这个主题的公开解决方案:
- ILAY - authorization for Vaadin;
- Secure Router Navigation(来自为 Vaadin 应用程序设置 Spring 安全性教程)。
他们似乎都使用相同的解决方案,设置一个 VaadinServiceInitListener
to add a BeforeEnterEvent
必须实现所有 authz 逻辑的侦听器。
我认为这种解决方案会出现一个问题:
Since Vaadin views are built from the class constructor, the target view class as already been instantiated when reaching the
BeforeEnterEvent
authz validation, which means that the view is processed whether or not the requester has authorization to access it.
如何在实例化 View
类 之前拦截请求,以便我可以使用 Shiro 的注释进行适当的 authz 检查?
在导航目标 class 实例化之前,目前没有任何好的拦截导航请求的方法。
作为一般解决方法,您应该确保您没有在 class 的构造函数中执行任何可能具有安全隐患的操作,而是使用 onAttach
等方法,因为它们将被调用仅在 BeforeEnterEvent
被调度后且仅当没有侦听器阻止导航时。
最近通过 issue #4595 在核心框架中解决了这个问题。尚未决定在哪个版本的 Vaadin 中引入更改,因为它们在某些情况下会影响向后兼容性。
由于 Vaadin 不提供 "any good way of intercepting navigation requests before the navigation target class is instantiated",我走了漫长而曲折的道路,想出了一个基于自定义 Interceptor
的 解决方案。这允许我在视图 class 实例化发生之前短路请求,结果如下:
优点:
安全性 - 未经授权的用户不会执行代码;
性能 - 由于我之前执行了 authz 验证,因此 运行 不必要代码的所有开销都消失了。
- 缺点:
- 需要使用额外的注解。如果有一个解决方案可以与现有的 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
注释的方法,这样对 classes 的构造函数的调用被 @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>
这总结了解决方案的主要部分。以下屏幕截图列出了我为使其工作而实施的所有 class 文件。基本上我不得不 复制 Shiro's logic related to MethodInvocation
to make it work for ConstructorInvocation
. The two Error
classes are implementation of Vaadin's Router Exception Handling for Shiro's AuthorizationException
and UnauthenticatedException
的一部分,当 authz 验证失败时抛出。