Vaadin AccessAnnotationChecker无法读取JWT Token中Keycloak角色的问题
环境
- Vaadin Flow 23.2.4
- Quarkus 2.13.2.Final
- Keycloak 16.1.1
问题描述
Keycloak定义的角色已存在于JWT Token的realm_access.roles中,但Vaadin的AccessAnnotationChecker无法通过请求获取这些角色。需求是直接使用AccessAnnotationChecker而非自定义校验器,且同类Spring Boot+Spring Security应用中该组件可正常工作。
现有实现与现象
BootstrapCustomizer代码实现
AccessAnnotationChecker accessAnnotationChecker = new AccessAnnotationChecker(); void onServiceInit(@Observes ServiceInitEvent event) { event.getSource().addUIInitListener(uiEvent -> { final UI ui = uiEvent.getUI(); ui.addBeforeEnterListener(this::beforeEnter); }); } private void beforeEnter(BeforeEnterEvent event) { accessToken.getClaimNames().stream().forEach(n -> System.out.println(n + ":" + accessToken.getClaim(n).toString())); System.out.println("is user in role user:" + ((HttpServletRequest)VaadinServletRequest.getCurrent()).isUserInRole("user")); if (!accessAnnotationChecker.hasAccess(event.getNavigationTarget())) { System.out.println("REROUTING ON ACCESS DENIED"); event.rerouteTo(FlightView.class); } }
Token角色打印结果
控制台输出显示Token包含目标角色:
realm_access:{"roles":["privileged","default-roles-xxx","User","offline_access","uma_authorization","user"]}
视图权限注解与菜单控制
Vaadin视图使用@RolesAllowed注解声明权限:
@RolesAllowed({"user","privileged"}) public class ProfileView extends VerticalLayout { // 视图逻辑实现 }
通过AccessAnnotationChecker控制菜单显示:
if (accessAnnotationChecker.hasAccess(ProfileView.class)) navbarTabs.add(new NavTab("Profile", ProfileView.class));
应用配置
quarkus.oidc.client-id=my-client quarkus.oidc.auth-server-url=https://xy.zzy.no/auth/realms/xxx quarkus.oidc.credentials.secret=secret quarkus.oidc.tls.verification=none quarkus.oidc.authentication.user-info-required=false # this must be false with type=web-app, but probably is needed for roles check? # this only works for service-applicatoins, see https://quarkus.io/guides/security-openid-connect-web-authentication instead quarkus.keycloak.policy-enforcer.enable=false # this is needed when backend is not https quarkus.oidc.authentication.force-redirect-https-scheme=false quarkus.oidc.application-type=web_app quarkus.http.auth.permission.authenticated.paths=/* quarkus.http.auth.permission.authenticated.policy=authenticated quarkus.oidc.logout.path=/logout quarkus.oidc.logout.post-logout-path=/
用户疑问
曾尝试考虑启用quarkus.keycloak.policy-enforcer.enable=true,但该配置仅适用于服务应用,不适用于当前Web应用场景。
解决方案
问题根源是Quarkus Web应用模式下,默认未将Keycloak Token中的realm_access.roles映射为Java EE标准的安全角色,而AccessAnnotationChecker依赖该安全上下文进行权限判断。
1. 添加角色映射配置
在application.properties中添加以下配置,指定从Token的realm_access.roles字段读取角色:
quarkus.oidc.roles.role-claim-path=realm_access.roles
2. 验证安全上下文
配置完成后重启应用,检查((HttpServletRequest)VaadinServletRequest.getCurrent()).isUserInRole("user")是否返回true。若返回true,说明角色已成功映射到安全上下文,AccessAnnotationChecker即可正常识别@RolesAllowed注解的权限。
3. 注意事项
quarkus.keycloak.policy-enforcer仅用于API服务的权限强制,Web应用无需启用。- 确保
quarkus.oidc.application-type=web_app配置正确,该模式适配Web UI的认证流程。
内容的提问来源于stack exchange,提问作者jon martin solaas

