iT邦幫忙

2023 iThome 鐵人賽

DAY 24
0
Software Development

Rust Web API 從零開始系列 第 24

Day24 - 權限驗證(2) - Login API

  • 分享至 

  • xImage
  •  

把昨天包裝好的JWTHandler加入AppState後,就要來完成Login API了,首先我們要建立一張新的表用來放管理員的帳號密碼:

    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        manager
            .create_table(
                Table::create()
                    .table(Users::Table)
                    .if_not_exists()
                    .col(ColumnDef::new(Users::Id).uuid().not_null().primary_key())
                    .col(
                        ColumnDef::new(Users::UserName)
                            .string()
                            .not_null()
                            .unique_key(),
                    )
                    .col(ColumnDef::new(Users::Password).string())
                    .col(
                        ColumnDef::new(Users::CreatedAt)
                            .timestamp_with_time_zone()
                            .not_null(),
                    )
                    .to_owned(),
            )
            .await
    }

接下來就開始完成handler,首先要用user_name找到管理員的資訊:

pub async fn login(
    state: State<AppState>,
    Json(user): Json<User>,
) -> Result<StatusCode, StatusCode> {
    let user_model = users::Entity::find()
        .filter(users::Column::UserName.eq(user.user_name))
        .one(&state.database)
        .await?
        .ok_or(StatusCode::NOT_FOUND)?;
        
    todo!();
}

接下來要進行密碼驗證,為了安全起見我們通常不將密碼以明碼的方式存在資料庫中,而是使用雜湊過後的密碼進行比對,先安裝套件:

cargo add bcrypt

使用上也十分簡單,只要使用verify方法即可:

pub async fn login(
    state: State<AppState>,
    Json(user): Json<User>,
) -> Result<StatusCode, StatusCode> {
    let user_model = users::Entity::find()
        .filter(users::Column::UserName.eq(user.user_name))
        .one(&state.database)
        .await
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
        .ok_or(StatusCode::NOT_FOUND)?;
        
    let valid = verify(user.password, &user_model.password.unwrap())
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
        
    todo!();
}

valid是一個bool,表示是否驗證成功,如果驗證成功的話,我們就要產生token並透過cookie的方式傳到client端:

pub async fn login(
    state: State<AppState>,
    Json(user): Json<User>,
) -> Result<(StatusCode, HeaderMap), StatusCode> {
    let user_model = users::Entity::find()
        .filter(users::Column::UserName.eq(user.user_name))
        .one(&state.database)
        .await
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
        .ok_or(StatusCode::NOT_FOUND)?;
        
    let valid = verify(user.password, &user_model.password.unwrap())
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
        
    if valid {
        let token = state
            .jwt_handler
            .clone()
            .create_token(&user_model.user_name);

        let cookie = Cookie::build("token", token.to_owned())
            .http_only(true)
            .finish();

        let mut headers = HeaderMap::new();
        headers.insert(SET_COOKIE, cookie.to_string().parse().unwrap());

        Ok((StatusCode::OK, headers))
    } else {
        Err(StatusCode::BAD_REQUEST)
    }
}

可以用AppState的方式傳遞jwt_handler,生成token後再用Axum充提供的Cookie Builder來產生Cookie的內容,最後再Axum中如果要傳遞Cookie的話,則是要將內容以HeaderMap作為Handler的回傳值,所以最後需要將這個Handler的回傳值改成Result<(StatusCode, HeaderMap), StatusCode>
最後再把這隻API加入路由即可:

//// application.rs
pub fn build() -> Router {
    let database = get_database(&config.database).await.unwrap();
    let email_client = get_email_client(&config.email_client);
    let jwt_handler = get_jwt_handler(&config.jwt_handler);
        
    let app = Router::new()
        .route("/", get(health_check))
        .route("/subscriptions", post(subscribe))
        .route("/subscriptions/confirm/:token", get(confirm))
        .route("/login", post(login))
        .merge(SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", ApiDoc::openapi()))
        .with_state(AppState {
            database,
            email_client,
            jwt_handler,
        });;
    app
}

上一篇
Day23 - 權限驗證(1) - JWT Handler
下一篇
Day25 - 權限驗證(3) - 自己寫個middleware吧
系列文
Rust Web API 從零開始30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言