🦔

actix-identityの保存先をCookieから変更する

2021/02/16に公開

RustのWeb Frameworkのactix-webでユーザー認証の機能を入れようと考えると、最初に出てくるのがactix-identityだと思います。

サンプルですとCookieIdentityPolicyを使いCookieに認証情報を出力します。
個人的には署名されている(されているのかな?)とは言え、そういった情報をCookieに書き出すのはあまり気持ちがよろしくないレガシーな人なので、これをSessionに書き出したいと思います。

方針

CookieではなくSession情報に認証情報を出力するようにする。
通常actix-sessionを使用した場合、Cookieにセッション情報を出力するため、解決策にはなりません。

ただし、actix-redisを使うことで、セッションのバックエンドにRedisを使うことができます。
actix-redisを使う想定で、sessionに認証情報を出力すれば、認証情報がCookieに露出することはないかなと思っています。

ソースコード

CookieIdentityPolicyの代わりに、SessionIdentityPolicyを作り、それを使用するようにしました。

/*
  SessionIdentityPolicy部分
*/
use std::rc::Rc;
use actix_identity::IdentityPolicy;
use actix_session::UserSession;
use actix_web::Error;
use futures_util::future::{ready, Ready};

struct SessionIdentityInner {
    key: String,
}

pub struct SessionIdentiyPolicy(Rc<SessionIdentityInner>);

impl SessionIdentiyPolicy {
    pub fn new() -> SessionIdentiyPolicy {
        SessionIdentiyPolicy(Rc::new(SessionIdentityInner { key: "Identity".to_string()}))
    }

    pub fn key(mut self, value: &str) -> SessionIdentiyPolicy {
        Rc::get_mut(&mut self.0).unwrap().key = value.into();
        self
    }
}

impl IdentityPolicy for SessionIdentiyPolicy {
    type Future = Ready<Result<Option<String>, Error>>;

    type ResponseFuture = Ready<Result<(), Error>>;

    fn from_request(&self, req: &mut actix_web::dev::ServiceRequest) -> Self::Future {
        let session = req.get_session();
        let key = &self.0.key;

        ready(session.get::<String>(key).or(Ok(None)))
    }

    fn to_response<B>(
        &self,
        identity: Option<String>,
        changed: bool,
        response: &mut actix_web::dev::ServiceResponse<B>,
    ) -> Self::ResponseFuture {
        let session = response.request().get_session();
        let key = &self.0.key;

        let ret = if changed {
            session.set(key, identity)
        } else {
            Ok(())
        };

        ready(ret)
    }
}

あらかじめRedisSessionを設定しておき、作成したSessionIdentityPolicyを使うようにします。
なお、RedisSessionとの順番が重要でRedisSession側を上に書くと動作しません。

/*
middleware 設定部分(例)
*/
#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(move || {
        App::new()
            .wrap(IdentityService::new(
                SessionIdentiyPolicy::new()
            ))
            .wrap(RedisSession::new(
                &CONFIG.session_server,
                CONFIG.secret_key.as_bytes(),
            ))
	// 各種設定(省略)
    })
    .bind_openssl("0.0.0.0:".to_owned() + &(CONFIG.port.to_string()), builder)?
    .run()
    .await
}

最後に

Rust初心者なんでもっといい方法があるとか、書き方があるとかありましたらご指摘等頂けると非常にうれしいです。

Discussion