Flaskでラズパイの状態を確認するWebアプリを作ってみた（其の四）

### 今回導入した機能

+ ~~テンプレート継承の導入~~ (其の三で紹介済み)
+ ~~メモリ・CPU・IOの状態を確認できるように free と vmstat コマンドを追加~~ (其の三で紹介済み)
+ ユーザーログイン認証
+ SQLite3でのユーザーデータ管理

### ファイル構成
```
RaspiApp 
  ├─ static
  |    ├─ css
  |    |   └─ style.css
  |    ├─ db
  |    |   └─ users.sqlite
  |    └─ images
  |        └─ raspi-logo.png
  ├─ templates
  |      ├─ base.html
  |      ├─ get_disk.html
  |      ├─ get_ip_addr.html
  |      ├─ get_mem.html
  |      ├─ get_route.html
  |      ├─ get_socket.html
  |      ├─ get_temp.html
  |      ├─ get_vmstat.html
  |      ├─ index.html
  |      └─ login.html
  └─ raspi_app.py
```

### GitHubリポジトリ
#### [GitHub : CannuWorks/RaspiApp](https://github.com/CannuWorks/RaspiApp.git "RaspiApp")

## ユーザーログイン認証

+ __Flask-login__ モジュールを利用しないで実装
+ sessionで管理する

![login_auth.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699739/dde85593-245b-d110-eec5-a5b0281daf15.png)

### 認証フロー
![auth_flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699739/296a6d99-3e26-7601-720b-a75775aac1ec.png)

::: note info 
#### 処理プロセス

0. indexでsession[login] =Trueであるかを確認し、Falseであればloginにリダイレクト
0. DBに保管されているpasswordは暗号化（ハッシュ化）されているので、入力されたpasswordもget_digest()メソッドで変換して照合する必要がある
2. idとpasswordを照合し、正しければsession[login] = Tureとして、Boolen値のTrueをセッションにセットする
3. Trueの場合はindex、Falseの場合はloginにリダイレクトされる
4. 認証が成功するとindex.html(トップページ/メニューページ）が表示される
5. 各種コマンドリンクにアクセスすると、sessionsession[login] =Trueであるかを確認し、Trueであればget_xxx()メソッドを実行するが、Falseの場合、loginにリダイレクトされる。

#### ポイント
+ 2つの条件判断
    + id & passwordでユーザーを照合
    + 照合に成功した場合 session[login] = True をセッションにセットする。以降の処理でそのセッションの状態を常に確認し、認証が成功していないユーザは、エンドポイントに定義したコマンドを実行できない。

:::

### コード
``` python
app.secret_key = os.urandom(21) 

# Password Digest
def get_digest(password):
    pwd = bytes(password, 'utf-8')
    diget = hashlib.sha256(pwd).hexdigest()
    return diget


# TopPage
@app.route('/')
def index():
    if not session.get('login'):
        return redirect(url_for('login'))
    else:
        return render_template('index.html')


# Login
@app.route('/login')
def login():
    return render_template('login.html')


# lockin check
@app.route('/logincheck', methods=['POST'])
def logincheck():
    user_id = request.form['user_id']
    pwd = request.form['password']
    session['username'] = user_id

    # make pwd hashed by "get_digest()"" function
    password = get_digest(pwd)
    id_pwd_hash = get_user_data()

    if user_id in id_pwd_hash:
        if password == id_pwd_hash[user_id]:
            session['login'] = True
        else:
            session['login'] = False
    else:
        session['login'] = False

    if session['login']:
        return redirect(url_for('index'))
    else:
        return redirect(url_for('login'))


# logout
@app.route('/logout')
def logout():
    session.pop('login', None)
    return redirect(url_for('index'))
```

``` login.html
<!-- main block のみ記載 -->
{% block main %}

<h2>Login Authentication</h2>
<h3>Please input User ID and Password!</h3>
<form method="POST" action="/logincheck">
    <p><input placeholder="User ID" type="text" name="user_id"></p>
    <p><input placeholder="Password" type="password" name="password"></p>
    <p><input type="submit" value="SEND"></p>
</form>

{% endblock %}
```


### SQLite3でのユーザーデータ管理

+ データベースを作成　（ファイル名：users.sqlite）
+ テーブルを作成（テーブル名：users）

| id | name | password | email |
|:-:|:-:|:-:|:-:|
| 1 | use_name |  user_pwd |  user@ydomain.com |

```python
import sqlite3

# DB connection
def get_user_data():
    sqlite_path = 'static/db/users.sqlite'
    connection = sqlite3.connect(sqlite_path)
    cursor = connection.cursor()
    cursor.execute('SELECT name, password FROM users')
    id_pwd_hash = dict(cursor.fetchall())
    connection.close()
    return id_pwd_hash
```



::: note info
### ポイント
1. ファイルの場所を示すパスを作成
2. connect()メソッドで、データベースに接続
3. カーソルを取得する
4. SQL文を実行する
5.　取得した __name__ と __password__ を辞書形式で、変数id_pwd_hashにセットする
6. データベースとのコネクションを切断
7. id_pwd_hash変数を返す
:::

## まとめ
+ 開発環境での実装は一旦終了
+ リファクタリング的活動を実施
    + Flask-loginによる認証
    + BluePrintによる機能分割
    + SqlalchemyでのDB接続
+ nginx + uWSGIによる、Production環境のセットアップ（其の五で紹介）