|
1 | 1 | using System.IdentityModel.Tokens.Jwt;
|
| 2 | +using System.Net.Http.Headers; |
2 | 3 | using System.Security.Claims;
|
3 | 4 | using System.Text;
|
4 | 5 | using System.Text.Json;
|
|
7 | 8 | using KoalaWiki.Domains.Users;
|
8 | 9 | using KoalaWiki.Dto;
|
9 | 10 | using MapsterMapper;
|
10 |
| -using Microsoft.EntityFrameworkCore; |
11 | 11 | using Octokit;
|
| 12 | +using ProductHeaderValue = Octokit.ProductHeaderValue; |
12 | 13 | using User = KoalaWiki.Domains.Users.User;
|
13 | 14 |
|
14 | 15 | namespace KoalaWiki.Services;
|
@@ -188,6 +189,168 @@ public async Task<LoginDto> RegisterAsync(RegisterInput input)
|
188 | 189 | }
|
189 | 190 | }
|
190 | 191 |
|
| 192 | + /// <summary> |
| 193 | + /// Gitee登录 |
| 194 | + /// </summary> |
| 195 | + /// <param name="code">授权码</param> |
| 196 | + /// <returns>登录结果</returns> |
| 197 | + public async Task<LoginDto> GiteeLoginAsync(string code) |
| 198 | + { |
| 199 | + try |
| 200 | + { |
| 201 | + var clientId = configuration["Gitee:ClientId"]; |
| 202 | + var clientSecret = configuration["Gitee:ClientSecret"]; |
| 203 | + var redirectUri = configuration["Gitee:RedirectUri"]; |
| 204 | + |
| 205 | + if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(clientSecret)) |
| 206 | + { |
| 207 | + return new LoginDto(false, string.Empty, null, null, "Gitee配置错误"); |
| 208 | + } |
| 209 | + |
| 210 | + // 获取访问令牌 |
| 211 | + using var httpClient = new HttpClient(); |
| 212 | + var tokenRequest = new FormUrlEncodedContent(new[] |
| 213 | + { |
| 214 | + new KeyValuePair<string, string>("grant_type", "authorization_code"), |
| 215 | + new KeyValuePair<string, string>("code", code), |
| 216 | + new KeyValuePair<string, string>("client_id", clientId), |
| 217 | + new KeyValuePair<string, string>("client_secret", clientSecret), |
| 218 | + new KeyValuePair<string, string>("redirect_uri", redirectUri ?? "") |
| 219 | + }); |
| 220 | + |
| 221 | + var tokenResponse = await httpClient.PostAsync("https://gitee.com/oauth/token", tokenRequest); |
| 222 | + if (!tokenResponse.IsSuccessStatusCode) |
| 223 | + { |
| 224 | + return new LoginDto(false, string.Empty, null, null, "Gitee授权失败"); |
| 225 | + } |
| 226 | + |
| 227 | + var tokenContent = await tokenResponse.Content.ReadAsStringAsync(); |
| 228 | + var tokenData = JsonSerializer.Deserialize<JsonElement>(tokenContent); |
| 229 | + |
| 230 | + if (!tokenData.TryGetProperty("access_token", out var accessTokenElement)) |
| 231 | + { |
| 232 | + return new LoginDto(false, string.Empty, null, null, "获取Gitee访问令牌失败"); |
| 233 | + } |
| 234 | + |
| 235 | + var accessToken = accessTokenElement.GetString(); |
| 236 | + if (string.IsNullOrEmpty(accessToken)) |
| 237 | + { |
| 238 | + return new LoginDto(false, string.Empty, null, null, "Gitee访问令牌为空"); |
| 239 | + } |
| 240 | + |
| 241 | + // 获取用户信息 |
| 242 | + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); |
| 243 | + var userResponse = await httpClient.GetAsync("https://gitee.com/api/v5/user"); |
| 244 | + |
| 245 | + if (!userResponse.IsSuccessStatusCode) |
| 246 | + { |
| 247 | + return new LoginDto(false, string.Empty, null, null, "获取Gitee用户信息失败"); |
| 248 | + } |
| 249 | + |
| 250 | + var userContent = await userResponse.Content.ReadAsStringAsync(); |
| 251 | + var userData = JsonSerializer.Deserialize<JsonElement>(userContent); |
| 252 | + |
| 253 | + var giteeUserId = userData.GetProperty("id").GetInt64().ToString(); |
| 254 | + var giteeLogin = userData.GetProperty("login").GetString() ?? ""; |
| 255 | + var giteeEmail = userData.TryGetProperty("email", out var emailElement) ? emailElement.GetString() : ""; |
| 256 | + var giteeAvatar = userData.TryGetProperty("avatar_url", out var avatarElement) ? avatarElement.GetString() : ""; |
| 257 | + |
| 258 | + // 查询用户是否存在 |
| 259 | + var userInAuth = await dbContext.UserInAuths.FirstOrDefaultAsync(u => |
| 260 | + u.Id == giteeUserId && u.Provider == "Gitee"); |
| 261 | + |
| 262 | + User user = null; |
| 263 | + if (userInAuth != null) |
| 264 | + { |
| 265 | + user = await dbContext.Users |
| 266 | + .FirstOrDefaultAsync(u => u.Id == userInAuth.UserId); |
| 267 | + } |
| 268 | + |
| 269 | + // 用户不存在,自动注册 |
| 270 | + if (user == null) |
| 271 | + { |
| 272 | + user = new User |
| 273 | + { |
| 274 | + Id = Guid.NewGuid().ToString("N"), |
| 275 | + Name = giteeLogin, |
| 276 | + Email = giteeEmail ?? string.Empty, |
| 277 | + Password = Guid.NewGuid().ToString(), // 随机密码 |
| 278 | + Avatar = giteeAvatar, |
| 279 | + CreatedAt = DateTime.UtcNow, |
| 280 | + }; |
| 281 | + |
| 282 | + // 绑定Gitee账号 |
| 283 | + userInAuth = new UserInAuth |
| 284 | + { |
| 285 | + Id = giteeUserId, |
| 286 | + UserId = user.Id, |
| 287 | + Provider = "Gitee", |
| 288 | + CreatedAt = DateTime.UtcNow |
| 289 | + }; |
| 290 | + |
| 291 | + // 获取普通用户角色 |
| 292 | + var userRole = await dbContext.Roles |
| 293 | + .FirstOrDefaultAsync(r => r.Name == "user"); |
| 294 | + |
| 295 | + await dbContext.UserInRoles.AddAsync(new UserInRole |
| 296 | + { |
| 297 | + UserId = user.Id, |
| 298 | + RoleId = userRole!.Id |
| 299 | + }); |
| 300 | + |
| 301 | + // 保存用户 |
| 302 | + await dbContext.Users.AddAsync(user); |
| 303 | + await dbContext.UserInAuths.AddAsync(userInAuth); |
| 304 | + await dbContext.SaveChangesAsync(); |
| 305 | + } |
| 306 | + |
| 307 | + // 更新登录信息 |
| 308 | + user.LastLoginAt = DateTime.UtcNow; |
| 309 | + user.LastLoginIp = httpContextAccessor.HttpContext?.Connection.RemoteIpAddress?.ToString(); |
| 310 | + |
| 311 | + if (httpContextAccessor.HttpContext?.Request.Headers["x-forwarded-for"].Count > 0) |
| 312 | + { |
| 313 | + user.LastLoginIp = httpContextAccessor.HttpContext.Request.Headers["x-forwarded-for"]; |
| 314 | + } |
| 315 | + else if (httpContextAccessor.HttpContext?.Request.Headers["x-real-ip"].Count > 0) |
| 316 | + { |
| 317 | + user.LastLoginIp = httpContextAccessor.HttpContext.Request.Headers["x-real-ip"]; |
| 318 | + } |
| 319 | + |
| 320 | + await dbContext.SaveChangesAsync(); |
| 321 | + |
| 322 | + // 获取当前用户的角色 |
| 323 | + var roleIds = await dbContext.UserInRoles |
| 324 | + .Where(ur => ur.UserId == user.Id) |
| 325 | + .Select(x => x.RoleId) |
| 326 | + .ToListAsync(); |
| 327 | + |
| 328 | + var roles = await dbContext.Roles |
| 329 | + .Where(r => roleIds.Contains(r.Id)) |
| 330 | + .ToListAsync(); |
| 331 | + |
| 332 | + // 生成JWT令牌 |
| 333 | + var jwtToken = GenerateJwtToken(user, roles); |
| 334 | + var refreshToken = GenerateRefreshToken(user); |
| 335 | + |
| 336 | + var userDto = mapper.Map<UserInfoDto>(user); |
| 337 | + userDto.Role = string.Join(',', roles.Select(x => x.Name)); |
| 338 | + |
| 339 | + // 设置到cookie |
| 340 | + httpContextAccessor.HttpContext?.Response.Cookies.Append("refreshToken", refreshToken, |
| 341 | + CreateCookieOptions(jwtOptions.RefreshExpireMinutes)); |
| 342 | + httpContextAccessor.HttpContext?.Response.Cookies.Append("token", jwtToken, |
| 343 | + CreateCookieOptions(jwtOptions.ExpireMinutes)); |
| 344 | + |
| 345 | + return new LoginDto(true, jwtToken, refreshToken, userDto, null); |
| 346 | + } |
| 347 | + catch (Exception ex) |
| 348 | + { |
| 349 | + logger.LogError(ex, "Gitee登录失败"); |
| 350 | + return new LoginDto(false, string.Empty, null, null, "Gitee登录失败,请稍后再试"); |
| 351 | + } |
| 352 | + } |
| 353 | + |
191 | 354 | /// <summary>
|
192 | 355 | /// GitHub登录
|
193 | 356 | /// </summary>
|
@@ -619,6 +782,19 @@ public async Task<List<SupportedThirdPartyLoginsDto>> GetSupportedThirdPartyLogi
|
619 | 782 | });
|
620 | 783 | }
|
621 | 784 |
|
| 785 | + // 检查Gitee配置 |
| 786 | + if (!string.IsNullOrEmpty(configuration["Gitee:ClientId"]) && |
| 787 | + !string.IsNullOrEmpty(configuration["Gitee:ClientSecret"])) |
| 788 | + { |
| 789 | + supportedLogins.Add(new SupportedThirdPartyLoginsDto |
| 790 | + { |
| 791 | + Name = "Gitee", |
| 792 | + Icon = "https://gitee.com/favicon.ico", |
| 793 | + ClientId = configuration["Gitee:ClientId"] ?? string.Empty, |
| 794 | + RedirectUri = configuration["Gitee:RedirectUri"] ?? string.Empty |
| 795 | + }); |
| 796 | + } |
| 797 | + |
622 | 798 | return await Task.FromResult(supportedLogins);
|
623 | 799 | }
|
624 | 800 |
|
|
0 commit comments