๐ŸชŸ
ASP.NET Core
WebAPI ServerASP.NET CoreMain Subject
  • Intro
  • Program.cs
  • Dependency Injection
  • Host
  • MVC
  • Middleware
  • Model Binding
  • Controller
  • Configure
  • Logging
  • Library
    • WebApplicationzBuilder
  • Example Code
    • basic01
    • basic02
    • basic03
    • basic04
    • basic05
    • basic06
    • basic07
Powered by GitBook
On this page
  • ๊ฐœ๊ด€
  • Porgram.cs
  • LoginController.cs
  • Request POST /login ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ
  • CreateAccount.cs
  • Request POST /CreateAccount ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ
  • Security.cs
  • DBManager.cs
  • ๋นŒ๋“œ๋Š” ๋ผ๋Š”๋ฐ, API์— ์ œ๋Œ€๋กœ ์‘๋‹ต ๋ชปํ•จ
  1. Example Code

basic03

๊ฐœ๊ด€

MySQL๊ณผ Redis๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ณ„์ • ์ƒ์„ฑ๊ณผ ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ๋ฅผ ํ•ฉ๋‹ˆ๋‹ค. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ: MySQL(Sqlkata), Redis(CloudStructures)

  • MySql ์Šคํ‚ค๋งˆ

CREATE TABLE IF NOT EXISTS account_db.`account`
(
    AccountId BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '๊ณ„์ •๋ฒˆํ˜ธ',
    Email VARCHAR(50) NOT NULL UNIQUE COMMENT '์ด๋ฉ”์ผ',
    SaltValue VARCHAR(100) NOT NULL COMMENT  '์•”ํ˜ธํ™” ๊ฐ’',
    HashedPassword VARCHAR(100) NOT NULL COMMENT 'ํ•ด์‹ฑ๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ',
    CreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '์ƒ์„ฑ ๋‚ ์งœ'
)

Porgram.cs

  1. ๋นŒ๋” ์ƒ์„ฑ

    var builder = WebApplication.CreateBuilder(args);

  2. ๋ฏธ๋“ค์›จ์–ด-์ปจํŠธ๋กค๋Ÿฌ ์ถ”๊ฐ€

    builder.Services.AddControllers();

  3. app ์ƒ์„ฑ

    var app = builder.Build();

  4. ์„œ๋น„์Šค ์ถ”๊ฐ€-Routing

    app.UseRouting();

  5. ์„œ๋น„์Šค ์ถ”๊ฐ€-Endpoints ์‚ฌ์šฉ(MapControllers)

    app.UseEndpoints(endpoints => { endpoints.MapControllers(); });

  6. DBManager ์ดˆ๊ธฐํ™”

    • config ํŒŒ์ผ์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ด

    IConfiguration configuration = app.Configuration; DBManager.Init(configuration);

  7. app ์‹คํ–‰

    app.Run(configuration["ServerAddress"]);

    • app.Run()์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ hostIp:port ๋„ฃ์œผ๋ฉด ๊ทธ๊ฑฐ๋กœ listenํ•˜๊ฒŒ ๋จ

LoginController.cs

Request POST /login ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ

  1. ์‘๋‹ตํ•  ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ (์ดํ›„ ๋กœ์ง์—์„œ ์ƒํ™ฉ์— ๋งž๊ฒŒ ๊ฐ’ ์„ค์ •)

    var response = new PkLoginResponse(); response.Result = ErrorCode.None;

  2. SqlKata.QueryFactory์˜ ์ƒ๋ช…์ฃผ๊ธฐ ๋ธ”๋ก์„ ๋งŒ๋“ค์–ด์คŒ

    using (var db = await DBManager.GetGameDBQuery()) { ... 3๋ฒˆ ๊ณผ์ • ... }

  3. 2๋ฒˆ์˜ using๋ธ”๋ก ์•ˆ์—์„œ QueryFactory๋ฅผ ํ†ตํ•ด user info ๊ฐ€์ ธ์™€์„œ ์œ ํšจ์„ฑ ํ™•์ธํ•˜๊ธฐ (Authentication(์ธ์ฆ))

    // parm : PkLoginRequest request
    using (var db = await DBManager.GetGameDBQuery())
    {
    	// user ์ •๋ณด๋ฅผ db์—์„œ ๊ฐ€์ ธ์˜ค๊ธฐ
    	var userInfo = await db.Query("account").Where("Email", request.Email).FirstOrDefaultAsync<DBUserInfo>();
    	
    	// db ์—์„œ ๊ฐ–๊ณ  ์˜จ ์ •๋ณด์— ๋กœ๊ทธ์ธ์— ํ•„์š”ํ•œ ๋‚ด์šฉ์ด ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธ
      if (userInfo == null || string.IsNullOrEmpty(userInfo.HashedPassword))
      {
          response.Result = ErrorCode.Login_Fail_NotUser;
          return response;
      }
      
      // ์œ ์ €๊ฐ€ ์ž…๋ ฅํ•œ password๋ฅผ hash ๊ฐ’์œผ๋กœ ๋ฐ”๊ฟˆ
      var hashingPassword = Security.MakeHashingPassWord(userInfo.SaltValue, request.Password);
    
    	// ๋น„๋ฐ€๋ฒˆํ˜ธ ์ฒดํฌ. ์œ ํšจํ•˜์ง€ ์•Š์œผ๋ฉด ์—๋Ÿฌ ๋ฐ˜ํ™˜
      Console.WriteLine($"[Request Login] Email:{request.Email}, request.Password:{request.Password},  saltValue:{userInfo.SaltValue}, hashingPassword:{hashingPassword}");
      if (userInfo.HashedPassword != hashingPassword)
      {
          response.Result = ErrorCode.Login_Fail_PW;
          return response;
      }
    
    	// ๊ฐ€์ ธ์˜จ db ์ •๋ณด ๋†“์•„์ฃผ๊ธฐ (ํ• ๋‹น ํ•ด์ œ)
      db.Dispose();
    }
  4. ์œ ํšจํ•œ ์‚ฌ์šฉ์ž๋ผ๊ณ  ํŒ๋‹จ ๋์œผ๋ฉด, ํ† ํฐ ๋ฐœ๊ธ‰ํ•˜๊ธฐ

    string tokenValue = Security.CreateAuthToken();
    var idDefaultExpiry = TimeSpan.FromDays(1);
  5. ์‚ฌ์šฉ์ž์—๊ฒŒ ์ค„ ํ† ํฐ ์ •๋ณด redis์— ๋„ฃ๊ธฐ

    var redisId = new RedisString<string>(DBManager.RedisConn, request.Email, idDefaultExpiry);
    await redisId.SetAsync(tokenValue);
    • ์ดํ›„์— ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ฐ์ดํ„ฐ ์š”์ฒญ์‹œ ์ด ํ† ํฐ ์ •๋ณด๋ฅผ ์„œ๋ฒ„์—๊ฒŒ ๋ณด๋‚ผ ๊ฒƒ์ด๋ฉฐ, ์„œ๋ฒ„๋Š” ์ด ํ† ํด์„ Redis์— ์žˆ๋Š”์ง€ ํ™•์ธํ•˜์—ฌ ์‚ฌ์šฉ์ž ์ธ์ฆ ๋ฐ ๊ถŒํ•œ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ์Œ

  6. ์‚ฌ์šฉ์ž์—๊ฒŒ ํ† ํฐ ๋ณด๋‚ด์ฃผ๊ธฐ

    response.Authtoken = tokenValue;
    return response;

CreateAccount.cs

Request POST /CreateAccount ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ

  1. ์‘๋‹ตํ•  ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ (์ดํ›„ ๋กœ์ง์—์„œ ์ƒํ™ฉ์— ๋งž๊ฒŒ ๊ฐ’ ์„ค์ •)

    var response = new PkCreateAccountResponse {Result = ErrorCode.None};
  2. ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ €์žฅํ•  hash ๊ฐ’ ๋งŒ๋“ค๊ธฐ

    var saltValue = Security.SaltString();
    var hashingPassword = Security.MakeHashingPassWord(saltValue, request.Password);
  3. SqlKata.QueryFactory์˜ ์ƒ๋ช…์ฃผ๊ธฐ ๋ธ”๋ก์„ ๋งŒ๋“ค์–ด์คŒ

    using (var db = await DBManager.GetGameDBQuery()) { ... 4๋ฒˆ๊ณผ์ • ... }
  4. db์— ์ƒˆ๋กœ์šด ๊ณ„์ • ์ •๋ณด ๋“ฑ๋ก(insert)

    try
    {
    		// db ์— ๊ณ„์ •์ •๋ณด insert : Async(๋น„๋™๊ธฐ)๋ฐฉ์‹์œผ๋กœ ๋„ฃ์Œ
        var count = await db.Query("account").InsertAsync(new
        {
            Email = request.Email,
            SaltValue = saltValue,
            HashedPassword = hashingPassword
        });
    
    		// ์œ„์˜ insert๊ฐ€ ์‹คํŒจ ํ–ˆ์„ ๊ฒฝ์šฐ. DB์—์„œ Email์˜ constraint๋ฅผ ์ค‘๋ณต ๋ถˆ๊ฐ€๋กœ ์„ค์ •
        if (count != 1)
        {
            response.Result = ErrorCode.Create_Account_Fail_Duplicate;
        }
    
        Console.WriteLine($"[Request CreateAccount] Email:{request.Email}, saltValue:{saltValue}, hashingPassword:{hashingPassword}");
    }
    // QueryFactory ์—์„œ ์—๋Ÿฌ ๋ฐœ์ƒ
    catch(Exception ex)
    {
        Console.WriteLine(ex.ToString());
        response.Result = ErrorCode.Create_Account_Fail_Exception;
        return response;
    }
    // ๊ฐ€์ ธ์˜จ db ์ •๋ณด ๋†“์•„์ฃผ๊ธฐ (ํ• ๋‹น ํ•ด์ œ)
    finally
    {
        db.Dispose();
    }
    • ์™œ try-catch๋กœ? db์—ฐ๊ฒฐ ์‹คํŒจ or QueryFactory ๊ธฐ๋Šฅ ์‹คํŒจ์˜ ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•œ๊ฑด๊ฐ€?

  5. ์‘๋‹ต ๋ฐ˜ํ™˜

    return response;

Security.cs

  • ๋ณด์•ˆ, ์•”ํ˜ธํ™” ๊ด€๋ จ๋œ ์ž‘์—…์„ ์œ„ํ•œ ๋ฉ”์†Œ๋“œ ๊ตฌํ˜„

    private const String AllowableCharacters = "abcdefghijklmnopqrstuvwxyz0123456789";

  1. ํŒจ์Šค์›Œ๋“œ ํ•ด์‹ฑ ์ ์šฉ

    public static String MakeHashingPassWord(String saltValue, String pw)
    {
        var sha = SHA256.Create();
        var hash = sha.ComputeHash(Encoding.ASCII.GetBytes(saltValue + pw));
        var stringBuilder = new StringBuilder();
        foreach (var b in hash)
        {
            stringBuilder.AppendFormat("{0:x2}", b);
        }
    
        return stringBuilder.ToString();
    }
  2. ํ•ด์‹ฑ์„ ์œ„ํ•œ ์•”ํ˜ธ ํ‚ค ์ƒ์„ฑ

    public static String SaltString()
    {
        var bytes = new Byte[64];
        using (var random = RandomNumberGenerator.Create())
        {
            random.GetBytes(bytes);
        }
    
        return new String(bytes.Select(x => AllowableCharacters[x % AllowableCharacters.Length]).ToArray());
    }
  3. ์ธ์ฆ ํ† ํฐ ์ƒ์„ฑ

    public static String CreateAuthToken()
    {
        var bytes = new Byte[25];
        using (var random = RandomNumberGenerator.Create())
        {
            random.GetBytes(bytes);
        }
    
        return new String(bytes.Select(x => AllowableCharacters[x % AllowableCharacters.Length]).ToArray());
    }

DBManager.cs

  1. ์—ฐ๊ฒฐํ•  DB server ๋“ค์— ๋Œ€ํ•œ ip/port ์ •๋ณด ๋“ฑ์„ ๊ฐ–๊ณ  ์žˆ์Œ

    static string GameDBConnectString;
    static string RedisAddress;
    
    public static RedisConnection RedisConn { get; set; }
    
    public static void Init(IConfiguration configuration)
    {
        GameDBConnectString = configuration.GetSection("DBConnection")["MySqlGame"];
        RedisAddress = configuration.GetSection("DBConnection")["Redis"];
        
        var config = new RedisConfig("basic", RedisAddress);
        RedisConn = new RedisConnection(config);
    }
  2. QueryFactory๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜ ์ œ๊ณต

    public static async Task<QueryFactory> GetGameDBQuery()
    {
        var connection = new MySqlConnection(GameDBConnectString);
        await connection.OpenAsync();
        
        var compiler = new SqlKata.Compilers.MySqlCompiler();
        var queryFactory = new SqlKata.Execution.QueryFactory(connection, compiler);
        
        return queryFactory;
    }

๋นŒ๋“œ๋Š” ๋ผ๋Š”๋ฐ, API์— ์ œ๋Œ€๋กœ ์‘๋‹ต ๋ชปํ•จ

  • ์˜ˆ์ œ ์ฝ”๋“œ์ผ ๋ฟ, ์•„์ง DB server๋ž‘ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์ง€ ์•Š์Œ

Previousbasic02Nextbasic04

Last updated 1 year ago