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);
}
}