非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
相关产品推荐
相关产品推荐

