using IdentityModel.OidcClient; using IdentityModel.OidcClient.Results; using System.Net.Http.Headers; using System.Text.Json; using AudioWallpaper.Sso.Exceptions; namespace AudioWallpaper.SSO { public class AuthManager { private OidcClient _oidcClient; private readonly AuthConfig authConfig = new(); public static readonly AuthManager Instance = new AuthManager(); private AuthStatus authStatus = AuthStatus.Unknown; //登录成功回调 public Action? OnLoginSuccess; //登录失败回调 public Action? OnLoginFailed; //刷新令牌成功回调 public Action? OnRefreshTokenSuccess; //刷新令牌失败回调 public Action? OnRefreshTokenFailed; //获取用户信息成功回调 public Action? OnGetUserInfoSuccess; //获取用户信息失败回调 public Action? OnGetUserInfoFailed; //撤销令牌成功回调 public Action? OnRevokeTokenSuccess; //撤销令牌失败回调 public Action? OnRevokeTokenFailed; //注销登录成功回调 public Action? OnLogoutSuccess; //注销登录失败回调 public Action? OnLogoutFailed; //登录状态改变回调 public Action OnAuthStatusChanged; public AuthStatus AuthStatus { get => authStatus; set { authStatus = value; OnAuthStatusChanged?.Invoke(value); } } public AuthManager() { var options = new OidcClientOptions { Authority = authConfig.authorityUrl, ClientId = authConfig.clientId, RedirectUri = authConfig.redirectUri.ToString(), Scope = authConfig.scope, Policy = new Policy { RequireAccessTokenHash = false, }, LoadProfile = true }; _oidcClient = new OidcClient(options); Console.WriteLine("OIDC 客户端初始化"); } public async void Login(Form? from = null) { if (from == null) { OnLoginFailed?.Invoke("登录窗口未初始化"); return; } if (_oidcClient == null) { OnLoginFailed?.Invoke("OIDC客户端未初始化"); return; } if (AuthStatus == AuthStatus.Success) { OnLoginFailed?.Invoke("已登录,请先注销"); return; } AuthStatus = AuthStatus.InProgress; _oidcClient.Options.Browser = new WebView2Browser(from); try { var result = await _oidcClient.LoginAsync(new LoginRequest()); if (result.IsError) { AuthStatus = AuthStatus.Failed; OnLoginFailed?.Invoke(result.Error); } else { TokenSet tokenSet = new( result.AccessToken, result.RefreshToken, result.IdentityToken ); SsoException? exception = CheckToken(tokenSet); if (exception != null) { throw exception; } AuthStatus = AuthStatus.Success; OnLoginSuccess?.Invoke(tokenSet); } } catch (Exception ex) { AuthStatus = AuthStatus.Failed; OnLoginFailed?.Invoke("登录异常:" + ex.Message); } } /// /// 获取用户信息 /// /// 访问令牌 /// 用户信息 public UserInfoSet? GetUserInfo(string? accessToken) { try { if (string.IsNullOrWhiteSpace(accessToken)) { throw new SsoException("AccessToken 为空"); } using var httpClient = new HttpClient(); var request = new HttpRequestMessage(HttpMethod.Get, authConfig.userInfoEndpoint); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); var response = httpClient.Send(request); if (!response.IsSuccessStatusCode) { OnGetUserInfoFailed?.Invoke($"获取用户信息失败,状态码: {response.StatusCode}"); return null; } var content = response.Content.ReadAsStringAsync().Result; var userInfoSet = JsonSerializer.Deserialize(content); if (userInfoSet == null) { throw new SsoException("获取用户信息失败,内容解析失败"); } OnGetUserInfoSuccess?.Invoke(userInfoSet); return userInfoSet; } catch (Exception ex) { OnGetUserInfoFailed?.Invoke($"获取用户信息异常: {ex.Message}"); return null; } } /// /// 刷新令牌 /// /// 刷新令牌 /// 令牌信息 public async Task RefreshToken(string? refreshToken) { try { if (_oidcClient == null) { OnRefreshTokenFailed?.Invoke("OidcClient 未初始化"); return null; } if (string.IsNullOrWhiteSpace(refreshToken)) { OnRefreshTokenFailed?.Invoke("刷新令牌失败:refreshToken 不能为空"); return null; } // 调用 OidcClient 的刷新令牌方法 RefreshTokenResult result = await _oidcClient.RefreshTokenAsync(refreshToken); if (result.IsError) { OnRefreshTokenFailed?.Invoke($"刷新令牌失败: {result.Error}"); return null; } var tokenSet = new TokenSet( result.AccessToken, result.RefreshToken, result.IdentityToken ); SsoException? exception = CheckToken(tokenSet); if (exception != null) { throw exception; } AuthStatus = AuthStatus.Success; OnRefreshTokenSuccess?.Invoke(tokenSet); return tokenSet; } catch (Exception ex) { OnRefreshTokenFailed?.Invoke($"刷新令牌异常: {ex.Message}"); return null; } } /// /// 撤销令牌 /// /// token /// token id public async void RevokeToken(string? token, string? tokenTypeHint, bool form = false) { if (string.IsNullOrWhiteSpace(token)) { OnRevokeTokenFailed?.Invoke("撤销令牌失败:token 不能为空"); if (form) { OnLogoutFailed?.Invoke("token 不能为空"); } return; } if (string.IsNullOrWhiteSpace(tokenTypeHint)) { OnRevokeTokenFailed?.Invoke("撤销令牌失败:tokenTypeHint 不能为空"); if (form) { OnLogoutFailed?.Invoke("tokenTypeHint 不能为空"); } return; } try { using var httpClient = new HttpClient(); var parameters = new Dictionary { { "token", token } }; if (!string.IsNullOrWhiteSpace(tokenTypeHint)) { parameters.Add("token_type_hint", tokenTypeHint); } parameters.Add("client_id", authConfig.clientId); // 如果有 client_secret,说明是 confidential client var clientSecretProperty = authConfig.GetType().GetProperty("clientSecret"); if (clientSecretProperty != null) { var clientSecret = clientSecretProperty.GetValue(authConfig) as string; if (!string.IsNullOrWhiteSpace(clientSecret)) { parameters.Add("client_secret", clientSecret); } } var content = new FormUrlEncodedContent(parameters); var response = await httpClient.PostAsync(authConfig.revocationEndpoint, content); var respContent = await response.Content.ReadAsStringAsync(); if (response.IsSuccessStatusCode) { // 撤销令牌成功,修改登录状态 AuthStatus = AuthStatus.LoginOut; OnRevokeTokenSuccess?.Invoke(); if (form) { OnLogoutSuccess?.Invoke(); } return; } else { OnRevokeTokenFailed?.Invoke($"撤销令牌失败,状态码: {response.StatusCode},内容: {respContent}"); if (form) { OnLogoutFailed?.Invoke($"撤销令牌失败,状态码: {response.StatusCode},内容: {respContent}"); } return; } } catch (Exception ex) { OnRevokeTokenFailed?.Invoke($"撤销令牌异常: {ex.Message}"); if (form) { OnLogoutFailed?.Invoke($"撤销令牌异常: {ex.Message}"); } } } /// /// 注销登录 /// public void Logout(TokenSet? set) { SsoException? exception = CheckToken(set); if (exception != null) { OnLogoutFailed?.Invoke(exception.Message); return; } #pragma warning disable CS8602 // 解引用可能出现空引用。 RevokeToken(set.RefreshToken, set.IdToken, true); #pragma warning restore CS8602 // 解引用可能出现空引用。 //TokenManager } /// /// 检查 TokenSet 是否有效 /// /// TokenSet 对象 /// 如果有效返回Null,如果无效返回对应的Exception public static SsoException? CheckToken(TokenSet? tokenSet) { if (tokenSet == null) { return new SsoException("TokenSet 为空"); } if (string.IsNullOrWhiteSpace(tokenSet.AccessToken)) { return new SsoException("AccessToken 为空"); } if (string.IsNullOrWhiteSpace(tokenSet.RefreshToken)) { return new SsoException("RefreshToken 为空"); } if (string.IsNullOrWhiteSpace(tokenSet.IdToken)) { return new SsoException("IdToken 为空"); } return null; } } }