问题描述
我开始为我们用 Spring Boot (2.2.6.RELEASE)
和 Vaadin 14 LTS (14.6.1)
编写的应用程序实施身份验证和授权。
我已关注这些资源:
我有代码用于检查登录用户是否对在 beforeEnter
方法中实现的指定资源具有访问权限。问题在于调用 event.reroutetoError(AccessDeniedException.class);
。它尝试使用反射创建指定异常的实例但失败,因为它不包含公共无参数构造函数。
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)
对于这种情况,最好的解决方案是什么?我正在考虑两种可能的解决方案:
- 首先实例化
AccessDeniedException
,然后将其传递给BeforeEvent
中的重载方法:public void reroutetoError(Exception exception,String customMessage)
应该跳过通过反射创建异常对象 - 创建专用的
ErrorView
并使用public void rerouteto(Class<? extends Component> routetargettype,RouteParameters parameters)
的方法BeforeEvent
我决定遵循 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");
}
}
我不推荐这种解决方案。