前言

上一篇我們在後端建立了 Token 發行與驗證的機制,今天我們回到主題在 Blaoz 怎麼取得 Token

登入畫面

首先我們建立一個登入的頁面,有帳號和密碼欄位以及一個送出的按鈕

  1. 建立登入頁,在Blazor.JWT.Client專案內的Page資料夾建立Login.razor,並且完成表單
  • Razor 元件
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@page "/Login"
<div class="form-signin w-100 m-auto">
    <form data-bitwarden-watching="1">
        <h1 class="h3 mb-3 fw-normal">請登入</h1>

        <div class="form-floating">
            <input type="email" class="form-control" id="floatingInput" placeholder="name@example.com">
            <label for="floatingInput">Email address</label>
        </div>
        <div class="form-floating">
            <input type="password" class="form-control" id="floatingPassword" placeholder="Password">
            <label for="floatingPassword">Password</label>
        </div>

        <div class="checkbox mb-3">
            <label>
                <input type="checkbox" value="remember-me"> 記住我
            </label>
        </div>
        <button class="w-100 btn btn-lg btn-primary" type="submit">登入</button>
        <p class="mt-5 mb-3 text-muted">© 2022</p>
    </form>
</div>
@code {

}
  • CSS
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
.form-signin {
    max-width: 330px;
    padding: 15px;
}

.form-signin .form-floating:focus-within {
    z-index: 2;
}

.form-signin input[type="email"] {
    margin-bottom: -1px;
    border-bottom-right-radius: 0;
    border-bottom-left-radius: 0;
}

.form-signin input[type="password"] {
    margin-bottom: 10px;
    border-top-left-radius: 0;
    border-top-right-radius: 0;
}

main {
    display: flex;
    align-items: center;
    padding-top: 40px;
    padding-bottom: 40px;
    background-color: #f5f5f5;
}
  1. 建立資料類別以及資料繫結 我們在 Blazor.Jwt.Shared 建立登入表單所需要的類別LoginDto.cs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
    public class LoginDto
    {
        /// <summary>
        /// 使用者名稱
        /// </summary>
        public string Username { get; set; }

        /// <summary>
        /// 密碼
        /// </summary>
        public string Password { get; set; }
    }

並且修改 Login.razor 的內容

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@page "/Login"
@using Blazor.Jwt.Shared
<div class="form-signin w-100 m-auto">
    <EditForm Model="@loginDto" OnValidSubmit="HandleValidSubmit" data-bitwarden-watching="1">
        <h1 class="h3 mb-3 fw-normal">請登入</h1>

        <div class="form-floating">
            <input type="email" class="form-control" id="floatingInput" placeholder="name@example.com"
            @bind-value="loginDto.Username">
            <label for="floatingInput">Email address</label>
        </div>
        <div class="form-floating">
            <input type="password" class="form-control" id="floatingPassword" placeholder="Password"
                   @bind-value="loginDto.Password">
            <label for="floatingPassword">Password</label>
        </div>

        <div class="checkbox mb-3">
            <label>
                <input type="checkbox" value="remember-me"> 記住我
            </label>
        </div>
        <button class="w-100 btn btn-lg btn-primary" type="submit">登入</button>
        <p class="mt-5 mb-3 text-muted">© 2022</p>
    </EditForm>
</div>
@code {
    private LoginDto loginDto = new LoginDto();

    private void HandleValidSubmit()
    {
        Console.WriteLine($"Username:{loginDto.Username}");
        Console.WriteLine($"Password:{loginDto.Password}");
    }

}

完成後可以試著填入資料並點選登入,如果沒意外的話可以有 Console 上看到,就代表成功將欄值綁定了

  1. 將使用者名稱及密碼送到後端取得 Token Razor 元件中加入 using
1
2
@using System.Net.Http
@inject HttpClient Http

在方法內呼叫 API

1
2
3
4
5
private async Task HandleValidSubmit()
{
    var token = await(await Http.PostAsJsonAsync("api/Authorize/Login", loginDto)).Content.ReadAsStringAsync();
    Console.WriteLine($"Token {token}");
}

WebAPI 部分

做完前端後,要完成後端的部分不然是沒辦法作用的,在昨天的 API Controller 內加入一個新的 Action

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[HttpPost]
public IActionResult Login(LoginDto query)
{
    var key = "2022/09/28 IThome 鐵人賽";
    var claims = new List<Claim>
    {
        // 使用者名稱
        new(JwtRegisteredClaimNames.Sub,query.Username),
        // Token Id
        new(JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString())
    };
    var claimsIdentity = new ClaimsIdentity(claims);
    var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
    var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
    var tokenDescriptor = new SecurityTokenDescriptor
    {
        Issuer = "發行者",
        Subject = claimsIdentity,
        Expires = DateTime.UtcNow.AddMinutes(30),
        SigningCredentials = signingCredentials
    };
    var tokenHandler = new JwtSecurityTokenHandler();
    var securityToken = tokenHandler.CreateToken(tokenDescriptor);
    return Ok(tokenHandler.WriteToken(securityToken));
}

完成後,建置完成後,到剛剛做的登入頁面,輸入帳號密碼登入!

小結

做到這邊已經初步的打通了前後端,下一章會說明怎麼讓請求(Request)送出時帶著 Token,還有 Token 該如何保存才好