新增注册流程
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
using Apimanager_backend.Data;
|
||||
using Apimanager_backend.Dtos;
|
||||
using Apimanager_backend.Exceptions;
|
||||
using Apimanager_backend.Models;
|
||||
using Apimanager_backend.Tools;
|
||||
using AutoMapper;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace Apimanager_backend.Services
|
||||
{
|
||||
public class AuthService:IAuthService
|
||||
{
|
||||
private readonly ApiContext apiContext;
|
||||
private readonly ILogger<IAuthService> logger;
|
||||
private readonly IConnectionMultiplexer redis;
|
||||
private readonly IEmailService emailService;
|
||||
private readonly IMapper mapper;
|
||||
private readonly int DbIndex = 1;
|
||||
public AuthService(ApiContext apiContext, IMapper automapper,ILogger<AuthService> logger,IConnectionMultiplexer redis,IEmailService emailService)
|
||||
{
|
||||
this.apiContext = apiContext;
|
||||
this.mapper = automapper;
|
||||
this.logger = logger;
|
||||
this.redis = redis;
|
||||
this.emailService = emailService;
|
||||
}
|
||||
public async Task<UserInfoDto> LoginAsync(string username, string password)
|
||||
{
|
||||
//查找用户
|
||||
User? user = await apiContext.Users.Include(x => x.Roles).SingleOrDefaultAsync(x =>
|
||||
x.Username == username && x.PassHash == password
|
||||
);
|
||||
|
||||
//用户不存在或密码错误都为登录失败
|
||||
if (user == null)
|
||||
{
|
||||
throw new BaseException(2001, "Invalid username or password");
|
||||
}
|
||||
|
||||
//用户被禁用
|
||||
if (user.IsBan)
|
||||
{
|
||||
throw new BaseException(2002, "User account is disabled");
|
||||
}
|
||||
|
||||
return mapper.Map<UserInfoDto>(user);
|
||||
}
|
||||
|
||||
public async Task<UserInfoDto> RegisterAsync(RegisterRequestDto dto)
|
||||
{
|
||||
var db = redis.GetDatabase(DbIndex);
|
||||
//获取邮箱对应验证码
|
||||
var code = await db.StringGetAsync(dto.Email);
|
||||
if(!code.HasValue || code.ToString() != dto.VerificationCode)
|
||||
{
|
||||
throw new BaseException(5005,"验证码错误");
|
||||
}
|
||||
User user = new User
|
||||
{
|
||||
Username = dto.Username,
|
||||
PassHash = dto.Password,
|
||||
Email = dto.Email,
|
||||
IsBan = false,
|
||||
IsDelete = false,
|
||||
Balance = 0,
|
||||
};
|
||||
try
|
||||
{
|
||||
//添加新用户
|
||||
await apiContext.Users.AddAsync(user);
|
||||
await apiContext.SaveChangesAsync();
|
||||
UserRole userRole = new UserRole
|
||||
{
|
||||
UserId = user.Id,
|
||||
Role = "User"
|
||||
};
|
||||
await apiContext.UserRoles.AddAsync(userRole);
|
||||
await apiContext.SaveChangesAsync();
|
||||
return mapper.Map<UserInfoDto>(user);
|
||||
}catch(Exception e)
|
||||
{
|
||||
throw new BaseException(1005,e.Message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task SendRegisterCodeAsync(string email)
|
||||
{
|
||||
//生成随机码
|
||||
string code = RandomCodeHelper.GetRandomCodeStr();
|
||||
string subject = "注册验证码";
|
||||
string body = $"您的注册验证码为:{code}<br>有效期60分钟!";
|
||||
//随机码写入redis
|
||||
var db = redis.GetDatabase(DbIndex);
|
||||
bool redisSuccess = await db.StringSetAsync(email,code,TimeSpan.FromHours(1));
|
||||
if (!redisSuccess)
|
||||
{
|
||||
throw new BaseException(1005,"Redis Str Set Error");
|
||||
}
|
||||
//发送邮件
|
||||
await emailService.SendEmailAsync(email,subject,body);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using Apimanager_backend.Exceptions;
|
||||
using System.Net;
|
||||
using System.Net.Mail;
|
||||
|
||||
namespace Apimanager_backend.Services
|
||||
{
|
||||
public class EmailService:IEmailService
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
public EmailService(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
SmtpHost = _configuration["EmailSettings:Server"];
|
||||
Port = int.Parse(_configuration["EmailSettings:Port"]);
|
||||
Username = _configuration["EmailSettings:Username"];
|
||||
Password = _configuration["EmailSettings:Password"];
|
||||
EnableSSL = bool.Parse(_configuration["EmailSettings:Ssl"]);
|
||||
}
|
||||
private string SmtpHost { get; set; }
|
||||
private int Port { get; set; }
|
||||
public bool EnableSSL { get; set; }
|
||||
private string Username { get; set; }
|
||||
private string Password { get; set; }
|
||||
public async Task SendEmailAsync(string toEmail,string subject,string body)
|
||||
{
|
||||
try
|
||||
{
|
||||
using SmtpClient smtpClient = new SmtpClient(SmtpHost, Port)
|
||||
{
|
||||
Credentials = new NetworkCredential(Username, Password),
|
||||
EnableSsl = EnableSSL, //启用ssl
|
||||
Timeout = 30000
|
||||
};
|
||||
using var emailMessage = new MailMessage
|
||||
{
|
||||
From = new MailAddress(Username),
|
||||
Subject = subject,
|
||||
Body = body,
|
||||
IsBodyHtml = true
|
||||
};
|
||||
emailMessage.To.Add(toEmail);
|
||||
await smtpClient.SendMailAsync(emailMessage);
|
||||
}catch(Exception e)
|
||||
{
|
||||
throw new BaseException(5004,e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Apimanager_backend.Dtos;
|
||||
|
||||
namespace Apimanager_backend.Services
|
||||
{
|
||||
public interface IAuthService
|
||||
{
|
||||
/// <summary>
|
||||
/// 登录用户,根据用户名和密码进行身份验证。
|
||||
/// </summary>
|
||||
/// <param name="username">用户名</param>
|
||||
/// <param name="password">密码</param>
|
||||
/// <returns>包含用户信息的 <see cref="UserInfoBaseDto"/></returns>
|
||||
Task<UserInfoDto> LoginAsync(string username, string password);
|
||||
/// <summary>
|
||||
/// 用户注册邮箱验证码
|
||||
/// </summary>
|
||||
/// <param name="email"></param>
|
||||
/// <returns></returns>
|
||||
Task SendRegisterCodeAsync(string email);
|
||||
/// <summary>
|
||||
/// 用户注册
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
Task<UserInfoDto> RegisterAsync(RegisterRequestDto dto);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Apimanager_backend.Services
|
||||
{
|
||||
public interface IEmailService
|
||||
{
|
||||
/// <summary>
|
||||
/// 发送邮件
|
||||
/// </summary>
|
||||
/// <param name="toEmail">收件人邮箱</param>
|
||||
/// <param name="subject">主题</param>
|
||||
/// <param name="body">正文</param>
|
||||
/// <returns></returns>
|
||||
public Task SendEmailAsync(string toEmail,string subject,string body);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
namespace Apimanager_backend.Services
|
||||
{
|
||||
public interface IRefreshTokenService
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建刷新令牌
|
||||
/// </summary>
|
||||
/// <param name="userId">用户id</param>
|
||||
/// <returns>刷新令牌</returns>
|
||||
Task<string> CreateRefereshTokenAsync(string userId);
|
||||
/// <summary>
|
||||
/// 验证刷新令牌
|
||||
/// </summary>
|
||||
/// <param name="refreshToken">刷新令牌</param>
|
||||
/// <returns>是否验证通过</returns>
|
||||
Task<bool> ValidateRefreshTokenAsync(string userId,string refreshToken);
|
||||
/// <summary>
|
||||
/// 删除刷新令牌
|
||||
/// </summary>
|
||||
/// <param name="refreshToken">刷新令牌</param>
|
||||
/// <returns>是否删除成功</returns>
|
||||
Task DeleterRefreshTokenAsync(string userId);
|
||||
/// <summary>
|
||||
/// 更新刷新令牌有效期
|
||||
/// </summary>
|
||||
/// <param name="refreshToken">刷新令牌</param>
|
||||
/// <returns>是否成功</returns>
|
||||
Task UpdateRefreshTokenAsync(string userId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Apimanager_backend.Models;
|
||||
|
||||
namespace Apimanager_backend.Services
|
||||
{
|
||||
public interface ITokenService
|
||||
{
|
||||
/// <summary>
|
||||
/// 拥护凭证
|
||||
/// </summary>
|
||||
/// <param name="userId">用户ID</param>
|
||||
/// <param name="username">用户名</param>
|
||||
/// <param name="role">角色</param>
|
||||
/// <returns>token</returns>
|
||||
string GenerateAccessToken(string userId, List<UserRole> roles);
|
||||
}
|
||||
}
|
||||
@@ -6,79 +6,83 @@ namespace Apimanager_backend.Services
|
||||
{
|
||||
public interface IUserService
|
||||
{
|
||||
/// <summary>
|
||||
/// 登录用户,根据用户名和密码进行身份验证。
|
||||
/// </summary>
|
||||
/// <param name="username">用户名</param>
|
||||
/// <param name="password">密码</param>
|
||||
/// <returns>包含用户信息的 <see cref="UserInfoDto"/></returns>
|
||||
Task<UserInfoDto> LoginAsync(string username, string password);
|
||||
/// <summary>
|
||||
/// 发送密码重置邮件到指定邮箱。
|
||||
/// </summary>
|
||||
/// <param name="email">用户注册的邮箱地址</param>
|
||||
/// <returns>异步操作</returns>
|
||||
Task SendResetPasswordEmailAsync(string email);
|
||||
|
||||
/// <summary>
|
||||
/// 发送密码重置邮件到指定邮箱。
|
||||
/// </summary>
|
||||
/// <param name="email">用户注册的邮箱地址</param>
|
||||
/// <returns>异步操作</returns>
|
||||
Task SendResetPasswordEmailAsync(string email);
|
||||
/// <summary>
|
||||
/// 重置用户密码,验证重置令牌的有效性并更新密码。
|
||||
/// </summary>
|
||||
/// <param name="email">用户邮箱地址</param>
|
||||
/// <param name="token">重置密码的令牌</param>
|
||||
/// <param name="newPassword">新的密码</param>
|
||||
/// <returns>异步操作</returns>
|
||||
Task ResetPasswordAsync(string email, string token, string newPassword);
|
||||
|
||||
/// <summary>
|
||||
/// 重置用户密码,验证重置令牌的有效性并更新密码。
|
||||
/// </summary>
|
||||
/// <param name="email">用户邮箱地址</param>
|
||||
/// <param name="token">重置密码的令牌</param>
|
||||
/// <param name="newPassword">新的密码</param>
|
||||
/// <returns>异步操作</returns>
|
||||
Task ResetPasswordAsync(string email, string token, string newPassword);
|
||||
/// <summary>
|
||||
/// 获取用户信息。
|
||||
/// </summary>
|
||||
/// <param name="userId">用户ID</param>
|
||||
/// <returns>包含用户信息的 <see cref="UserInfoDto"/></returns>
|
||||
Task<UserInfoDto> GetUserAsync(int userId);
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户信息。
|
||||
/// </summary>
|
||||
/// <param name="username">用户名</param>
|
||||
/// <returns>包含用户信息的 <see cref="UserInfoDto"/></returns>
|
||||
Task<UserInfoDto> GetUserAsync(string username);
|
||||
/// <summary>
|
||||
/// 更新用户信息。
|
||||
/// </summary>
|
||||
/// <param name="user">包含更新信息的 <see cref="UpdateUserDto"/></param>
|
||||
/// <returns>更新后的 <see cref="UserInfoDto"/></returns>
|
||||
Task<UserInfoDto> UpdateUserAsync(UpdateUserDto user);
|
||||
|
||||
/// <summary>
|
||||
/// 更新用户信息。
|
||||
/// </summary>
|
||||
/// <param name="user">包含更新信息的 <see cref="UpdateUserDto"/></param>
|
||||
/// <returns>更新后的 <see cref="UserInfoDto"/></returns>
|
||||
Task<UserInfoDto> UpdateUserAsync(UpdateUserDto user);
|
||||
/// <summary>
|
||||
/// 删除指定的用户。
|
||||
/// </summary>
|
||||
/// <param name="username">要删除的用户名</param>
|
||||
/// <returns>异步操作</returns>
|
||||
Task DeleteUserAsync(string username);
|
||||
|
||||
/// <summary>
|
||||
/// 删除指定的用户。
|
||||
/// </summary>
|
||||
/// <param name="username">要删除的用户名</param>
|
||||
/// <returns>异步操作</returns>
|
||||
Task DeleteUserAsync(string username);
|
||||
/// <summary>
|
||||
/// 创建新用户。
|
||||
/// </summary>
|
||||
/// <param name="user">包含新用户信息的 <see cref="CreateUserDto"/></param>
|
||||
/// <returns>创建成功的用户信息 <see cref="UserInfoDto"/></returns>
|
||||
Task<UserInfoDto> CreateUserAsync(CreateUserDto user);
|
||||
|
||||
/// <summary>
|
||||
/// 创建新用户。
|
||||
/// </summary>
|
||||
/// <param name="user">包含新用户信息的 <see cref="CreateUserDto"/></param>
|
||||
/// <returns>创建成功的用户信息 <see cref="UserInfoDto"/></returns>
|
||||
Task<UserInfoDto> CreateUserAsync(CreateUserDto user);
|
||||
/// <summary>
|
||||
/// 禁用用户,使其无法登录。
|
||||
/// </summary>
|
||||
/// <param name="username">要禁用的用户名</param>
|
||||
/// <returns>异步操作</returns>
|
||||
Task BanUserAsync(string username);
|
||||
|
||||
/// <summary>
|
||||
/// 禁用用户,使其无法登录。
|
||||
/// </summary>
|
||||
/// <param name="username">要禁用的用户名</param>
|
||||
/// <returns>异步操作</returns>
|
||||
Task BanUserAsync(string username);
|
||||
/// <summary>
|
||||
/// 取消禁用用户,恢复登录权限。
|
||||
/// </summary>
|
||||
/// <param name="username">要取消禁用的用户名</param>
|
||||
/// <returns>异步操作</returns>
|
||||
Task UnbanUserAsync(string username);
|
||||
|
||||
/// <summary>
|
||||
/// 取消禁用用户,恢复登录权限。
|
||||
/// </summary>
|
||||
/// <param name="username">要取消禁用的用户名</param>
|
||||
/// <returns>异步操作</returns>
|
||||
Task UnbanUserAsync(string username);
|
||||
|
||||
/// <summary>
|
||||
/// 获取分页的用户列表。
|
||||
/// </summary>
|
||||
/// <param name="page">要获取的页码,从1开始</param>
|
||||
/// <param name="pageSize">每页的用户数量</param>
|
||||
/// <param name="desc">是否按降序排序</param>
|
||||
/// <returns>包含用户信息的 <see cref="List{UserInfoDto}"/></returns>
|
||||
Task<List<UserInfoDto>> GetUsersAsync(int page, int pageSize, bool desc);
|
||||
/// <summary>
|
||||
/// 获取分页的用户列表。
|
||||
/// </summary>
|
||||
/// <param name="page">要获取的页码,从1开始</param>
|
||||
/// <param name="pageSize">每页的用户数量</param>
|
||||
/// <param name="desc">是否按降序排序</param>
|
||||
/// <returns>包含用户信息的 <see cref="List{UserInfoDto}"/></returns>
|
||||
Task<List<UserInfoDto>> GetUsersAsync(int page, int pageSize, bool desc);
|
||||
/// <summary>
|
||||
/// 检测用户名是否被使用
|
||||
/// </summary>
|
||||
/// <param name="username">用户名</param>
|
||||
/// <returns></returns>
|
||||
Task<bool> IsUsernameExist(string username);
|
||||
/// <summary>
|
||||
/// 检测邮箱是否被使用
|
||||
/// </summary>
|
||||
/// <param name="email">邮箱</param>
|
||||
/// <returns></returns>
|
||||
Task<bool> IsEmailExist(string email);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
using Apimanager_backend.Exceptions;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace Apimanager_backend.Services
|
||||
{
|
||||
public class RefreshTokenService : IRefreshTokenService
|
||||
{
|
||||
private readonly IConnectionMultiplexer redis;
|
||||
private readonly IConfiguration configuration;
|
||||
private readonly int DbIndex = 0;
|
||||
public RefreshTokenService(IConnectionMultiplexer redis, IConfiguration configuration)
|
||||
{
|
||||
this.redis = redis;
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
public async Task<string> CreateRefereshTokenAsync(string userId)
|
||||
{
|
||||
var refreshToken = Guid.NewGuid().ToString();
|
||||
var expiryDays = Convert.ToDouble(configuration["JwtSettings:RefreshTokenExpiryDays"]);
|
||||
|
||||
// 保存到Redis,设置过期时间
|
||||
var db = redis.GetDatabase(DbIndex);
|
||||
var res = await db.StringSetAsync( userId , refreshToken, TimeSpan.FromDays(expiryDays));
|
||||
if (!res)
|
||||
{
|
||||
throw new BaseException(1006, "Service unavailable");
|
||||
}
|
||||
return refreshToken;
|
||||
}
|
||||
|
||||
public async Task DeleterRefreshTokenAsync(string userId)
|
||||
{
|
||||
var db = redis.GetDatabase(DbIndex);
|
||||
bool res = await db.KeyDeleteAsync(userId);
|
||||
if (!res)
|
||||
{
|
||||
throw new BaseException(1006, "Service unavailable");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateRefreshTokenAsync(string userId)
|
||||
{
|
||||
var db = redis.GetDatabase(DbIndex);
|
||||
var expiryDays = Convert.ToDouble(configuration["JwtSettings:RefreshTokenExpiryDays"]);
|
||||
//获取refresh剩余有效时间
|
||||
var time =await db.KeyTimeToLiveAsync(userId);
|
||||
//判断有效时间是否大于零天小于三天,否则不刷新有效期
|
||||
if(time <= TimeSpan.Zero || time >= TimeSpan.FromDays(3))
|
||||
{
|
||||
return;
|
||||
}
|
||||
//刷新过期时间
|
||||
await db.KeyExpireAsync(userId,TimeSpan.FromDays(expiryDays));
|
||||
}
|
||||
|
||||
public async Task<bool> ValidateRefreshTokenAsync(string userId,string refreshToken)
|
||||
{
|
||||
var db = redis.GetDatabase(DbIndex);
|
||||
var redisValue = await db.StringGetAsync(userId);
|
||||
//验证refreshToken是否存在
|
||||
if (!redisValue.HasValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
string refreshTokenTrue = redisValue.ToString();
|
||||
if (!refreshToken.Equals(refreshTokenTrue))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
using Apimanager_backend.Models;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
|
||||
namespace Apimanager_backend.Services
|
||||
{
|
||||
public class TokenService:ITokenService
|
||||
{
|
||||
public readonly IConfiguration configuration;
|
||||
public TokenService(IConfiguration configuration)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
public string GenerateAccessToken(string userId,List<UserRole> roles)
|
||||
{
|
||||
var jwtSettings = configuration.GetSection("JwtSettings");
|
||||
|
||||
// 创建Claims列表,包含用户名和角色信息
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new Claim("userId", userId), // 使用userId作为唯一标识
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
|
||||
};
|
||||
//添加用户角色
|
||||
foreach(var role in roles)
|
||||
{
|
||||
var claim = new Claim(ClaimTypes.Role, role.Role.ToString());
|
||||
claims.Add(claim);
|
||||
}
|
||||
|
||||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["Secret"]));
|
||||
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
|
||||
|
||||
var token = new JwtSecurityToken(
|
||||
issuer: jwtSettings["Issuer"],
|
||||
audience: jwtSettings["Audience"],
|
||||
claims: claims,
|
||||
expires: DateTime.Now.AddMinutes(Convert.ToDouble(jwtSettings["AccessTokenExpiryMinutes"])),
|
||||
signingCredentials: creds);
|
||||
|
||||
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,9 +34,15 @@ namespace Apimanager_backend.Services
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<UserInfoDto> GetUserAsync(string username)
|
||||
public async Task<UserInfoDto> GetUserAsync(int userId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
User? user = await apiContext.Users.SingleOrDefaultAsync(x => x.Id == userId);
|
||||
//未找到用户
|
||||
if (user == null)
|
||||
{
|
||||
throw new BaseException(2004, "User not found");
|
||||
}
|
||||
return mapper.Map<UserInfoDto>(user);
|
||||
}
|
||||
|
||||
public Task<List<UserInfoDto>> GetUsersAsync(int page, int pageSize, bool desc)
|
||||
@@ -44,26 +50,14 @@ namespace Apimanager_backend.Services
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<UserInfoDto> LoginAsync(string username, string password)
|
||||
public async Task<bool> IsEmailExist(string email)
|
||||
{
|
||||
//查找用户
|
||||
User? user = await apiContext.Users.SingleOrDefaultAsync(x =>
|
||||
x.Username == username && x.PassHash == password
|
||||
);
|
||||
return await apiContext.Users.AnyAsync(x => x.Email == email);
|
||||
}
|
||||
|
||||
//用户不存在或密码错误都为登录失败
|
||||
if(user == null)
|
||||
{
|
||||
throw new BaseException(2001, "Invalid username or password");
|
||||
}
|
||||
|
||||
//用户被禁用
|
||||
if (user.IsBan)
|
||||
{
|
||||
throw new BaseException(2002, "User account is disabled");
|
||||
}
|
||||
|
||||
return mapper.Map<UserInfoDto>(user);
|
||||
public async Task<bool> IsUsernameExist(string username)
|
||||
{
|
||||
return await apiContext.Users.AnyAsync(x => x.Username == username);
|
||||
}
|
||||
|
||||
public Task ResetPasswordAsync(string email, string token, string newPassword)
|
||||
|
||||
Reference in New Issue
Block a user