335 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Lazy.Captcha.Core;
using Microsoft.Extensions.Options;
using NETCore.Encrypt;
using ZR.Admin.WebApi.Filters;
using ZR.Infrastructure.Cache;
using ZR.Service.System;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Model.Dto;
using ZR.ServiceCore.Services.IService;
namespace ZR.Admin.WebApi.Controllers.System
{
/// <summary>
/// 登录
/// </summary>
[ApiExplorerSettings(GroupName = "sys")]
public class SysLoginController : BaseController
{
//static readonly NLog.Logger logger = NLog.LogManager.GetLogger("LoginController");
private readonly IHttpContextAccessor httpContextAccessor;
private readonly ISysUserService sysUserService;
private readonly ISysMenuService sysMenuService;
private readonly ISysLoginService sysLoginService;
private readonly ISysPermissionService permissionService;
private readonly ICaptcha SecurityCodeHelper;
private readonly ISysConfigService sysConfigService;
private readonly ISysRoleService roleService;
private readonly OptionsSetting optionSettings;
public SysLoginController(
IHttpContextAccessor contextAccessor,
ISysMenuService sysMenuService,
ISysUserService sysUserService,
ISysLoginService sysLoginService,
ISysPermissionService permissionService,
ISysConfigService configService,
ISysRoleService sysRoleService,
ICaptcha captcha,
IOptions<OptionsSetting> optionSettings)
{
httpContextAccessor = contextAccessor;
SecurityCodeHelper = captcha;
this.sysMenuService = sysMenuService;
this.sysUserService = sysUserService;
this.sysLoginService = sysLoginService;
this.permissionService = permissionService;
this.sysConfigService = configService;
roleService = sysRoleService;
this.optionSettings = optionSettings.Value;
}
// RSA私钥
private static readonly string PrivatePem = AppSettings.GetConfig("RSA:PrivatePem");
/// <summary>
/// 登录
/// </summary>
/// <param name="loginBody">登录对象</param>
/// <returns></returns>
[Route("login")]
[HttpPost]
[Log(Title = "登录")]
public IActionResult Login([FromBody] LoginBodyDto loginBody)
{
if (loginBody == null) { throw new CustomException("请求参数错误"); }
loginBody.LoginIP = HttpContextExtension.GetClientUserIp(HttpContext);
var sysConfig = sysConfigService.GetSysConfigByKey("sys.account.captchaOnOff");
var headers = HttpContext.Request.Headers;
var isRemoteInvoke = headers["Remote-Invoke"].FirstOrDefault().ParseToBool();
if (sysConfig?.ConfigValue != "off" && !SecurityCodeHelper.Validate(loginBody.Uuid, loginBody.Code)
&& !isRemoteInvoke)
{
return ToResponse(ResultCode.CAPTCHA_ERROR, "验证码错误");
}
sysLoginService.CheckLockUser(loginBody.Username);
string location = HttpContextExtension.GetIpInfo(loginBody.LoginIP);
// RSA解密
loginBody.Password = EncryptProvider.RSADecryptWithPem(PrivatePem, loginBody.Password);
var user = sysLoginService.Login(loginBody, new SysLogininfor() { LoginLocation = location });
List<SysRole> roles = roleService.SelectUserRoleListByUserId(user.UserId);
//权限集合 eg *:*:*,system:user:list
List<string> permissions = permissionService.GetMenuPermission(user);
TokenModel loginUser = new(user.Adapt<TokenModel>(), roles.Adapt<List<Roles>>());
CacheService.SetUserPerms(GlobalConstant.UserPermKEY + user.UserId, permissions);
return SUCCESS(JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser)));
}
/// <summary>
/// 注销
/// </summary>
/// <returns></returns>
[Log(Title = "注销")]
[HttpPost("logout")]
public IActionResult LogOut()
{
//Task.Run(async () =>
//{
// //注销登录的用户相当于ASP.NET中的FormsAuthentication.SignOut
// await HttpContext.SignOutAsync();
//}).Wait();
var userid = HttpContext.GetUId();
var name = HttpContext.GetName();
CacheService.RemoveUserPerms(GlobalConstant.UserPermKEY + userid);
return SUCCESS(new { name, id = userid });
}
/// <summary>
/// 获取用户信息
/// </summary>
/// <returns></returns>
[Verify]
[HttpGet("getInfo")]
public IActionResult GetUserInfo()
{
long userid = HttpContext.GetUId();
var user = sysUserService.SelectUserById(userid);
//前端校验按钮权限使用
//角色集合 eg: admin,yunying,common
List<string> roles = permissionService.GetRolePermission(user);
//权限集合 eg *:*:*,system:user:list
List<string> permissions = permissionService.GetMenuPermission(user);
user.WelcomeContent = GlobalConstant.WelcomeMessages[new Random().Next(0, GlobalConstant.WelcomeMessages.Length)];
return SUCCESS(new { user, roles, permissions });
}
/// <summary>
/// 获取路由信息
/// </summary>
/// <returns></returns>
[Verify]
[HttpGet("getRouters")]
public IActionResult GetRouters()
{
long uid = HttpContext.GetUId();
var menus = sysMenuService.SelectMenuTreeByUserId(uid);
return SUCCESS(sysMenuService.BuildMenus(menus));
}
/// <summary>
/// 生成图片验证码
/// </summary>
/// <returns></returns>
[HttpGet("captchaImage")]
public IActionResult CaptchaImage()
{
string uuid = Guid.NewGuid().ToString().Replace("-", "");
SysConfig sysConfig = sysConfigService.GetSysConfigByKey("sys.account.captchaOnOff");
var captchaOff = sysConfig?.ConfigValue ?? "0";
var info = SecurityCodeHelper.Generate(uuid, 60);
var obj = new { captchaOff, uuid, img = info.Base64 };// File(stream, "image/png")
return SUCCESS(obj);
}
/// <summary>
/// 注册
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("/register")]
[AllowAnonymous]
[Log(Title = "注册", BusinessType = BusinessType.INSERT)]
public IActionResult Register([FromBody] RegisterDto dto)
{
SysConfig config = sysConfigService.GetSysConfigByKey("sys.account.register");
if (config?.ConfigValue != "true")
{
return ToResponse(ResultCode.CUSTOM_ERROR, "当前系统没有开启注册功能!");
}
SysConfig sysConfig = sysConfigService.GetSysConfigByKey("sys.account.captchaOnOff");
if (sysConfig?.ConfigValue != "off" && !SecurityCodeHelper.Validate(dto.Uuid, dto.Code))
{
return ToResponse(ResultCode.CAPTCHA_ERROR, "验证码错误");
}
SysUser user = sysUserService.Register(dto);
if (user.UserId > 0)
{
return SUCCESS(user);
}
return ToResponse(ResultCode.CUSTOM_ERROR, "注册失败,请联系管理员");
}
/// <summary>
/// 发送邮箱验证码
/// </summary>
/// <param name="sendEmailVo"></param>
/// <returns></returns>
[HttpPost("getMailCode")]
public async Task<IActionResult> GetMailCode([FromBody] SendEmailDto sendEmailVo)
{
if (string.IsNullOrEmpty(sendEmailVo.ToUser))
{
return ToResponse(ApiResult.Error($"邮箱不能为空"));
}
if (string.IsNullOrEmpty(optionSettings.MailOptions.FromEmail) || string.IsNullOrEmpty(optionSettings.MailOptions.Password))
{
return ToResponse(ApiResult.Error($"请配置邮箱信息"));
}
if (sendEmailVo.ToUser.Length > 32)
{
// 解密邮箱
sendEmailVo.ToUser = EncryptProvider.RSADecryptWithPem(PrivatePem, sendEmailVo.ToUser);
// 验证邮箱是否存在于系统用户中
var user = await sysUserService.IsAnyAsync(it => it.Email == sendEmailVo.ToUser);
// if (user == null || user.UserId <= 0)
// 不存在则提示错误
if (!user)
{
return ToResponse(ApiResult.Error($"邮箱错误,请联系管理员"));
}
}
else
{
string[] saltKey = await RedisServer.Session.LRangeAsync("saltMail:" + sendEmailVo.ToUser, 0, 1);
if (saltKey.Length > 0)
{
sendEmailVo.ToUser = saltKey[0];
}
else
{
return ToResponse(ApiResult.Error($"邮箱错误,请联系管理员"));
}
}
// 实例化MailHelper以准备发送邮件
MailHelper mailHelper = new();
// 生成6位邮箱验证码
var verifyCode = mailHelper.VerifyCode(6);
// 设置存入Redis的key为 verifyCode: 前缀 + (盐 + 邮箱的MD5值
var key = "verifyCode:" + sendEmailVo.ToUser;
// 存入Redis中设置过期时间为15分钟
var res = await RedisServer.Session.SetAsync(key, verifyCode, 900);
// 发送邮件,主题为 ZRAdmin.NET邮箱验证码内容为验证码
mailHelper.SendMail(sendEmailVo.ToUser, "ZRAdmin.NET邮箱验证码", verifyCode);
return SUCCESS(res);
}
#region
/// <summary>
/// 生成二维码
/// </summary>
/// <param name="uuid"></param>
/// <param name="deviceId"></param>
/// <returns></returns>
[HttpGet("/GenerateQrcode")]
public IActionResult GenerateQrcode(string uuid, string deviceId)
{
var state = Guid.NewGuid().ToString();
var dict = new Dictionary<string, object>
{
{ "state", state }
};
CacheService.SetScanLogin(uuid, dict);
return SUCCESS(new
{
status = 1,
state,
uuid,
codeContent = new { uuid, deviceId }// "https://qm.qq.com/cgi-bin/qm/qr?k=kgt4HsckdljU0VM-0kxND6d_igmfuPlL&authKey=r55YUbruiKQ5iwC/folG7KLCmZ++Y4rQVgNlvLbUniUMkbk24Y9+zNuOmOnjAjRc&noverify=0"
});
}
/// <summary>
/// 轮询判断扫码状态
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("/VerifyScan")]
[AllowAnonymous]
public IActionResult VerifyScan([FromBody] ScanDto dto)
{
int status = -1;
object token = string.Empty;
if (CacheService.GetScanLogin(dto.Uuid) is Dictionary<string, object> str)
{
status = 0;
str.TryGetValue("token", out token);
if (str.ContainsKey("status") && (string)str.GetValueOrDefault("status") == "success")
{
status = 2;//扫码成功
CacheService.RemoveScanLogin(dto.Uuid);
}
}
return SUCCESS(new { status, token });
}
/// <summary>
/// 移动端扫码登录
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("/ScanLogin")]
[Log(Title = "扫码登录")]
[Verify]
public IActionResult ScanLogin([FromBody] ScanDto dto)
{
if (dto == null) { return ToResponse(ResultCode.CUSTOM_ERROR, "扫码失败"); }
var name = App.HttpContext.GetName();
sysLoginService.CheckLockUser(name);
TokenModel tokenModel = JwtUtil.GetLoginUser(HttpContext);
if (CacheService.GetScanLogin(dto.Uuid) is not null)
{
Dictionary<string, object> dict = new() { };
dict.Add("status", "success");
dict.Add("token", JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(tokenModel)));
CacheService.SetScanLogin(dto.Uuid, dict);
return SUCCESS(1);
}
return ToResponse(ResultCode.FAIL, "二维码已失效");
}
#endregion
}
}