如何在 Vaadin 14 LTS 中正确处理拒绝访问

How to handle Access Denied properly in Vaadin 14 LTS

我开始为我们用 Spring Boot (2.2.6.RELEASE)Vaadin 14 LTS (14.6.1) 编写的应用程序实施身份验证和授权。

我关注了这些资源:

我有检查登录用户是否有权访问指定资源的代码,在 beforeEnter 方法中实现。问题在于 event.rerouteToError(AccessDeniedException.class); 的调用。它尝试使用反射创建指定异常的实例但失败,因为它不包含 public 无参数构造函数。

private void beforeEnter(final BeforeEnterEvent event) {
    if (!AuthView.class.equals(event.getNavigationTarget()) && !AuthUtils.isUserLoggedIn()) {
        event.rerouteTo(AuthView.class);
    }

    if (!AuthUtils.isAccessGranted(event.getNavigationTarget())) {
        event.rerouteToError(AccessDeniedException.class);
    }
}
java.lang.IllegalArgumentException: Unable to create an instance of 'org.springframework.security.access.AccessDeniedException'. Make sure the class has a public no-arg constructor.
    at com.vaadin.flow.internal.ReflectTools.createProxyInstance(ReflectTools.java:519)
    at com.vaadin.flow.internal.ReflectTools.createInstance(ReflectTools.java:451)
    at com.vaadin.flow.router.BeforeEvent.rerouteToError(BeforeEvent.java:720)
    at com.vaadin.flow.router.BeforeEvent.rerouteToError(BeforeEvent.java:704)

这种情况的最佳解决方案是什么?我正在考虑两种可能的解决方案:

  1. 首先实例化AccessDeniedException,然后将其传递给BeforeEvent中的重载方法: public void rerouteToError(Exception exception, String customMessage)应该跳过通过反射创建异常对象
  2. 创建专用 ErrorView 并使用 BeforeEvent
  3. 的方法 public void rerouteTo(Class<? extends Component> routeTargetType, RouteParameters parameters)

我决定按照 Leif Åstrand's 回答。我创建了自定义 AccessDeniedException 和适当的错误处理程序。这是我的实现。也许对某人有帮助。

public class AccessDeniedException extends RuntimeException {
    private final int code;

    public AccessDeniedException() {
        super("common.error.403.details");
        this.code = HttpServletResponse.SC_FORBIDDEN;
    }

    public int getCode() {
        return code;
    }

}
@Tag(Tag.DIV)
@CssImport(value = "./styles/access-denied-view.css")
@CssImport(value = "./styles/access-denied-box.css", themeFor = "vaadin-details")
public class AccessDeniedExceptionHandler extends VerticalLayout implements HasErrorParameter<AccessDeniedException> {

    private final Details details;

    public AccessDeniedExceptionHandler() {
        setWidthFull();
        setHeight("100vh");
        setPadding(false);
        setDefaultHorizontalComponentAlignment(Alignment.CENTER);
        setJustifyContentMode(JustifyContentMode.CENTER);
        setClassName(ComponentConstants.ACCESS_DENIED_VIEW);

        this.details = new Details();
        this.details.setClassName(ComponentConstants.ACCESS_DENIED_BOX);
        this.details.addThemeVariants(DetailsVariant.REVERSE, DetailsVariant.FILLED);
        this.details.setOpened(true);

        add(this.details);
    }

    @Override
    public final int setErrorParameter(final BeforeEnterEvent event, final ErrorParameter<AccessDeniedException> parameter) {
        final int code = parameter.getException().getCode();

        this.details.setSummaryText(getTranslation("common.error.403.header", code));
        this.details.setContent(new Text(getTranslation(parameter.getException().getMessage())));

        return code;
    }

}

我建议创建自定义异常类型,而不是重用 Spring 中的 AccessDeniedException。这样一来,您根本不必处理所需的错误消息。

正如您在第一个解决方案中提到的那样,您可以这样做:

event.rerouteToError(new AccessDeniedException("Navigation target not permitted"), "");
  • 或者,如果需要,也可以指定 customMessage。如果您看到 rerouteToError(Class) 方法的实现,它只是传递空 customMessage 并创建异常 - 您可以手动执行,这是完全可以接受的。 我推荐这个解决方案。

另一个解决方案可能是继承 AccessDeniedException 并将其与反射一起使用:

public class RouteAccessDeniedException extends AccessDeniedException {
    public RouteAccessDeniedException() {
        super("Navigation target not permitted");
    }
}

推荐这个解决方案。