.NET C#中Spotify带Scope的授权令牌无法正常工作
问题根源与解决方案
你现在的代码犯了两个致命错误,直接导致了401错误和接口调用失败:
1. 用错了授权流程
你要实现的是授权码流程(Authorization Code Flow),但代码里写的是客户端凭证流程(Client Credentials Flow)。这俩完全不是一回事:
- 客户端凭证流程拿的是「应用级令牌」,只能访问不需要用户授权的公共数据,根本没有
user-read-private、user-read-email这类用户权限 - 授权码流程拿的是「用户级令牌」,代表用户操作,才能访问
/me这类需要用户权限的接口
2. 兑换授权码的参数完全错误
授权码流程兑换令牌时,必须满足以下要求:
grant_type必须设为authorization_code,不是client_credentials- 必须传入
code参数(你之前拿到的授权码) - 必须传入
redirect_uri参数(和你发起授权请求时用的回调地址完全一致,哪怕是本地测试地址也不能错)
修正后的代码实现
第一步:修正令牌兑换方法
public async Task<SpotifyTokenResponseEntity> ExchangeAuthorizationCodeForToken( SpotifyAuthRequestEntity authRequest, string authorizationCode, string redirectUri, string[] scopes) { var scopeString = string.Join(" ", scopes); var content = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("grant_type", "authorization_code"), new KeyValuePair<string, string>("code", authorizationCode), new KeyValuePair<string, string>("redirect_uri", redirectUri), new KeyValuePair<string, string>("scope", scopeString) }); var authString = $"{authRequest.ClientId}:{authRequest.ClientSecret}"; var base64Auth = Convert.ToBase64String(Encoding.ASCII.GetBytes(authString)); var request = new HttpRequestMessage(HttpMethod.Post, _tokenUrl) { Headers = { Authorization = new AuthenticationHeaderValue("Basic", base64Auth) }, Content = content }; var response = await _httpClient.SendAsync(request); if (!response.IsSuccessStatusCode) { var errorDetails = await response.Content.ReadAsStringAsync(); throw new Exception($"兑换令牌失败: {response.StatusCode} - {errorDetails}"); } var json = await response.Content.ReadAsStringAsync(); return JsonSerializer.Deserialize<SpotifyTokenResponseEntity>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); }
第二步:修正命令处理器
你需要把拿到的授权码和回调地址传入处理器,而不是只传ClientId和ClientSecret:
public class GetSpotifyUserToken : IRequest<ResponseObjectJsonDto> { public string ClientId { get; set; } public string ClientSecret { get; set; } public string AuthorizationCode { get; set; } // 新增:授权码 public string RedirectUri { get; set; } // 新增:回调地址 } public class GetSpotifyUserTokenHandler : IRequestHandler<GetSpotifyUserToken, ResponseObjectJsonDto> { private readonly ISpotifyAuthService _spotifyAuthService; public GetSpotifyUserTokenHandler(ISpotifyAuthService spotifyAuthService) { _spotifyAuthService = spotifyAuthService; } public async Task<ResponseObjectJsonDto> Handle(GetSpotifyUserToken request, CancellationToken cancellationToken) { try { var authRequest = new SpotifyAuthRequestEntity { ClientId = request.ClientId, ClientSecret = request.ClientSecret }; var scopes = new[] { "user-read-private", "user-read-email" }; Console.WriteLine($"请求带权限的用户令牌: {string.Join(", ", scopes)}"); // 调用修正后的兑换方法 var token = await _spotifyAuthService.ExchangeAuthorizationCodeForToken( authRequest, request.AuthorizationCode, request.RedirectUri, scopes); return new ResponseObjectJsonDto() { Code = (int)CodeHttp.OK, Message = "用户令牌生成成功", Response = token }; } catch (Exception ex) { return new ResponseObjectJsonDto() { Code = (int)CodeHttp.INTERNALSERVER, Message = ex.Message, Response = null }; } } }
额外注意事项
- 授权请求时的scope必须和兑换令牌时的scope一致,或者是其子集
- 回调地址
redirect_uri必须和你在Spotify开发者后台配置的地址完全匹配(包括http/https、端口、路径) - 授权码只能用一次,用过就失效,需要重新发起授权请求获取新的授权码
- 调用
/me接口时,必须用兑换得到的access_token,格式是Authorization: Bearer {access_token}
内容的提问来源于stack exchange,提问作者Isaac
相关产品推荐
相关产品推荐

