SuperAPI AI 工具集

security

安全与审计

查看项目

更新于 10-16 03:34

技能介绍

用途

本技能用于指导智能体在 Vaadin 25 应用中通过 Spring Security 实现安全防护。当用户要求“添加安全”“添加登录”“创建登录页”等任务时使用。技能覆盖 Java/Flow 视图的安全配置,包括登录、访问控制、登出与 OAuth2/OpenID Connect 集成。

主要能力

  • Spring Security 配置:使用 VaadinSecurityConfigurer.vaadin() 自动处理 CSRF、登出、请求缓存、异常处理、授权请求与导航访问控制。
  • 登录视图:使用内置 LoginForm 组件创建登录页,兼容密码管理器并自动处理 CSRF 令牌。
  • 视图访问控制:通过 @AnonymousAllowed、@PermitAll、@RolesAllowed、@DenyAll 注解控制视图访问。
  • 程序化访问检查:注入 AuthenticationContext 检查角色或获取用户信息。
  • 登出:推荐使用 AuthenticationContext.logout(),也可使用 SecurityContextLogoutHandler。
  • OAuth2/OpenID Connect:支持 Google、GitHub、Keycloak、Okta、Azure AD 等身份提供商。

使用方式

设置 Spring Security

添加依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

创建安全配置类:

@EnableWebSecurity
@Configuration
public class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.with(VaadinSecurityConfigurer.vaadin(), configurer -> {
            configurer.loginView(LoginView.class);
        });
        return http.build();
    }

    @Bean
    public UserDetailsManager userDetailsManager() {
        // WARNING: In-memory users for development only.
        // Use JDBC, LDAP, or OAuth2 in production.
        var user = User.withUsername("user")
                .password("{noop}user")
                .roles("USER")
                .build();
        var admin = User.withUsername("admin")
                .password("{noop}admin")
                .roles("ADMIN")
                .build();
        return new InMemoryUserDetailsManager(user, admin);
    }
}

登录视图

使用 LoginForm 创建登录页,关键点包括:autoLayout = false、@AnonymousAllowed、login.setAction("login")、BeforeEnterObserver 检查 ?error 参数,并在 SecurityConfig 中注册 loginView(LoginView.class)。

视图访问控制

注解 访问级别 典型用途
@AnonymousAllowed 任何人(无需登录) 登录视图、公开落地页
@PermitAll 任何已认证用户 仪表盘、用户资料
@RolesAllowed("ADMIN") 指定角色用户 管理面板、用户管理
@DenyAll 无人 无注解时的默认值

注意:Vaadin 中 @PermitAll 表示“任何已认证用户”,与标准 Jakarta Security 不同;真正公开视图应使用 @AnonymousAllowed。

注解解析规则:无注解默认 @DenyAll;继承最近的已注解父类;子类注解覆盖父类;接口注解不检查;布局与视图独立检查,两者都必须授权;优先级 @DenyAll > @AnonymousAllowed > @RolesAllowed > @PermitAll。

程序化访问检查

注入 AuthenticationContext 检查角色或获取用户信息:

@Route("settings")
@PermitAll
public class SettingsView extends VerticalLayout {

    public SettingsView(AuthenticationContext authContext) {
        authContext.getAuthenticatedUser(UserDetails.class)
                .ifPresent(user -> add(new H2("Welcome " + user.getUsername())));

        if (authContext.hasRole(Roles.ADMIN)) {
            add(new Button("Admin Settings", event -> {
                // show admin-only settings
            }));
        }
    }
}

登出

推荐使用 AuthenticationContext.logout(),它自动处理会话失效与重定向。AuthenticationContext 字段必须声明为 transient。默认登出后重定向到 /,可通过 configurer.loginView(LoginView.class, "/goodbye") 自定义。

OAuth2 / OpenID Connect

添加依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

Google 配置示例:

spring.security.oauth2.client.registration.google.client-id=${GOOGLE_CLIENT_ID}
spring.security.oauth2.client.registration.google.client-secret=${GOOGLE_CLIENT_SECRET}
spring.security.oauth2.client.registration.google.scope=openid,profile,email

Keycloak 配置示例:

spring.security.oauth2.client.registration.keycloak.provider=keycloak
spring.security.oauth2.client.registration.keycloak.client-id=my-client-id
spring.security.oauth2.client.registration.keycloak.client-secret=${KEYCLOAK_CLIENT_SECRET}
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.keycloak.scope=openid,profile

spring.security.oauth2.client.provider.keycloak.issuer-uri=http://keycloak.local:8180/realms/my-app

OAuth2 的 SecurityConfig 使用 oauth2LoginPage() 替代 loginView():

http.with(VaadinSecurityConfigurer.vaadin(), configurer -> {
    configurer.oauth2LoginPage(
            "/oauth2/authorization/google",
            "{baseUrl}");
});

使用 OAuth2 时无需 LoginView 类,身份提供商负责登录界面。

适用场景

  • 为 Vaadin 25 应用添加登录与访问控制。
  • 使用注解保护 Java/Flow 视图。
  • 集成外部身份提供商(Google、GitHub、Keycloak、Okta、Azure AD)。
  • 实现登出与用户信息展示。

限制与注意事项

  • 视图安全应使用注解,而非 URL 模式匹配;Vaadin 所有视图共用一个 servlet 端点,HttpSecurity 的 requestMatchers 无法控制视图访问。URL 模式规则仍适用于 REST API 等非 Vaadin 端点。
  • 不要在视图上使用 @Secured 或 @PreAuthorize,这些 Spring Security 注解不受支持。
  • 不要硬编码凭据;内存 {noop} 密码仅用于开发。
  • 登录视图必须设置 autoLayout = false。
  • 布局与视图都必须独立授权。
  • 使用 SecurityContextLogoutHandler 时必须先重定向再调用,推荐使用 AuthenticationContext.logout()。
  • 使用 Vaadin MCP 工具查询最新文档时,vaadin_version 设为 "25",ui_language 设为 "java"。

详细速查表(安全注解、OAuth2 提供商配置、登录视图清单、SecurityConfig 模板)见 references/security-patterns.md。