using Easy.Admin.Application.Auth; using Easy.Admin.Application.Client.Dtos; using MrHuo.OAuth.QQ; namespace Easy.Admin.Application.Client; /// /// 第三方授权登录 /// [ApiDescriptionSettings("博客前端接口")] public class OAuthController : IDynamicApiController { /// /// 第三方授权缓存 /// private const string OAuthKey = "oauth."; /// /// 授权成功后回调页面缓存键 /// private const string OAuthRedirectKey = "oauth.redirect."; private readonly QQOAuth _qqoAuth; private readonly AuthManager _authManager; private readonly ISqlSugarRepository _accountRepository; private readonly ISqlSugarRepository _friendLinkRepository; private readonly IEasyCachingProvider _easyCachingProvider; private readonly IHttpContextAccessor _httpContextAccessor; private readonly IIdGenerator _idGenerator; public OAuthController(QQOAuth qqoAuth, AuthManager authManager, ISqlSugarRepository accountRepository, ISqlSugarRepository friendLinkRepository, IEasyCachingProvider easyCachingProvider, IHttpContextAccessor httpContextAccessor, IIdGenerator idGenerator) { _qqoAuth = qqoAuth; _authManager = authManager; _accountRepository = accountRepository; _friendLinkRepository = friendLinkRepository; _easyCachingProvider = easyCachingProvider; _httpContextAccessor = httpContextAccessor; _idGenerator = idGenerator; } /// /// 获取授权地址 /// /// 授权登录类型 /// 回跳地址 /// [HttpGet("{type}")] [AllowAnonymous] public async Task Get(string type, [FromQuery][Required(ErrorMessage = "缺少参数")] string referer) { string code = _idGenerator.Encode(_idGenerator.NewLong()); var request = _httpContextAccessor.HttpContext!.Request; await _easyCachingProvider.SetAsync($"{OAuthRedirectKey}{code}", referer, TimeSpan.FromMinutes(5)); string authUrl = type.ToLower() switch { "qq" => _qqoAuth.GetAuthorizeUrl(code), _ => throw Oops.Bah("无效请求") }; return authUrl; } /// /// 授权回调 /// /// 授权类型 /// /// 缓存唯一ID /// [HttpGet("{type}/callback")] [AllowAnonymous] public async Task Callback(string type, [FromQuery] string code, [FromQuery] string state) { if (string.IsNullOrWhiteSpace(state) || !await _easyCachingProvider.ExistsAsync($"{OAuthRedirectKey}{state}")) { throw Oops.Oh("缺少参数"); } AuthAccount account; switch (type.ToLower()) { case "qq": var auth = await _qqoAuth.AuthorizeCallback(code, state); if (!auth.IsSccess) { throw Oops.Bah(auth.ErrorMessage); } var info = auth.UserInfo; string openId = await _qqoAuth.GetOpenId(auth.AccessToken.AccessToken); account = await _accountRepository.AsQueryable().FirstAsync(x => x.OAuthId == openId && SqlFunc.ToLower(x.Type) == "qq"); var gender = info.Gender == "男" ? Gender.Male : info.Gender == "女" ? Gender.Female : Gender.Unknown; if (account != null) { await _accountRepository.UpdateAsync(x => new AuthAccount() { Avatar = string.IsNullOrWhiteSpace(info.QQ100Avatar) ? info.Avatar : info.QQ100Avatar, Name = info.Name, Gender = gender }, x => x.OAuthId == openId && SqlFunc.ToLower(x.Type) == "qq"); } else { account = await _accountRepository.InsertReturnEntityAsync(new AuthAccount() { Gender = gender, Avatar = info.Avatar, Name = info.Name, OAuthId = openId, Type = "QQ" }); } break; default: throw Oops.Bah("无效请求"); } string key = $"{OAuthKey}{state}"; await _easyCachingProvider.SetAsync(key, account, TimeSpan.FromMinutes(3)); //登录成功后的回调页面 string url = (await _easyCachingProvider.GetAsync($"{OAuthRedirectKey}{state}")).Value;//App.Configuration["oauth:redirect_uri"]?.TrimEnd('/'); string redirect = url.Contains("?") ? $"{url}&code={state}" : $"{url}?code={state}"; return new RedirectResult(redirect); } /// /// 登录 /// /// /// [HttpPost("login/{code}")] [AllowAnonymous] public async Task Login(string code) { string key = $"{OAuthKey}{code}", key2 = $"{OAuthRedirectKey}{code}"; var value = await _easyCachingProvider.GetAsync(key); if (!value.HasValue) { throw Oops.Bah("无效参数"); } long uniqueId = _idGenerator.NewLong(); var account = value.Value; string token = JWTEncryption.Encrypt(new Dictionary() { [AuthClaimsConst.AuthIdKey] = account.Id, [AuthClaimsConst.AccountKey] = account.OAuthId, [AuthClaimsConst.UuidKey] = uniqueId, [AuthClaimsConst.AuthPlatformTypeKey] = AuthPlatformType.Blog }); // 获取刷新 token var refreshToken = JWTEncryption.GenerateRefreshToken(token); // 设置响应报文头 _httpContextAccessor.HttpContext!.Response.Headers["access-token"] = token; _httpContextAccessor.HttpContext.Response.Headers["x-access-token"] = refreshToken; string url = (await _easyCachingProvider.GetAsync(key2)).Value; await _easyCachingProvider.RemoveAsync(key); await _easyCachingProvider.RemoveAsync(key2); return url; } /// /// 获取用户信息 /// /// [HttpGet] public async Task UserInfo() { long id = _authManager.UserId; return await _accountRepository.AsQueryable().LeftJoin((account, link) => account.Id == link.AppUserId) .Where(account => account.Id == id) .Select((account, link) => new OAuthAccountDetailOutput { Id = account.Id, Avatar = account.Avatar, Status = link.Status, NickName = account.Name, Link = link.Link, Logo = link.Logo, SiteName = link.SiteName, Url = link.Url, Remark = link.Remark }).FirstAsync(); } /// /// 申请友链 /// /// /// [HttpPost] public async Task AddLink(AddLinkOutput dto) { long userId = _authManager.UserId; var link = await _friendLinkRepository.GetFirstAsync(x => x.AppUserId == userId); if (link == null) { link = dto.Adapt(); link.AppUserId = userId; link.Status = AvailabilityStatus.Disable; await _friendLinkRepository.InsertAsync(link); return; } link = dto.Adapt(link); link.Status = AvailabilityStatus.Disable; await _friendLinkRepository.UpdateAsync(link); } }