You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Spring Security+Keycloak同时保护浏览器请求与RESTful请求

统一防护浏览器请求与REST API的Spring Boot Security + Keycloak方案

我之前帮不少开发者解决过这个场景的问题,本质上你需要的是同一应用内兼容两种不同的认证范式:浏览器的交互式会话认证,以及API的无状态Token认证。不用纠结必须用单一客户端,Keycloak的多客户端设计刚好适配这个场景,配合Spring Security的分路径配置就能完美解决。

第一步:Keycloak客户端配置

先在Keycloak后台创建两个客户端,分别对应两种请求类型:

  • 公共客户端(Public Client):给浏览器请求用,开启Standard Flow(授权码模式),禁用Direct Access Grants,Valid Redirect URIs设为你的应用域名(比如http://localhost:8080/*),Web Origins设为*或对应域名。这个客户端不需要密钥,毕竟浏览器场景下密钥没法保密。
  • 保密客户端(Confidential Client):给REST API用,开启Service Accounts Enabled,如果需要密码模式获取Token就打开Direct Access Grants,Valid Redirect URIs可以留空,记得保存生成的客户端密钥——后续Spring配置要用到。

第二步:Spring Security分路径配置

核心思路是用HttpSecurity的antMatcher()为不同路径组配置独立的认证规则,完美隔离浏览器和API的安全逻辑。下面是完整的SecurityConfig.java示例:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // 支持方法级权限控制
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        KeycloakAuthenticationProvider keycloakAuthProvider = keycloakAuthenticationProvider();
        // 把Keycloak角色转换为Spring Security标准权限格式
        keycloakAuthProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthProvider);
    }

    // 加载Spring Boot配置文件中的Keycloak参数
    @Bean
    public KeycloakSpringBootConfigResolver keycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }

    // 浏览器请求的会话管理策略
    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        
        // 1. 处理浏览器页面请求(比如非/api开头的路径)
        http.antMatcher("/**")
            .authorizeRequests()
                .antMatchers("/api/**").permitAll() // 先放行API路径,交给下面的规则处理
                .anyRequest().authenticated()
            .and()
            .oauth2Login() // 用OAuth2授权码模式处理浏览器登录
            .loginPage("/sso/login") // 可自定义登录页,也用Keycloak默认页
            .defaultSuccessUrl("/home");

        // 2. 处理REST API请求
        http.antMatcher("/api/**")
            .authorizeRequests()
                .anyRequest().authenticated()
            .and()
            .oauth2ResourceServer()
                .jwt() // 用JWT承载者认证
                .jwtAuthenticationConverter(jwtAuthenticationConverter());
        
        // API场景关闭CSRF,浏览器场景Spring Security会自动处理
        http.csrf().ignoringAntMatchers("/api/**");
        
        // API设置无状态会话,认证失败直接返回401而非重定向
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            .exceptionHandling()
                .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
    }

    // 自定义JWT转换器,统一Keycloak角色与Spring Security权限格式
    private JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtGrantedAuthoritiesConverter authoritiesConverter = new JwtGrantedAuthoritiesConverter();
        authoritiesConverter.setAuthorityPrefix("ROLE_");
        authoritiesConverter.setAuthoritiesClaimName("roles");

        JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
        jwtConverter.setJwtGrantedAuthoritiesConverter(authoritiesConverter);
        return jwtConverter;
    }
}

关键细节说明

  • 路径隔离逻辑:用antMatcher()分别匹配浏览器路径和API路径,各自配置认证逻辑——浏览器用oauth2Login对应公共客户端的授权码模式,API用oauth2ResourceServer.jwt对应保密客户端的Token认证。
  • 会话策略差异:浏览器请求保留RegisterSessionAuthenticationStrategy管理会话,API请求设置SessionCreationPolicy.STATELESS,完全无状态符合REST规范。
  • 权限统一控制:通过转换器把Keycloak角色统一转换为Spring Security的权限格式,不管是浏览器还是API请求,都能共用@PreAuthorize("hasRole('ADMIN')")这类方法级权限注解。
  • 异常处理适配:API场景下配置HttpStatusEntryPoint,未认证时直接返回401状态码,不会像浏览器那样重定向到登录页,符合API的交互逻辑。

额外注意事项

  • CORS配置:如果API需要跨域访问,记得在Spring里配置CORS允许对应Origin,避免跨域拦截问题。
  • API Token获取:前端调用API时,需要先从Keycloak获取Bearer Token(比如用密码模式或授权码模式),然后在请求头带上Authorization: Bearer {token}。
  • 配置文件对应:Spring配置文件里的keycloak.client-id设为公共客户端ID,API的资源服务器会自动读取keycloak.resource(保密客户端ID)和keycloak.credentials.secret(保密客户端密钥)。

内容的提问来源于stack exchange,提问作者Jason Tian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:11:37