ヘッダーの値によって変換処理を行い、DBに格納する
java - Reading HTTP headers in a Spring REST controller - Stack Overflow
RequestHeader (Spring Framework API) - Javadoc
@RequestHeader(name="Content-Type") String contentType
で、ヘッダーにアクセスしようとすると...
Content-Typeは:{"message":"テスト","roomId":"498894c2-a9e4-4b62-aaf0-4869e2f6b02d"}
なぜかメッセージの中身が現れた
MessageでのHeaderへのアクセスは下記。
Header (Spring Framework API) - Javadoc
Content-Typeは:application/json
※日記記事以外でまとめる予定
実装前に...
画像を送信すると下記エラーがでた。
org.springframework.messaging.simp.stomp.StompConversionException: STOMP 'content-length' header value 142026 exceeds configured buffer size limit 65536
25.4.15
WebSocketMessageBrokerConfigurer (Spring Framework API) - Javadoc
configureWebSocketTransport(WebSocketTransportRegistration registry)を使用して設定。2MB以下なら数秒で送受信できた。
続いて、画像はbyte型に変換し、BLOB型のテーブルに保存する。
その前に...Lineで桜の写真を送信したのだが、これが5.5MBであった。これをLine側で保存すると、あまり画質を損なわずに843KBになっていた。
実際に送った写真をこちらに送り、imageフォルダーに書き込む。
String path = "src/main/resources/static/image/test.jpeg";
File file = new File(path);
FileOutputStream outputStream = new FileOutputStream(file);
outputStream.write(imageData);
outputStream.close();
結果は、843,684 バイト。
image-compressionで指定した1MB以下はちゃんと適用されている。
Springboot+JPAでbyte配列をMYSQLに登録したい
詳細
Data truncation: Data too long for column 'image' at row 1
MySQL側を確認すると...tinyblobになっていた。
mysql> show columns from chat_message where Field='image';
+-------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| image | tinyblob | YES | | NULL | |
+-------+----------+------+-----+---------+-------+
1 row in set (0.00 sec)
DDLを生成する時に使用されるとあるので、DBをリセットして動かすとMediumBlobになった。
(ただのSELECTだけで見ようとすると大変な事になる...SQL勉強しないと...。)
フロントで受け取った画像を表示する
①SUBSCRIBEした画像を表示する
②DBから受け取った画像をメッセージの受け取り時刻と揃えて表示する
続いて、受信したmessageはデコードしてbyteで渡してあげる。
document.getElementById('contributor' + nowLow).value = chatLogs[i].id;
document.getElementById('MessageLog' + nowLow).value = chatLogs[i].message;
(かな~り前のコードだったから忘れていた...)
レスポンス自体には、メッセージと画像を別々に持たせているので、送る事さえできれば問題ない。
過去ログ取得と新規ログ取得をわける。
問題は、「URL毎の認可処理が完了しているユーザーにだけアクセス時に既読管理の時間を更新する」という実装。
既読管理の更新時間は①入室②退出orブラウザバックの2点のみ。
以上、今回の実装は、入室時点での既読管理更新をURL認可時点で行う
URL認可のところに追記で終わり(前作成した構成とつけていた変数のせいですごく冗長にせざるをえなかった...)
if(alreadyReadTimeRepository.findByAlreadyReadUserAndTargetRoom(participatingUser, room) != null) {
AlreadyReadTime alreadyReadTime = alreadyReadTimeRepository.findByAlreadyReadUserAndTargetRoom(participatingUser, room);
alreadyReadTime.setAlreadyReadTime(LocalDateTime.now());
alreadyReadTimeRepository.save(alreadyReadTime);
}else {
AlreadyReadTime alreadyReadTime = new AlreadyReadTime();
alreadyReadTime.setAlreadyReadTime(LocalDateTime.now());
alreadyReadTime.setAlreadyReadUser(participatingUser);
alreadyReadTime.setTargetRoom(room);
alreadyReadTimeRepository.save(alreadyReadTime);
}
次にブラウザバックだが、これは再入室で自動で更新されるから問題はない。
次に退出。SessionDisconnectEventでいいしょう。
25.4.12
RoomのURLはヘッダーで管理していたので、接続が切れた場合の処理ができない。
SessionDisconnectEvent時点でわかる情報は「セッションが切れたユーザーのAuthenticationToken」のみ。→ログイン状態を管理する実装等のついでに付けたい。現状は不要の判断で。
※次回以降
画像を表示する
続いて、メッセージの仕分けと通信サイズを調べ、異常があれば弾く実装をする。
-------------------------------------------------------------------------------------------
他のエラー
VM352:161 crbug/1173575, non-JS module files deprecated
+
Started ReaDiscussion20220605Application in 184.562 seconds (process running for 186.209)
何だこれ...
全てのブレークポイントを削除したら直りました。なんだこれ...
------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------
STOMP over WebSocketでのバリデーション
base64だと、使用される文字が限定されるので、バリデーションを行いたい。
しかし、公式のドキュメントでは、SUBSCRIBEはあるが、SENDに関わるイベントはない。(追記:その下にInterceptorについて記載がありました...)
BrokerやInboundChannelといったWebSocketで使用するクラスでは実装できない...?
そして、このSTOMP over WebSocketはSpring frameworkのMessageを使用して作られているとの事。
Spring Integration メッセージ - リファレンス
現在は、ChannelInterceptorとの事。
ChannelInterceptor (Spring Framework 5.2.16.RELEASE API) - Javadoc
まず、Messageが呼ばれる事だけ確認する。
GenericMessage (Spring Framework API) - Javadoc
となると、Interceptorの登録のみ。
WebSocketMessageBrokerConfigurer (Spring Framework API) - Javadoc
後はBeanで登録したChannelInterceptorの実現したクラスを加えて...
ChannelInterceptorのpostSend()を試すと...
postSend()
{simpMessageType=HEARTBEAT, simpUser=UsernamePasswordAuthenticationToken [Principal=org.springframework.security.core.userdetails.User [Username=秘密, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=], Credentials=[PROTECTED], Authenticated=true, Details=CustomWebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=5CEB1CB9B3702CC5FCC3BFB1056C232E], Granted Authorities=], simpSessionId=2ijplqoo}
[B@6343ff1e
ヘッダーがちゃんと取れました。(あ、Roleの登録処理が抜けていた...単体テストやり直し...)
あとはpayloadです。
payloadはbyte型で格納されています。
String str = new String*1;
System.out.println(str);
とすると、結果は...
{"content":"メッセージを送ります。"}
取得できました。
処理順はPreSend()→Controller→afterSendCompletion()。
PreSend()で行う予定
・受け取ったMessageのheaderからContentType等を調べる
・payloadに不正な入力がないか
Injection Flaws | OWASP Foundation
OWASP Secure Coding Practices-Quick Reference Guide | OWASP Foundation
A04 安全が確認されない不安な設計 - OWASP Top 10:2021
HTTP Request Smuggling を理解する - Qiita
・DOS
Denial of Service | OWASP Foundation
コンテントタイプなどをホワイトリスト式に
バリデーション
bucket4jなどによるリクエスト等の制限
-------------------------------------------------------------------------------------------------
HashSetは2分岐構造なので、データの削除だと速い。
Listは配列なので、データを消す時に時間がかかるケースもある。
--------------------------------------------------------------------------------------------------
感想
この記事はかなり前のものだったと思います。
他の記事を投稿しつつ、消さずに追記追記で今日にいたりました。
所詮日記です。技術者への道は遠い...
ちなみに、AOPや例外処理の記事も書いていましたが、没になりました。
さて、一旦仕切り直してより良いアウトプットができるように頑張ります。
また、ムエタイ始めました。もう夏から試合参加してもいいかもと言って頂いたので、まだ確定ではないですが、Bクラスで参加想定して頑張ります。
あと、MMAも始める予定です。
理想の一週間としては、平日が仕事終わりにムエタイとMMA、帰宅後はよりコンピューターサイエンスに近いJavaの学習と周辺知識の学習。土日が基礎トレと開発、その他。
心に余裕がなくなってきて、少し気持ちめげかけましたが、社内の方々や交流会の方々とお話していく中で、初心を思い出しました。松下幸之助の道の考え方に近いです。別の方の言葉だったと思いますが、一言で言うと「人事を尽くして天命を待つ」。この精神で生きていきたいと思います。
スピード落ちましたが上げていきます。
*1:byte[])message.getPayload(