Java实现Steam OpenID登录遇is_valid:false问题咨询及方案寻求
Steam OpenID登录随机返回is_valid:false的问题排查与替代方案
问题描述
我用Java实现了Steam OpenID登录功能,但目前遇到随机出现is_valid:false响应的问题,即便传递的参数完全准确也会触发。我的需求是:
- 确保参数准确时能稳定拿到is_valid:true的响应;
- 如果问题无法解决,希望了解其他Steam登录的实现方式。
登录校验核心代码
@GetMapping("/login/check") public String startSession( @RequestParam(value = "openid.ns") String openidNs, @RequestParam(value = "openid.mode") String openidMode, @RequestParam(value = "openid.op_endpoint") String openidOpEndpoint, @RequestParam(value = "openid.claimed_id") String openidClaimedId, @RequestParam(value = "openid.identity") String openidIdentity, @RequestParam(value = "openid.return_to") String openidReturnTo, @RequestParam(value = "openid.response_nonce") String openidResponseNonce, @RequestParam(value = "openid.assoc_handle") String openidAssocHandle, @RequestParam(value = "openid.signed") String openidSigned, @RequestParam(value = "openid.sig") String openidSig, HttpServletRequest request, HttpServletResponse response) throws InterruptedException { log.info("openidResponseNonce = {} ", openidResponseNonce); ResponseEntity<String> block = WebClient.create("https://steamcommunity.com") .get() .uri(uriBuilder -> uriBuilder .path("/openid/login") .queryParam("openid.ns", openidNs) .queryParam("openid.mode", "check_authentication") .queryParam("openid.op_endpoint", openidOpEndpoint) .queryParam("openid.claimed_id", openidClaimedId) .queryParam("openid.identity", openidIdentity) .queryParam("openid.return_to", openidReturnTo) .queryParam("openid.response_nonce", openidResponseNonce) .queryParam("openid.assoc_handle", openidAssocHandle) .queryParam("openid.signed", openidSigned) .queryParam("openid.sig", openidSig) .build() ) .retrieve() .toEntity(String.class) .block(); boolean isTrue = Objects.requireNonNull(block).getBody().contains("true"); log.info("isTrue = {} ", isTrue); if (!isTrue) { return "redirect:https://steamcommunity.com/openid/login?openid.ns=http://specs.openid.net/auth/2.0&openid.claimed_id=http://specs.openid.net/auth/2.0/identifier_select&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.return_to=" + baseUrl + "/login/check&openid.realm=" + baseUrl + "&openid.mode=checkid_setup"; } Pattern pattern = Pattern.compile("\\d+"); Matcher matcher = pattern.matcher(openidIdentity); String steamId; if (matcher.find()) { steamId = matcher.group(); } else { throw new IllegalArgumentException(); } if (!userService.findBySteamId(steamId).isPresent()) { userService.create(getUserInfo(steamId)); } SecurityUser user = SecurityUser.builder() .id(userService.findBySteamId(steamId).get().getId()) .username(userService.findBySteamId(steamId).get().getUsername()) .steamId(steamId) .build(); Authentication authentication = new OAuth2AuthenticationToken(user, user.getAuthorities(), "steam"); SecurityContextHolder.getContext().setAuthentication(authentication); HttpSession session = request.getSession(false); if (session != null) { session.invalidate(); } // 新建会话 session = request.getSession(true); session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext()); // 将会话ID设置到Cookie中 Cookie cookie = new Cookie("JSESSIONID", session.getId()); cookie.setHttpOnly(true); cookie.setPath("/"); response.addCookie(cookie); return "redirect:/user/checkFirstVisit"; }
一、解决随机is_valid:false的问题
1. 切换请求方式并校验参数一致性
- Steam官方推荐用POST请求传递校验参数,GET请求可能因URL长度限制、编码异常导致参数篡改,尤其是
openid.return_to、openid.sig这类含特殊字符的参数; - 确保
openid.return_to与跳转Steam登录时传递的地址完全一致,包括协议(http/https)、域名、路径和参数,不能有任何修改。
2. 规范Nonce处理
openid.response_nonce包含时间戳,Steam会校验其是否在有效时间窗口内(通常几分钟),需优化请求延迟避免超时;- 记录已使用的nonce,禁止重复提交同一nonce触发校验失败。
3. 修复WebClient配置与响应解析
- 给WebClient添加超时配置,避免超时重试导致重复提交:
WebClient webClient = WebClient.builder() .baseUrl("https://steamcommunity.com") .clientConnector(new ReactorClientHttpConnector( HttpClient.create().responseTimeout(Duration.ofSeconds(10)) )) .build();
- 替换当前模糊的响应判断逻辑,按Steam响应格式精准解析
is_valid字段:
String responseBody = block.getBody(); boolean isTrue = responseBody.lines() .map(String::trim) .anyMatch(line -> line.equals("is_valid:true"));
二、替代Steam登录实现方式
1. Spring Security OAuth2集成Steam
利用Spring Security的OAuth2客户端能力快速集成,无需手动处理OpenID流程:
- 注册Steam开发者账号获取API Key;
- 配置自定义OAuth2客户端:
@Configuration public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .oauth2Login(oauth2 -> oauth2 .clientRegistrationRepository(clientRegistrationRepository()) .userInfoEndpoint(userInfo -> userInfo .customUserType(SteamUser.class, "steam") ) ); return http.build(); } @Bean public ClientRegistrationRepository clientRegistrationRepository() { return new InMemoryClientRegistrationRepository(steamClientRegistration()); } private ClientRegistration steamClientRegistration() { return ClientRegistration.withRegistrationId("steam") .clientId("YOUR_STEAM_API_KEY") .clientAuthenticationMethod(ClientAuthenticationMethod.NONE) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .redirectUri("{baseUrl}/login/oauth2/code/steam") .authorizationUri("https://steamcommunity.com/openid/login") .tokenUri("https://steamcommunity.com/openid/login") .userInfoUri("https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key={client_id}&steamids={user_id}") .userNameAttributeName("steamid") .clientName("Steam") .build(); } }
2. 直接调用Steam Web API
跳过OpenID,用Steam官方Web API实现登录:
- 引导用户访问Steam授权页面获取授权凭证;
- 结合API Key调用接口换取用户SteamID;
- 调用
GetPlayerSummaries接口获取用户信息完成登录流程。
内容的提问来源于stack exchange,提问作者Hyeok Geun
相关产品推荐
相关产品推荐

