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

如何通过OIDC授权码流实现Quarkus、Keycloak与Vue SPA的安全身份认证

解决方案

你遇到的核心问题是跨域场景下用AJAX请求触发OIDC登录的302重定向,浏览器自动跟随重定向到Keycloak地址时触发CORS错误,且fetch规范不允许读取重定向地址手动跳转。以下是符合你安全要求的落地方案:


方案1:后端托管OIDC流+HttpOnly Cookie(推荐,匹配你的初始需求)

该方案完全符合你「前端不接触令牌、令牌存在带安全属性的Cookie、API无状态」的要求,无需修改技术栈。

1. Quarkus侧配置调整

在原有application.properties基础上补充以下配置:

# 允许Vue前端跨域访问,开启凭证支持
quarkus.http.cors.origins=http://localhost:3000
quarkus.http.cors.credentials=true
quarkus.http.cors.methods=GET,POST,PUT,DELETE,OPTIONS
quarkus.http.cors.headers=*

# OIDC Cookie安全属性配置
quarkus.oidc.authentication.cookie.secure=true
quarkus.oidc.authentication.cookie.http-only=true
quarkus.oidc.authentication.cookie.same-site=strict
# 登录成功后跳转回SPA的地址,替换为你Vue实际运行的地址
quarkus.oidc.authentication.redirect-path=http://localhost:3000

新增公开的认证接口,专门处理登录、登出、状态校验逻辑:

@Path("/api/auth")
public class AuthController {
    @Inject
    OidcAuthenticationContext authContext;

    // 登录触发端点:公开访问,跳转至Keycloak认证页
    @GET
    @Path("/login")
    public Uni<Response> login() {
        return authContext.redirectToAuthorizationEndpoint();
    }

    // 登出端点:清理Cookie,跳转至Keycloak登出页
    @GET
    @Path("/logout")
    @Authenticated
    public Uni<Response> logout() {
        return authContext.logout()
                .map(redirectUri -> Response.seeOther(URI.create(redirectUri)).build());
    }

    // 登录状态校验端点:公开访问,给前端判断登录状态用
    @GET
    @Path("/status")
    public Response checkStatus(@Context SecurityContext securityContext) {
        boolean isLoggedIn = securityContext.getUserPrincipal() != null;
        return Response.ok(Map.of("loggedIn", isLoggedIn)).build();
    }
}

原有受保护接口和OIDC配置不需要修改,quarkus.oidc.application-type保持web-app即可。

2. 前端逻辑调整

不要通过AJAX请求被动触发登录跳转,改为主动整页跳转登录端点,避免跨域问题。修改后的组件逻辑参考:

<template>
  <div>
    <button v-on:click="fetchFromBackend">Fetch</button>
    <p><b>Output:</b>{{ message }}</p>
  </div>
</template>

<script lang="ts">
import { defineComponent } from "vue";

export default defineComponent({
  data() {
    return {
      message: "Click the button to fetch.",
    };
  },
  methods: {
    fetchFromBackend(): void {
      this.message = "Waiting...";
      fetch("http://localhost:8081/hello", {
        credentials: "include",
      })
        .then((resp) => {
          // 未登录直接跳转登录端点,不用处理302
          if (resp.status === 401) {
            window.location.assign("http://localhost:8081/api/auth/login");
            return;
          }
          return resp.text().then((text) => (this.message = text));
        })
        .catch((reason) => (this.message = "Caught error: " + reason));
    },
  },
});
</script>

你也可以在SPA初始化时先调用/api/auth/status接口判断用户登录状态,未登录直接引导跳转登录。


方案2:SPA侧走带PKCE的授权码流(可选,体验更流畅)

如果你不想做整页跳转,可以选择当前SPA认证的主流方案:

  1. Quarkus侧将quarkus.oidc.application-type改为service,只做令牌校验
  2. 前端使用oidc-client-ts这类库,走带PKCE的授权码流,拿到的access token只存在内存中,不存入本地存储
  3. 调用接口时将令牌放在Authorization: Bearer <token>头中,设置较短的令牌有效期(5分钟以内),过期后通过静默iframe刷新令牌
    该方案的缺点是令牌存在前端内存,有XSS泄露风险,优点是无需整页跳转,用户体验更好。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 20:15:01