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

非Spring Boot的Spring 4.x项目从CAS迁移Okta OpenID登录失败问题咨询

问题根因
  • 协议不兼容:Spring Security 4 提供的spring-security-openid依赖和openidLogin()配置,是OpenID 2.0协议的实现,而Okta提供的是基于OAuth2的OpenID Connect(OIDC 1.0) 协议,二者完全不兼容,自然无法识别Okta的元配置文件
  • 配置缺失:你当前的配置没有声明OIDC对接必须的client_id、client_secret、重定向回调地址、授权范围等核心参数,也没有配置认证入口直接跳转Okta登录页
适配方案(兼容Spring 4.3 + Spring Security 4.2)

不需要升级Spring版本,手动实现OIDC授权码流程即可完成对接,步骤如下:

1. 调整依赖

移除原有spring-security-openid依赖,新增OAuth2和OIDC工具依赖:

<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
    <version>2.0.19.RELEASE</version>
</dependency>
<dependency>
    <groupId>com.nimbusds</groupId>
    <artifactId>oauth2-oidc-sdk</artifactId>
    <version>9.43.1</version>
</dependency>

2. 修改Spring Security配置

删掉原有的.openidLogin()配置,新增自定义认证入口点实现直接跳转Okta登录页,同时放开OIDC回调接口的访问权限:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
            .antMatchers("/admin/**").authenticated()
            .antMatchers("/protected/**").authenticated()
            .antMatchers("/api/**", "/oidc/callback").permitAll() // 新增放开回调接口
            .and()
            .logout().logoutSuccessUrl("https://dev--mycompany.oktapreview.com/oauth2/default/v1/logout?post_logout_redirect_uri=http://localhost:8080/myapp/public/logout.xhtml") // 适配Okta登出逻辑
            .and()
            .csrf().disable()
            // 新增自定义认证入口点,直接跳Okta登录页,不会再弹本地登录页
            .exceptionHandling()
            .authenticationEntryPoint((request, response, authException) -> {
                String authUrl = "https://dev--mycompany.oktapreview.com/oauth2/default/v1/authorize?" +
                        "client_id=替换为你Okta应用的clientId&" +
                        "response_type=code&" +
                        "scope=openid profile email&" +
                        "redirect_uri=" + URLEncoder.encode("http://localhost:8080/myapp/oidc/callback", "UTF-8") +
                        "&state=" + UUID.randomUUID().toString();
                response.sendRedirect(authUrl);
            });
}

3. 实现OIDC回调接口

新增Controller接收Okta返回的授权码,完成令牌校验、用户认证逻辑:

@Controller
@RequestMapping("/oidc")
public class OidcCallbackController {
    // 以下参数替换为你Okta应用的对应配置
    @Value("${okta.client-id}")
    private String clientId;
    @Value("${okta.client-secret}")
    private String clientSecret;
    private final String tokenEndpoint = "https://dev--mycompany.oktapreview.com/oauth2/default/v1/token";
    private final String jwksUri = "https://dev--mycompany.oktapreview.com/oauth2/default/v1/keys";
    private final String issuer = "https://dev--mycompany.oktapreview.com/oauth2/default";
    private final String redirectUri = "http://localhost:8080/myapp/oidc/callback";

    @Autowired
    private UserDetailsService userDetailsService;

    @GetMapping("/callback")
    public String callback(@RequestParam String code, HttpServletRequest request) throws Exception {
        // 1. 使用授权码兑换id_token和access_token
        TokenRequest tokenRequest = new TokenRequest(
                URI.create(tokenEndpoint),
                new ClientID(clientId),
                new Secret(clientSecret),
                GrantType.AUTHORIZATION_CODE,
                new AuthorizationCode(code),
                URI.create(redirectUri)
        );
        TokenResponse tokenResponse = tokenRequest.toHTTPRequest().send().parseAsTokenResponse();
        if (tokenResponse instanceof TokenErrorResponse) {
            return "redirect:/login?error";
        }
        AccessTokenResponse successResp = (AccessTokenResponse) tokenResponse;
        JWT idToken = successResp.getOIDCTokens().getIDToken();

        // 2. 校验id_token的合法性:签名、发行方、受众、过期时间
        JWKSet jwkSet = JWKSet.load(URI.create(jwksUri).toURL());
        JWSVerifier verifier = new RSASSAVerifier((RSAPublicKey) jwkSet.getKeyByKeyId(idToken.getHeader().getKeyID()));
        JWTClaimsSet claims = idToken.getJWTClaimsSet();
        if (!idToken.verify(verifier) 
            || !issuer.equals(claims.getIssuer()) 
            || !claims.getAudience().contains(clientId) 
            || claims.getExpirationTime().before(new Date())) {
            return "redirect:/login?error";
        }

        // 3. 从id_token获取用户名,调用现有UserDetailsService加载本地用户,完成认证
        String username = claims.getClaim("preferred_username").toString();
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(auth);
        request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());

        // 4. 跳转应用首页
        return "redirect:/";
    }
}

4. 补充Okta端配置

在Okta的应用配置里,将回调地址http://localhost:8080/myapp/oidc/callback和登出回调地址http://localhost:8080/myapp/public/logout.xhtml加入允许列表即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 19:15:04