Spring Boot中如何实现Vaadin管理UI与独立认证REST API共存
核心结论
不需要实例化两个独立的Spring应用上下文,Spring Security原生支持的多安全过滤链机制完全可以满足两个模块认证逻辑完全隔离的需求。两个模块在同一个应用上下文中可以正常共享数据访问层、公共工具组件,同时各自走独立的认证、授权、异常处理逻辑,相比拆分上下文的方案维护成本低、配置简单,是这类场景的标准实现方案。
具体实现路径
前置准备
先统一两个模块的路径前缀,避免路径交叉导致规则混乱:
- Vaadin管理UI的所有路由、静态资源统一归到
/admin/**路径下,同时覆盖Vaadin框架默认的内置路径/VAADIN/**、自定义的登录登出路径/login、/logout - 所有供原生应用调用的REST接口统一加
/api/**前缀
注意:之前将RestController路径从Spring Security拦截规则中排除的配置需要删掉,一旦排除了路径,所有安全逻辑都不会生效,没法走独立认证流程。
配置两套完全隔离的安全过滤链
Spring Security的过滤链是按@Order注解的优先级依次匹配的,只要给两个模块分别定义专属的SecurityFilterChain,明确指定各自负责的路径范围,两套配置就不会互相干扰。
示例配置代码如下:
@Configuration @EnableWebSecurity public class MultiModuleSecurityConfig { /** * REST API专属安全过滤链,优先级最高 */ @Bean @Order(1) public SecurityFilterChain apiSecurityChain(HttpSecurity http) throws Exception { http // 明确指定当前过滤链只处理/api前缀的请求,和其他链完全隔离 .securityMatcher("/api/**") // API一般是无状态,关闭CSRF、不创建会话 .csrf(csrf -> csrf.disable()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) // 挂载API专属的认证过滤器:比如校验请求头X-Api-Token的自定义过滤器 .addFilterBefore(apiTokenAuthFilter(), BasicAuthenticationFilter.class) // 绑定API专属的认证提供器,和UI的认证逻辑完全不互通 .authenticationProvider(apiTokenAuthProvider()) // 自定义API未认证/无权限的返回格式,直接返回JSON,不跳转到UI的登录页 .exceptionHandling(ex -> { ex.authenticationEntryPoint((req, resp, e) -> { resp.setStatus(401); resp.setContentType("application/json;charset=UTF-8"); resp.getWriter().write("{\"code\":401,\"msg\":\"API认证无效\"}"); }); ex.accessDeniedHandler((req, resp, e) -> { resp.setStatus(403); resp.setContentType("application/json;charset=UTF-8"); resp.getWriter().write("{\"code\":403,\"msg\":\"API无访问权限\"}"); }); }); return http.build(); } /** * Vaadin管理UI专属安全过滤链,优先级低于API链 */ @Bean @Order(2) public SecurityFilterChain uiSecurityChain(HttpSecurity http) throws Exception { http // 明确指定当前过滤链负责的UI相关路径 .securityMatcher("/admin/**", "/VAADIN/**", "/login", "/logout") // 按Vaadin官方要求配置CSRF规则 .csrf(csrf -> csrf.ignoringRequestMatchers("/VAADIN/**")) // 配置表单登录逻辑 .formLogin(form -> form .loginPage("/login") .defaultSuccessUrl("/admin/dashboard", true) .permitAll() ) .logout(logout -> logout.logoutSuccessUrl("/login").permitAll()) .authorizeHttpRequests(auth -> auth .requestMatchers("/VAADIN/**", "/login", "/logout").permitAll() .anyRequest().authenticated() ) // 绑定UI专属的表单认证提供器,只校验后台用户的用户名密码 .authenticationProvider(uiFormAuthProvider()); // 其余Vaadin相关的安全配置按官方要求补充即可 return http.build(); } }
认证逻辑隔离注意点
- 两个模块的
AuthenticationProvider要完全独立实现:- API专属的认证提供器只处理令牌校验逻辑,不管是固定令牌还是数据库存储的动态令牌,校验逻辑完全在这个类里实现,生成的认证对象只包含API调用方的权限信息
- UI专属的认证提供器只处理表单提交的用户名密码校验,查管理后台用户表做密码比对,生成的认证对象只包含后台管理员的权限信息
- 不要把两个认证提供器注册为全局共享组件,直接在对应
SecurityFilterChain中注入绑定即可,避免认证逻辑串用 - API过滤链不要配置表单登录、会话自动创建相关的逻辑,避免和UI的会话认证逻辑冲突
常见踩坑
- 每个
SecurityFilterChain必须加securityMatcher指定负责的路径范围,不加的话会变成全局默认过滤链,拦截所有请求,导致两套配置冲突 - 如果项目里之前引入了Vaadin官方的Spring Security starter,记得关掉它的自动默认安全配置,避免自动生成多余的过滤链打乱自定义规则
- 两个过滤链的
@Order顺序不能反,API路径匹配规则更明确,要放在更高优先级先匹配
内容的提问来源于stack exchange,提问作者stba
相关产品推荐
相关产品推荐

