簡體   English   中英

如何防止用戶登錄多個設備以及如何從以前的設備結束 session?

[英]How to prevent a user from logging into multiple devices and how do I end the session from the previous device?

如何防止用戶 ID 一次只能登錄一台計算機? 例如,用戶“Sam”登錄到他的桌面。 用戶“Sam”也從他們的筆記本電腦登錄。 現在我需要桌面上的 session 被殺死,或者我想要發生的事情是使舊令牌在桌面上無效。

我正在使用 dotnet 5.0

您是否需要在數據庫的“用戶”表中創建一個名為 currentloggedinUser 的列來查看他們是否使用 Boolean 登錄?

您還需要安全印章嗎?

下面是放在 AuthController 中的登錄方法

[AllowAnonymous]
[HttpPost("login")]
public async Task<IActionResult> Login(UserForLoginDto userForLoginDto)
{

    try 
    {
        // does the user exist in the database
        var user = await _userManager.FindByNameAsync(userForLoginDto.Username);
        // does the password match
        var result = await _signInManager.CheckPasswordSignInAsync(user, userForLoginDto.Password, true);

        //if the users are logged in, store True Value in the database 
            
            user.nonconcurrent = true; 
            var noLoggedin = await _userManager.UpdateAsync(user);

        if(!result.IsLockedOut)
        {
            if(user.IsEnabled)
            {
                if (result.Succeeded)
                {   
                    var userToReturn = _mapper.Map<UserForReturnDto>(user); 

                    return Ok(new
                    {
                        token = GenerateJwtToken(user).Result,
                        user = userToReturn,                 
                    });
                }
                return Unauthorized("Login Failed"); //if username and password are incorrect return unauthorised
            }
            return Unauthorized("This account is disabled. Please get an administrator to unlock this account.");                               
        }                
        return Unauthorized("This account is locked out. Please try again in 10 minutes or get an " + 
            "administrator to unlock your account.");              
    }
    catch (ArgumentNullException) 
    {
        return Unauthorized("Login Failed");
    }
}

下面是 StartUp.cs 的一部分

 namespace Schedular.API
{
    public class Startup
    {
      public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // services.AddDbContext<DataContext>(x => x.UseMySql(Configuration
            //     .GetConnectionString("DefaultConnection")));

            services.AddDbContext<DataContext>(x => x.UseMySql(Configuration
                .GetConnectionString("DefaultConnection"),
                        new MySqlServerVersion(new Version(8, 0, 21)), 
                        mySqlOptions => mySqlOptions
                            .CharSetBehavior(CharSetBehavior.NeverAppend)));
 
            var lockoutOptions = new LockoutOptions()
            {
                AllowedForNewUsers = true,
                DefaultLockoutTimeSpan = TimeSpan.FromMinutes(10),
                MaxFailedAccessAttempts = 5
            };
                
            // for role and identity authentication
            // initial creation of user in the user table in database 
            IdentityBuilder builder = services.AddIdentityCore<User>(opt =>
            {
                // password requirements
                opt.Lockout = lockoutOptions;
                opt.Password.RequireDigit = true;
                opt.Password.RequiredLength = 8;
                opt.Password.RequireNonAlphanumeric = false;
                opt.Password.RequireUppercase = true;
            });

            builder = new IdentityBuilder(builder.UserType, typeof(Role), builder.Services);
            builder.AddEntityFrameworkStores<DataContext>();
            builder.AddRoleValidator<RoleValidator<Role>>();
            builder.AddRoleManager<RoleManager<Role>>();
            builder.AddSignInManager<SignInManager<User>>();

            
            // allows api to use authentication 
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options => 
                {
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII
                            .GetBytes(Configuration.GetSection("AppSettings:Token").Value)),
                        ValidateIssuer = false,
                        ValidateAudience = false
                    };
                });  


            // authorisation policy
            services.AddAuthorization(options =>
            {
                options.AddPolicy("AdminAccess", policy => policy.RequireRole("Admin"));
                options.AddPolicy("everyone", policy => policy.RequireRole("Admin", "standard"));
            });

            services.AddControllers(options => 
            {
                var policy = new AuthorizationPolicyBuilder()
                    .RequireAuthenticatedUser()
                    .Build();
                
                options.Filters.Add(new AuthorizeFilter(policy));
            })
             .AddNewtonsoftJson(opt =>
            {
                opt.SerializerSettings.ReferenceLoopHandling = 
                Newtonsoft.Json.ReferenceLoopHandling.Ignore;
            }); 
            
            services.AddScoped<ITaskScheduleRepository, TaskScheduleRepository>();
            services.AddScoped<IUserRepository, UserRepository>();
            services.AddScoped<INotesRepository, NotesRepository>();
            services.AddScoped<IAttachmentFileRepository, AttachmentFileRepository>();
            services.AddScoped<ICustomerRepository, CustomerRepository>();
            services.AddScoped<IReportRepository, ReportRepository>();
            services.AddControllers();
            services.AddCors(); 
        


            //allows use of tokens
  

            // Auto Mapper Configurations
            var mappingConfig = new MapperConfiguration(mc =>
            {
                mc.AddProfile(new AutoMapperProfiles());
            });
            IMapper mapper = mappingConfig.CreateMapper();
            services.AddSingleton(mapper);            
        }

這是 user.cs model 文件

using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Identity;

namespace Schedular.API.Models
{
    public class User: IdentityUser<int>
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }

        // to disable account and have a time limiter on when it can be enabled again

        public bool nonconcurrent { get; set; }

        // connect User table to the userRole join table. the below configure the relationship
        public virtual ICollection<UserRole> UserRoles {get; set;}
    }
}

下面是 userForLoginDto

namespace Schedular.API.Dtos
{
    public class UserForLoginDto
    {
        public string Username { get; set; }
        public string Password { get; set; }
    }
}

有幾種方法可以實現這一點。 作為一個簡單的起點,我將提供兩種可能性。

首先可能是在您的數據(數據庫、緩存、令牌等)中包含用戶的 IP 地址。 然后,您可以驗證是否正在使用第一次登錄的 IP 地址,並阻止給定用戶的所有其他登錄嘗試或來自任何其他 IP 的后續請求。

一個簡單的方法可能如下所示:

public class UserForLoginDto
{
    public string Username { get; set; }
    public string Password { get; set; }
    public string CurrentIpAddress { get; set; }
}

然后,您可以將 IP 從請求http 上下文中拉出並比較驗證登錄,如下所示:

if(HttpContext.Connection.RemoteIpAddress == userForLoginDto.CurrentIpAddress)
{
    // do things here after validation.
}
else
{
    // kick-out invalid login attempt.
}

最后,當用戶結束其 session、被踢出不活動等時,您需要清除 IP 值,因此下次登錄可以在不同的設備上更改 ZA12A3079E14CED46E69BA52B8A90B21 地址。 不是一個完美的解決方案,而是一個從簡單開始的好方法。

這對於概念驗證很有用,但對於生產來說不一定是理想的,因為您可能會遇到 IP 並不總是唯一的問題。


其次,代替 IP 地址(它並不總是唯一的),您可以生成一個 GUID、UUID 或其他類型的唯一數據,這些數據通過令牌和持久數據(即在數據庫中)傳遞。

public class UserForLoginDto
{
    public string Username { get; set; }
    public string Password { get; set; }
    public Guid CurrentLogin { get; set; }
}

然后在登錄時繼續進行與以前相同的檢查:

if(Request.Headers.GetValues("your-token-here").FirstOrDefault() == userForLoginDto.CurrentLogin)
{
    // do things here after validation.
}
else
{
    // kick-out invalid login attempt.
}

因此,這與第一個示例非常相似,但現在您有一個最有可能的唯一數據點來檢查請求以獲取更安全的假設。 只需清除 session 端的令牌數據,並將其保存在服務器端,以便在下次成功登錄時重置它。

您還需要在每次成功登錄時創建一個新的唯一數據點,如果已經存在,您可以結束之前的 session 或使登錄嘗試無效,以幫助防止一次使用多個設備。


嘗試查看 JWT(Json Web 令牌)以獲取基於令牌的方法。 那里有很多很好的示例和教程,例如JWT Authentication with C#在 asp net core 中創建和驗證 jwt 令牌

同樣,go 有很多方法可以實現這樣的復雜事情。 所以嘗試幾種不同的方法,看看你喜歡什么,什么最適合你和你的項目或工作。 就個人而言,我建議研究基於令牌的身份驗證和授權,以便您可以更輕松地檢查每個請求,但是 go 在您深入研究時最適合您的場景。

在創建和返回 session 令牌之前,為用戶獲取當前活動的 session 令牌,使其無效,然后返回新的。 有了這個,我們可以確保在給定的時間點只有一個有效的 session。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM