带有 Keycloak 的 Angular/Spring Boot 抛出 403

Posted

技术标签:

【中文标题】带有 Keycloak 的 Angular/Spring Boot 抛出 403【英文标题】:Angular/Spring Boot with Keycloak throws 403 【发布时间】:2021-01-09 12:18:12 【问题描述】:

我在 this documentation 之后使用 Keycloak 11.0.2 和 Spring Security 保护了我的 Spring Boot 应用程序。

我在application.properties中使用了基本的Keycloak配置:

    keycloak.auth-server-url=http://localhost:8085/auth
    keycloak.realm=cirta
    keycloak.resource=cirta-api
    keycloak.public-client=false

我有一个单独的前端 Angular 应用程序,它在 Keylocak 中配置为不同的客户端;但与 Spring Boot 应用程序处于同一领域。从 Angular 应用程序中,我在 HTTP 标头中发送 Keycloak 提供的令牌:

'Authorization' : 'Bearer ' + this.securityService.kc.token

当我访问调用 GET API 的 Angular 页面时,我收到 blocked by CORS policy 错误:

Access to XMLHttpRequest at 'http://localhost:8080/api/modePaiements' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.

所以我尝试将keycloak.cors=true 属性添加到application.properties。添加该属性后,GET 调用正在运行。但是现在当我调用 POST/PUT API 时,我收到了 Failed to load resource: the server responded with a status of 403 () 错误。

KeycloakWebSecurityConfigurerAdapter:

@Override
protected void configure(HttpSecurity http) throws Exception 
    super.configure(http);
    http.authorizeRequests().antMatchers("/api/*").hasRole("app-manager").anyRequest().permitAll();

Spring 示例应用程序: https://github.com/bilaldekar/kc

Angular 示例应用程序: https://github.com/bilaldekar/kc-ang

请求标头:

【问题讨论】:

评论不用于扩展讨论;这个对话是moved to chat。 this.securityService.kc.token 解码后的有效载荷是什么样的?有app-manager角色吗? 我在问题中添加了令牌,是的,我创建了一个角色 app-manager。 @deduper 你能给出解决 401/403 的配置吗,我会用前端调用测试它,看看它是否有效 我发现后端 api 应该仅配置为承载,而不是公共客户端,以便通过前端发送的令牌提供对 api 的访问,但这并没有解决问题 【参考方案1】:

在我看来是一个 csrf 问题。

将以下内容添加到安全配置中

http.authorizeRequests().antMatchers("/api/*").hasRole("app-manager").anyRequest().permitAll()
                .and().csrf()
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());

以上代码将在名为XSRF-TOKEN的cookie中设置CSRF令牌。默认情况下,Angular 会为 CSRF 令牌读取此 cookie,并将其添加到后续请求中。为了确保 Angular 可以读取它,我们将 Http only 设置为 False。这有其自身的安全隐患,因为现在可以通过脚本访问 CSRF 令牌。如果您不喜欢这样做,另一种方法是从响应标头中读取X-XSRF-TOKEN 令牌,并使用 Angular 拦截器将其发送到后续请求标头中。

更新:

在本地测试时,Angular 运行在 4200、Spring Boot 应用、Keycloak 服务器在 8080、8085 端口上。

通过以下步骤使用 Webpack 开发服务器代理。 (你不再需要这个配置了keycloak.cors=true

environment.ts 中更新apiUrl

apiUrl: '', //Spring Boot API

然后在src文件夹下添加proxy.conf.json,内容如下


    "/api": 
        "target": "http://localhost:8080",
        "secure": false
    ,
    "logLevel": "debug"

angular.json serve 命令中添加代理配置

"options": 
    "browserTarget": "kc-ang:build",
    "proxyConfig": "src/proxy.conf.json"

现在您会注意到 api 请求将转到 localhost:4200/api/* 并通过上述配置路由到 Spring Boot 应用程序。

这样,与 XSRF 相关的 cookie 和标头将成为请求的一部分,您应该不会遇到任何问题。

[![示例工作版本][1]][1]

更新 2:支持 CORS

如果你的前端和后端在不同的域上,如果你必须支持 CORS,那么这就是需要做的事情

Spring 上的安全配置应该允许 CORS 作为前端的来源(在本例中为 http://localhost:4200):

protected void configure(HttpSecurity http) throws Exception 
    super.configure(http);
    http.authorizeRequests().antMatchers("/api/*").hasRole("app-manager").anyRequest()
            .permitAll()
    .and().csrf()
            .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
    .and().cors();


@Bean
CorsConfigurationSource corsConfigurationSource() 
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
    configuration.setAllowedMethods(Arrays.asList("GET","POST"));
    configuration.setAllowCredentials(Boolean.TRUE);
    configuration.addAllowedHeader("*");

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/api/*", configuration);
    return source;

在 Angular 上,将 environment.ts 文件中的 apiUrl 更新为

apiUrl: '//localhost:8085'

在使用 Angular http 客户端进行任何写入操作(POST/PUT/DELETE)时,请将withCredentials 选项设置为true。 (还要确保服务器端配置上的 CORS 支持该方法。在上述 CORS 配置中,我们只允许 GET 和 POST。如果您需要支持 PUT/DELETE,请确保相应地更新 CORS 配置)

在这种情况下更新fournisseur.service.ts中的以下方法

addFournisseur(fournisseur: Fournisseur): Observable<Fournisseur> 
    return this.httpClient.post<Fournisseur>(this.envUrl + '/api/fournisseurs', fournisseur, withCredentials: true);
  

【讨论】:

你能完全禁用 CSRF 并检查一下。这至少应该可以帮助您消除一种可能性。 我如何禁用 csrf,角度或弹簧? 您需要在 Spring 上禁用。在配置方法中调用禁用方法。 http.csrf().disable(); 所以我应该keeo http.csrf().disable();在我的配置中,这样安全吗? 如果您的 api 将仅被服务器访问,即后端到后端模型,那么您可以。如果要通过前端访问它,在这种情况下,你不应该。您是否会使用此答案中提到的配置检查您是否在响应中获得了“XSRF-TOKEN”cookie。如果是,那么只需要检查 Angular 方面(我现在也要看看)。如果不是,那么我们还必须查看 CSRF 配置的 Spring 端。

以上是关于带有 Keycloak 的 Angular/Spring Boot 抛出 403的主要内容,如果未能解决你的问题,请参考以下文章

登录后带有 Spring 'Invalid credentials' 的 Keycloak

Keycloak :使用带有永不过期令牌的服务帐户

带有外部身份提供者的 Keycloak 失败

带有 OpenIdConnect 外部身份提供者的 Keycloak

带有 Spring Boot KeycloakSecurityContext 的 Keycloak 始终为空

带有 keycloak 设置的 Spring Security (HttpSecurity)