- 投稿日:2020-05-29T22:59:48+09:00
Javaのメリット・デメリット
はじめに
JavaというAndroidやゲームに使われたりする素晴らしい言語のメリット・デメリットをまとめました。
※これはJavaについての記事です。JavaScriptは全く別の言語であるので注意してください。
メリット
オブジェクト指向
オブジェクト指向とは、プログラムの可読性をあげるものです。リストに追加するにはaddメソッド(処理のまとまり)を呼び出すという直感的なプログラミングができます。Javaにはクラスなどでオブジェクト指向のプログラミングができます。
リストにアイテムを追加するlist.add(item);型
型とは変数の値のタイプ(文字列、数値など)のことです。例えば変数numに数値が入っていると仮定して作られたプログラムがあるとします。しかしそのプログラムを使用する際に間違えて文字列を渡してしまうと実行時に数値でないためエラーがでます。Javaならコンパイルという文法チェックで型が違うと実行できません。面倒かと思いますが、実行前に気づけることはメリットです。
型を使った例int num;// 数値(Integer)しか入れることができない。また変数は宣言しないと使えない num = "文字列";// "で囲むと文字列を使える。また整数でないのでエラーを出す int num2; num2 = num + 1;// 整数でないと計算できない一つのコードで複数のOSで動かせる
OSに処理を依存することなくプログラミングができます。
デメリット
記述が長い
記述が長く、少し読みにくいです。学習の場合はjshell(Javaのツール)をつかうことで簡単に学習できると思います。以下は画面にHello!と出力するプログラムです。jsellを実行する場合はターミナルで
jshell
と入力して開始できます。ファイルに書いた場合public class Main { public static void main(String[] args) { System.out.println("Hello!"); } }jshellの場合System.out.println("Hello, World");環境構築が大変
windowsの場合、JavaをインストールするにはOracleのホームページでダウンロードし、インストーラーをし、環境変数に追加しないといけません。初心者には大変な作業です。Ubuntuだと
sudo apt install openjdk-jdk-11-headless
で完了できるのでおすすめです。おわりに
Javaだけに邪魔な言語ではなく、習得することでオブジェクト指向という本質を理解し、プログラミングが簡単だ楽しいことだと気づけるはずです。すでに他の言語を学んでいても試してみてください。
- 投稿日:2020-05-29T17:32:16+09:00
Java14×Spring Boot2.3×JUnit5でHelloWorldしたよ〜
はじめに
こんにちは!macでSTSの環境構築を行った者です。
今回は「HelloWorldファイルとHelloWorldのテストコードファイルを作成し、jarに保存してmacで実行してみる」ところまで実施しました。
前回同様、Spring徹底入門を参考に進めました。簡単な手順ですが、バージョンのせいかJUnitで実行する辺りやjarにする辺りで思いの外詰まったので記事にしました。
ぜひ俺の屍を越えていってください。使用環境とバージョン
- macOS Catalina
- jdk14.0.1
- JUnit5
- Maven 3.6.3_1
- STS 4.6.1
- Spring Boot 2.3
Spring Initializrでプロジェクトの雛形を作る
まずSpring Initializrでプロジェクトの雛形を作成します。
Spring InitializrはSpring Bootが提供しているWebサービスです。必要事項をチェックするだけで、Spring Bootプロジェクト作成の基礎となるディレクトリがzip形式でダウンロードできます。また、メインメソッドの雛形があるDemoApplication.javaと、テストコードの雛形があるDemoApplicationTests.javaが、あらかじめ適切なディレクトリ配下に配置されています。つまり、ダウンロードするだけで今回の作成物の8割は完成します笑
前回Mavenで環境構築を行ったので、ProjectはMavenを選びます。
その他、言語やSpringBootのバージョン、Javaのバージョンなど、自分に合ったものを選択し、GENERATEボタンをクリックすると、demo.zipのダウンロードが始まります。demo.zipを解凍すると、以下のようなMavenプロジェクトが既に作成されています。すごい!めちゃめちゃ簡単じゃないですか!!
解凍したdemoファイルは、環境構築した際に作成したworkspaceディレクトリ配下に移動させておくと進めやすいです。
STSを開き、File > Import > Existing Maven Projectsで、先ほど解凍したdemoフォルダを選択すれば、インポートを行えます。自動的にビルドが走るので、Package Explorerが以下画像のようにパッケージ表示になるまでしばし待ちます。自動で以下のようにならなかったら、メニューバーからProject > Cleanを押下してください。
DemoApplication.javaの編集
ダウンロードしたばかりのDemoApplication.javaの中身は以下のようになっているはずです。
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }SpringはMVCモデルのフレームワークです。HelloWorldプロジェクトは最低限の実装で良いので、クライアントからのリクエストを受け取ってレスポンスを返す1、Controller部分のみを実装していきたいと思います。
■DemoApplicationクラスに
@Controller
をつける言わずもがなですが、
@Controller
アノテーションをつければ、そのクラスをコントローラーとすることができます。■
@RequestMapping
、@ResponseBody
を付与したメソッドhelloWorld()を作成する
@RequestMapping
と@ResponseBody
、二つのアノテーションを付与することで、戻り値をレスポンスのコンテンツとすることができます。
参考:Spring MVCのコントローラでの戻り値いろいろpackage com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @SpringBootApplication @Controller public class DemoApplication { @RequestMapping("/") @ResponseBody String helloWorld() { return "Hello World!"; } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }保存して、DemoApplicationを右クリック > Run As > Java Applicationと選択してメインメソッドを実行すると、http://localhost:8080 でハロワできるはずです!やったね!
上記以外の書き方でも、コントローラーのアノテーションを
@Controller
から@RestController
に変更すれば、helloWorld()のアノテーションは@RequestMapping
のみで動きます。ただし、アノテーションの組み合わせを間違えるとエラーが発生します(やらかしました)。どうやらHTTPリクエストがアプリケーション側で受け取れていないとこの画面になるようで、DemoApplication.javaをダウンロードしたまんまで実行しても同じ画面になるそうです。
DemoApplicationTests.javaの編集
DemoApplication.javaの実装に合わせて、テストコードも編集していきます。以下がダウンロードしたまんまのテストコードです。これに手を加えていきます。
package com.example.demo; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class DemoApplicationTests { @Test void contextLoads() { } }■
@SpringApplicationConfigration
、@WebIntegrationTest
の代わりに@SpringBootTest
を付与する前者2つのアノテーションはSpring Boot 1.4から非推奨になったため、
@SpringBootTest
に置き換えます。
@SpringBootTest
は@SpringBootApplication
がついているクラスをテスト用のコンフィグレーションクラスとして認識します。■
@RunWith(SpringRunner.class)
の代わりに@ExtendWith(SpringExtension.class)
を使用するSpringRunnerはJUnit4のものなので、JUnit5で使用するとその後のjar作成時にビルドがうまく通らなくなりました…
JUnit5では@RunWith
アノテーション自体が@ExtendWith
に置き換えられていたので、そちらに変更しました。JUnit5 @RunWith■
@LocalServerPort
でポート番号を取得するSpring Boot 1.3以前では
@Value(“${local.server.port}”)
で取得していたポート番号ですが、1.4以降@LocalServerPort
がショートカットとして追加されています。2.3だと@Value
でポート番号を取得しようとすると以下のエラーが発生して、テスト実行ができませんでした。。org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.example.demo.DemoApplicationTests': Unsatisfied dependency expressed through field 'port'; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'int'; nested exception is java.lang.NumberFormatException: For input string: "${local.server.port"■assertThatメソッドはorg.junit.Assert.assertThatではなくorg.hamcrest.MatcherAssert.assertThatを使用する
JUnitのassertThatは非推奨と出て、deprecatedを許容するアノテーション
@SuppressWarnings(“deprecated”)
を付与してもうまくjarのビルドができませんでした…(上記以外が原因かもです)
代わりに、hamcrestのassertThatを使用しました。最終的なテストコードは以下のようになりました。
package com.example.demo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.web.server.LocalServerPort; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment=SpringBootTest.WebEnvironment.RANDOM_PORT) public class DemoApplicationTests { TestRestTemplate restTemplate = new TestRestTemplate(); @LocalServerPort int port; @Test public void testHello() { assertThat(restTemplate.getForObject("http://localhost:" + port, String.class), is("Hello World!")); } }DemoApplicationTests.javaを右クリック > Run As > JUnit Testでテストコードが実行できます。JUnitパネルに以下のように表示されれば、テスト成功です!
上記テストコードかDemoApplication.javaのどちらかの"Hello World!"部分を別の言葉に変えて、ちゃんとテストが失敗するかも確認してみてください。
実行可能jarを作成する
ここまでの工程で、既にSpring プロジェクトのHelloWorldは出来上がっています。しかし、実際にjarにしてみると、コンパイルやビルドがうまくいかないことに気づけるので、やっておくと吉です!
jarを作成する手順を説明していきます。
①macのターミナルから、ワークスペース配下のdemoディレクトリまで移動する
cd ~/Users/xxx/workspace/demo②実行可能jarを作成するMavenコマンドを実行する
./mvnw clean packageSTS上ではHelloWorldもテストもうまく行っていたとしても、ここでエラーが出ることもあります(自分です)
自分はここで@RunWith
が使えないことを知りました。うまくいった場合は、下図のように
BUILD SUCCESS
が表示されます。③jarを実行する
無事にビルドが成功したら、demo/target 配下に「demo-0.0.1-SNAPSHOT.jar」ができているはずです。末尾にoriginalとついていない方を実行します。
java -jar /Users/xxx/workspace/demo/target/demo-0.0.1-SNAPSHOT.jar以下のようにSpringが立ち上がれば成功です! http://localhost:8080 で同じようにHello World!が表示できます。
おわりに
冒頭に記した通り、Spring徹底入門を読みながら実施しましたが、ただのHelloWorldを作る際に、同じJavaやIDEでも、バージョンが違うだけでこんなに違うんだなと驚きました…バージョン大事と言われる理由が分かりました…
せっかく雛形を作成したので、次回は簡単にアプリを作成して、テストコードも書いてみたいと思います!お読みいただきありがとうございました!
ここ違うかもよ〜、という箇所ありましたら、例の如く、そ〜っと教えてください…!
Spring MVCの細かな説明は省きますが、正確にはコントローラーの前にあるフロントコントローラーというサーブレットが、リクエストを受け取ってレスポンスを返しています。ここはフレームワークに任せるため、開発者はコントローラーのみ実装します。 ↩
- 投稿日:2020-05-29T17:07:32+09:00
Minecraft サーバを AWS で作る
Minecraft のサーバを作る。いろんな方が既に記事を書いているので目新しさはない。この組み合わせで動作したという確認。
Minecraft サーバとは
Minecraft はネットワークで通信してマルチプレイができる。マルチプレイをするにはネットワーク内でMinecraftをプレイしているマシンを1台公開してそこに他のプレーヤがつなげるやりかたと、ネットワーク内でサーバ専用サービスを実行するホストを作りサーバにするやりかたの2つがある。
Minecraft でマルチプレイをするには Minecraft の系統と、バージョンを合わせる必要がある。
Minecraft の系統
マルチプレイをするときには、系統を合わせないといけない。今の主な系統は2つ
- Java 版
- 統合版 (Bedrock Edition)
系統は、プレイする全員、および、サーバその系統に合わせる必要がある。
なお、現在公開されているサーバのソフトウェアは Java 版系統。
統合版のサーバも公開されている( https://help.minecraft.net/hc/en-us/articles/360035131651-Dedicated-Servers-for-Minecraft-on-Bedrock- ) が、アルファ版。ちなみに、上記以外の系統も存在する。サーバソフトウェアは公開されていないが、ピア2ピア的にマルチプレイができる。その場合はそれぞれの系統同士でしかマルチプレイできない。
- ブラウザ版
- Raspberry Pi edition
- Earth edition
- Education edition
- Legacy console editon (現在、PS4,Nintendo Switch等は統合版にアップデート、それ以外の旧いプラットフォームではLegacy conosole edition 同士でも異機種だとマルチプレイできないぽい)
New Nintendo 3DS Edition
他に Minecraft China というのがあるがナンダ? https://minecraft-ja.gamepedia.com/Minecraft_China
バージョン
サーバのバージョンと、マルチプレイの参加者の Minecraft のバージョンを合わせる必要がある。
バージョンはJava版において、2020/5/29現在最新のものは以下の2つ。
- 1.15.2 (正式版)
- 20w21a (開発版1.16のスナップショット)
であるが、こちらからダウンロード
https://www.minecraft.net/ja-jp/download/server/
できるサーバのバージョンは正式版の最新のものである 1.15.2。
それ以外の old バージョンや、開発版はこちらから。
サーバの動作環境
java で動作するので Windows / Mac / Linux で動作する。もちろん、Raspsberry Piも!
サーバを動かすのは難しい?
https://minecraft.gamepedia.com/Tutorials/Setting_up_a_server
によると、Setting up a server takes some time, and some technical knowledge. Don't try to set up a server unless you have some basic computer and networking abilities.
とあるけれども、試してみたらあっさり動いた。
AWS サーバを起動
まずは TOKYOリージョン、t2.midium を利用して、Ubuntu 20.04を起動する。
ログイン後、
$ sudo apt update $ sudo apt upgradeをしたあと、Java だけインストール。
とりあえずUbuntu20.04で最新のJREを入れてみた。$ sudo apt install openjdk-14-jrehttps://www.minecraft.net/ja-jp/download/server/
からサーバをダウンロード。リンクをマウスで右クリックしてアドレスを取得、ssh で以下に貼り付けた。
$ wget https://launcher.mojang.com/v1/objects/bb2b6b1aefcd70dfd1892149ac3a215f6c636b07/server.jar起動してみる
$ java -Xmx1024M -Xms1024M -jar server.jar nogui [08:18:47] [main/ERROR]: Failed to load properties from file: server.properties [08:18:47] [main/WARN]: Failed to load eula.txt [08:18:47] [main/INFO]: You need to agree to the EULA in order to run the server. Go to eula.txt for more info.という結果になった。
$ ls -alhとしてみたら、
total 35M drwxr-xr-x 5 ubuntu ubuntu 4.0K May 28 08:18 . drwxr-xr-x 3 root root 4.0K May 28 07:51 .. -rw-r--r-- 1 ubuntu ubuntu 220 Feb 25 12:03 .bash_logout -rw-r--r-- 1 ubuntu ubuntu 3.7K Feb 25 12:03 .bashrc drwx------ 2 ubuntu ubuntu 4.0K May 28 07:56 .cache -rw-r--r-- 1 ubuntu ubuntu 807 Feb 25 12:03 .profile drwx------ 2 ubuntu ubuntu 4.0K May 28 07:51 .ssh -rw-r--r-- 1 ubuntu ubuntu 0 May 28 08:01 .sudo_as_admin_successful -rw-rw-r-- 1 ubuntu ubuntu 181 May 28 08:18 eula.txt drwxrwxr-x 2 ubuntu ubuntu 4.0K May 28 08:18 logs -rw-rw-r-- 1 ubuntu ubuntu 35M Jan 17 10:06 server.jar -rw-rw-r-- 1 ubuntu ubuntu 940 May 28 08:18 server.propertiesとあって、eula.txt ができているようだ。
$ vim eula.txtとして、中身の
#By changing the setting below to TRUE you are indicating your agreement to our EULA (https://account.mojang.com/documents/minecraft_eula). #Fri May 29 19:21:06 JST 2020 eula=falsefalse を true に書き換えて保存。
もう一度起動
$ java -Xmx1024M -Xms1024M -jar server.jar nogui [08:20:14] [main/WARN]: Ambiguity between arguments [teleport, destination] and [teleport, targets] with inputs: [Player, 0123, @e, dd12be42-52a9-4a91-a8a1-11c01849e498] [08:20:14] [main/WARN]: Ambiguity between arguments [teleport, location] and [teleport, destination] with inputs: [0.1 -0.5 .9, 0 0 0] [08:20:14] [main/WARN]: Ambiguity between arguments [teleport, location] and [teleport, targets] with inputs: [0.1 -0.5 .9, 0 0 0] [08:20:14] [main/WARN]: Ambiguity between arguments [teleport, targets] and [teleport, destination] with inputs: [Player, 0123, dd12be42-52a9-4a91-a8a1-11c01849e498] [08:20:14] [main/WARN]: Ambiguity between arguments [teleport, targets, location] and [teleport, targets, destination] with inputs: [0.1 -0.5 .9, 0 0 0] [08:20:14] [Server thread/INFO]: Starting minecraft server version 1.15.2 [08:20:14] [Server thread/INFO]: Loading properties [08:20:14] [Server thread/INFO]: Default game type: SURVIVAL [08:20:14] [Server thread/INFO]: Generating keypair [08:20:15] [Server thread/INFO]: Starting Minecraft server on *:25565 [08:20:15] [Server thread/INFO]: Using epoll channel type [08:20:15] [Server thread/INFO]: Preparing level "world" [08:20:15] [Server thread/INFO]: Found new data pack vanilla, loading it automatically [08:20:15] [Server thread/INFO]: Reloading ResourceManager: Default [08:20:47] [Server thread/INFO]: Loaded 6 recipes [08:20:47] [Server thread/INFO]: Loaded 825 advancements [08:20:53] [Server thread/INFO]: Preparing start region for dimension minecraft:overworld [08:20:53] [Server-Worker-1/INFO]: Preparing spawn area: 0% [08:20:53] [Server-Worker-1/INFO]: Preparing spawn area: 0% [08:20:54] [Server-Worker-1/INFO]: Preparing spawn area: 0% [08:20:54] [Server-Worker-1/INFO]: Preparing spawn area: 0% [08:20:55] [Server-Worker-1/INFO]: Preparing spawn area: 1% [08:20:55] [Server-Worker-1/INFO]: Preparing spawn area: 2% [08:20:56] [Server-Worker-1/INFO]: Preparing spawn area: 4% [08:20:56] [Server-Worker-1/INFO]: Preparing spawn area: 5% [08:20:57] [Server-Worker-1/INFO]: Preparing spawn area: 7% [08:20:57] [Server-Worker-1/INFO]: Preparing spawn area: 10% [08:20:58] [Server-Worker-1/INFO]: Preparing spawn area: 12% [08:20:58] [Server-Worker-1/INFO]: Preparing spawn area: 14% [08:20:59] [Server-Worker-1/INFO]: Preparing spawn area: 17% [08:20:59] [Server-Worker-1/INFO]: Preparing spawn area: 19% [08:21:00] [Server-Worker-1/INFO]: Preparing spawn area: 22% [08:21:00] [Server-Worker-1/INFO]: Preparing spawn area: 24% [08:21:01] [Server-Worker-1/INFO]: Preparing spawn area: 26% [08:21:01] [Server-Worker-1/INFO]: Preparing spawn area: 29% [08:21:02] [Server-Worker-1/INFO]: Preparing spawn area: 32% [08:21:02] [Server-Worker-1/INFO]: Preparing spawn area: 33% [08:21:03] [Server-Worker-1/INFO]: Preparing spawn area: 36% [08:21:03] [Server-Worker-1/INFO]: Preparing spawn area: 38% [08:21:04] [Server-Worker-1/INFO]: Preparing spawn area: 41% . . .起動したようだ。
ポート公開
TCP25565 と UDP25565 を開放したら、外部から接続し、プレイできた。
- 投稿日:2020-05-29T15:48:41+09:00
PDFテキストコンテンツを抽出するJava
日常の作業では、巨大なPDFドキュメントに含まれているテキストコンテンツを抽出する必要がある場合があります。そして、Free Spire.PDF for Javaは、便利で高速なテキスト抽出方法を提供します、次に、プロセスで使用されるJavaコードを紹介します。
基本的な手順:
1. Free Spire.PDF for Javaパッケージをダウンロードして解凍します。
2. libフォルダーのSpire.Pdf.jarパッケージを依存関係としてJavaアプリケーションにインポートするか、MavenリポジトリーからJARパッケージをインストールします(pom.xmlファイルを構成するコードについては、以下を参照してください)。
3. Javaアプリケーションで、新しいJava Class(ここではExtractTextという名前)を作成し、対応するJavaコードを入力して実行します。pom.xmlファイルを構成します:
<repositories> <repository> <id>com.e-iceblue</id> <name>e-iceblue</name> <url>http://repo.e-iceblue.com/nexus/content/groups/public/</url> </repository> </repositories> <dependencies> <dependency> <groupId>e-iceblue</groupId> <artifactId>spire.pdf.free</artifactId> <version>2.6.3</version> </dependency> </dependencies>Javaコード:
import com.spire.pdf.PdfDocument; import com.spire.pdf.PdfPageBase; import java.io.*; public class ExtractText { public static void main(String[] args) { //PdfDocumentインスタンスを作成する PdfDocument doc = new PdfDocument(); //PDFファイルをロード doc.loadFromFile("雪.pdf"); //StringBuilderインスタンスを作成する StringBuilder sb = new StringBuilder(); PdfPageBase page; //PDFページをトラバースし、各ページのテキストを取得して、StringBuilderオブジェクトに追加します for(int i= 0;i<doc.getPages().getCount();i++){ page = doc.getPages().get(i); sb.append(page.extractText(true)); } FileWriter writer; try { //StringBuilderオブジェクトのテキストをテキストファイルに書き込みます writer = new FileWriter("ExtractText.txt"); writer.write(sb.toString()); writer.flush(); } catch (IOException e) { e.printStackTrace(); } doc.close(); } }
- 投稿日:2020-05-29T15:36:44+09:00
Angular+Rest(Java)でCORS対応
Angularクライアントからドメインの異なるRestサーバー(Java)へアクセスしようとするとCORS問題によってエラーとなります。
その対策を以下に記述します。
(サーバー側のRest API処理は通常と変わらないため省略します)Angular側のサーバーへリクエストする処理
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root', }) export class ClientService { url = "http://XXXXX:XXXX/XXXXX"; // アクセス先 returnModel : ReturnModel; // サーバーからの戻り値 // コンストラクタ constructor(private http: HttpClient) {} // 送信メソッド request(): void { // Httpヘッダー(CORS対応でAccess-Control-Allow-Originを指定する) const httpOptions = { headers: new HttpHeaders({ 'Access-Control-Allow-Origin': '*', }) }; // 送信データ const data = new HttpParam({ fromObject: { value: '1000" } }); // サーバーへPOST this.http.post<ReturnModel>(this.url , data, httpOptions).subscribe(h => this.returnModel = h); } }サーバー側のServlet Filter
package sample.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletResponse; // サーブレットフィルタ @WebFilter(filterName = "CorsFilter", urlPatterns = { "/*" }) public class CorsFilter implements Filter { @Override public void init(FilterConfig config) { } // フィルター処理 @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (response instanceof HttpServletResponse) { // 以下の通りCORS対応のためHeaderwo指定する HttpServletResponse http = (HttpServletResponse) response; http.addHeader("Access-Control-Allow-Origin", "*"); http.addHeader("Access-Control-Allow-Headers", "*"); http.addHeader("Access-Control-Allow-Credentials", "true"); http.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD"); } chain.doFilter(request, response); } @Override public void destroy() { } }
- 投稿日:2020-05-29T14:42:45+09:00
eclipseショートカット表
はじめに
OSはWindows10
基本はsts4で動作確認していますが、EclipseやPleiadesでも動くはず。
ショートカットは調べればいくらでも出てくるけど、個人的にパッと見られるいい感じのサイトがなかったので雑に表を作っただけ。
確証はないけどmacOSならCtrlをcommand、Altをoptionに換えればほとんどは動くはず。そもそも自分が使ってよかったものを載せているけど、個人的に特に使い勝手が良かったもの、使用頻度が高いものは★つけたので参考になれば嬉しいです。
エディタ内
コピペと削除
ショートカット キー 補足 全選択 Ctrl + A コピー Ctrl + C 任意の箇所を選択 ペースト Ctrl + V 任意の箇所を選択 カット Ctrl + X 任意の箇所を選択 ★行コピペ Ctrl + Alt + ↓ 1行下にペーストされる ★行削除 Ctrl + D キャレットのから行末まで削除 Ctrl + Shift + Delete コードの見た目を整える
ショートカット キー 補足 行ごと上/下移動 Alt + ↑/↓ ★インデント整理 Ctrl + Shift + F 右にインデント tab 複数行選択も可能 左にインデント Shift + tab 複数行選択も可能 作業効率向上
ショートカット キー 補足 テンプレート・プロポーザル Ctrl + space コード記述補助。ググるの推奨 直近に記述したクラスをimport Ctrl + Shift + M import編成 Ctrl + Shift + O 複数クラスをimport、不要import削除 ★quick fixes表示 Ctrl + 1 コンパイルエラーの修正 ★次の警告・エラーに移動 Ctrl + . メソッドの型変更 Shift + Alt + C 参照元なども勝手に変わる メソッドの呼び出し階層を開く Ctrl + Alt + H 定義の中や宣言の上で押す。コードの解読に◎ 名前変更 Shift + Alt + R 変数・メソッド両方可。呼び出し先も変わる ★キャレット移動 Ctrl + ←/→ 単語ごと等に移動。Shiftと組み合わせると◎ ★コメントにする/戻す Ctrl + / 任意の箇所を選択 ★対応する括弧行き来 Ctrl + Shift + P ★選択範囲の拡大/縮小 Shift + Alt + ↑/↓ まとまりごとに自動選択してくれる エディタ外
ファイル・タブ
ショートカット キー 補足 ★元に戻す Ctrl + Z やり直し Ctrl + Y 戻しすぎたものを進める ★ファイル保存 Ctrl + S 全ファイルを保存 Ctrl + Shift + S ファイルを閉じる Ctrl + W 全ファイルを閉じる Ctrl + Shift + W ★編集タブを戻る/進む Alt + ←/→ 検索
ショートカット キー 補足 ファイル検索 Alt + Aを押してF Javaファイル検索 Alt + H ★編集タブを検索して移動 Ctrl + E タブのリストが出てくるので選択して移動 文字列検索 Shift + F 型検索 Ctrl + Shift + F
- 投稿日:2020-05-29T12:26:37+09:00
Bypass JEP290
关于JEP290
JEP290是Java底层为了缓解反序列化攻击提出的一种解决方案,主要做了以下几件事
- 提供一个限制反序列化类的机制,白名单或者黑名单。
- 限制反序列化的深度和复杂度。
- 为RMI远程调用对象提供了一个验证类的机制。
- 定义一个可配置的过滤机制,比如可以通过配置properties文件的形式来定义过滤器。
JEP290的实际限制
写一个RMIServer
RMIServer.java
package org.chabug.rmi.server; import java.rmi.Naming; import java.rmi.registry.LocateRegistry; public class RMIServer { public static String HOST = "127.0.0.1"; public static int PORT = 1099; public static String RMI_PATH = "/hello"; public static final String RMI_NAME = "rmi://" + HOST + ":" + PORT + RMI_PATH; public static void main(String[] args) { try { // 注册RMI端口 LocateRegistry.createRegistry(PORT); // 创建一个服务 Hello hello = new HelloImpl(); // 服务命名绑定 Naming.rebind(RMI_NAME, hello); System.out.println("启动RMI服务在" + RMI_NAME); } catch (Exception e) { e.printStackTrace(); } } }HelloImpl.java
package org.chabug.rmi.server; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; public class HelloImpl extends UnicastRemoteObject implements Hello { protected HelloImpl() throws RemoteException { } public String hello() throws RemoteException { return "hello world"; } public String hello(String name) throws RemoteException { return "hello" + name; } public String hello(Object object) throws RemoteException { System.out.println(object); return "hello "+object.toString(); } }Hello.java
package org.chabug.rmi.server; import java.rmi.Remote; import java.rmi.RemoteException; public interface Hello extends Remote { String hello() throws RemoteException; String hello(String name) throws RemoteException; String hello(Object object) throws RemoteException; }使用JDK7u21打Commonscollections1,成功弹出calc。
使用JDK8u221启动RMIServer攻击失败
报错显示ObjectInputFilter REJECTED: class sun.reflect.annotation.AnnotationInvocationHandler, array length: -1, nRefs: 8, depth: 2, bytes: 298, ex: n/aJEP290的过滤机制
在上文的报错中可见
sun.reflect.annotation.AnnotationInvocationHandler
被拒绝,跟一下RMI的过程,看下在哪里过滤了,JEP290又是怎么实现的。以下使用JDK8U221调试首先我们要清楚RMI的实现流程
在远程引用层中客户端服务端两个交互的类分别是RegistryImpl_Stub
和RegistryImpl_Skel
,在服务端的RegistryImpl_Skel
类中,向注册中心进行bind、rebind操作时均进行了readObject操作以此拿到Remote远程对象引用。
跟进63行进入到
java.io.ObjectInputStream#readObject
,然后进入readObject0()
在readObject0()之中进入readOrdinaryObject()
继续进入readClassDesc()
进入readProxyDesc()
先检查其所有接口,然后检查对象自身。进入filterCheck()之后
调用了serialFilter.checkInput(),最终来到sun.rmi.registry.RegistryImpl#registryFilter
return String.class != var2 && !Number.class.isAssignableFrom(var2) && !Remote.class.isAssignableFrom(var2) && !Proxy.class.isAssignableFrom(var2) && !UnicastRef.class.isAssignableFrom(var2) && !RMIClientSocketFactory.class.isAssignableFrom(var2) && !RMIServerSocketFactory.class.isAssignableFrom(var2) && !ActivationID.class.isAssignableFrom(var2) && !UID.class.isAssignableFrom(var2) ? Status.REJECTED : Status.ALLOWED;
没有给AnnotationInvocationHandler
白名单,所以返回REJECTED。绕过JEP290
在RMI远程方法调用过程中,方法参数需要先序列化,从本地JVM发送到远程JVM,然后在远程JVM上反序列化,执行完后,将结果序列化,发送回本地JVM,而在本地的参数是我们可以控制的,如果向参数中注入gadget会怎么样?
我在HelloImpl实现了三个hello()方法,分别是void、string、Object类型的参数
也就是说:如果目标的RMI服务暴漏了Object参数类型的方法,我们就可以注入payload进去。
那么别的参数类型呢?在sun.rmi.server.UnicastRef#unmarshalValue中判断了远程调用方法的参数类型
如果不是基本类型,就进入readObject,之后的流程也走了filterCheck过滤
不过在sun.rmi.transport.DGCImpl#checkInput
这里ObjID是在白名单中的,所以可以被反序列化。那这个只是object类型的参数可以,其他的参数类型呢?
由于攻击者可以完全控制客户端,因此他可以用恶意对象替换从Object类派生的参数(例如String)有几种方法:
- 将java.rmi软件包的代码复制到新软件包,然后在其中更改代码
- 将调试器附加到正在运行的客户端,并在序列化对象之前替换对象
- 使用Javassist之类的工具更改字节码
- 通过实现代理来替换网络流上已经序列化的对象
afanti师傅用的是通过RASP hook住
java.rmi.server.RemoteObjectInvocationHandler
类的InvokeRemoteMethod
方法的第三个参数非Object的改为Object的gadget。他的项目地址在RemoteObjectInvocationHandler。修改
src\main\java\afanti\rasp\visitor\RemoteObjectInvocationHandlerHookVisitor.java
的dnslog地址,然后打包出来在RMIClient运行前加上-javaagent:e:/rasp-1.0-SNAPSHOT.jar
但是dnslog已经收到请求了。
参考
- 投稿日:2020-05-29T01:37:22+09:00
SpringBootでStrategyパターンを適用する
はじめに
SpringBootでStrategy パターンを適用したサンプルコードを書いてみました。
Strategy パターンについては IT専科さんの解説記事 などを参照してください。
サンプルコードは GitHub に公開しています。動作環境
- Java 11
- SpringBoot 2.3.0
題材
大学の図書館を題材にしています。ユーザは本の貸出、返却ができます。またユーザは役職によって借りられる本の数が変化することとします。今回、Strategyにあてはめるのはユーザの役職によって変化する処理の部分です。サンプルコードでは教授、院生、学部生の場合を実装しています。
Strategyを利用する(呼び出す)側はFactoryに依頼をして目的にあったStrategyを取得します。クラス図
インターフェイスの定義
インターフェイスでは、各役職に依存した処理を定義しています。ユーザごとに借りれる本の上限が決まるわけではなく、役職ごとに決定するので上限冊数を返却するメソッドも定義します。また、役職を識別するEnumである
PositionName
を返却するメソッドも定義します。
borrowBook
とreturnBook
で引数としているUser
とBook
は単なるPOJOです。詳しくは GitHub を参照してください。BorrowerStrategy.javapublic interface BorrowerStrategy { int getMaxBorrowNum(); User borrowBook(User user, Book book); User returnBook(User user, Book book); PositionName getPositionName(); }PositionName.javapublic enum PositionName { PROFESSOR, GRADUATE_STUDENT, COLLEGE_STUDENT }Strategyの実装
先程定義したインターフェイスを実装します。教授と院生の場合を例として挙げます。今回は実装が同じメソッドが目立ちますが、権限によって借りられる本の種類が制限されたりすると更にありがたみを感じることでしょう。
後述しますが、SpringによるDIを利用したいので@Component
アノテーションをクラスに付与します。ProfessorStrategy.java@Component public class ProfessorStrategy implements BorrowerStrategy { @Override public int getMaxBorrowNum() { return 50; } @Override public User borrowBook(User user, Book book) { if (user.getCurrentNum() >= getMaxBorrowNum()) { throw new CannotBorrowBookException(); } final var books = new ArrayList<>(user.getBooks()); books.add(book); return new User(user.getName(), user.getPositionName(), books); } @Override public User returnBook(User user, Book book) { if (user.getCurrentNum() == 0 || !user.getBooks().contains(book)) { throw new CannotReturnBookException(book.getName()); } final var books = new ArrayList<>(user.getBooks()); books.remove(book); return new User(user.getName(), user.getPositionName(), books); } @Override public PositionName getPositionName() { return PositionName.PROFESSOR; } }GraduateStudentStrategy.java@Component public class GraduateStudentStrategy implements BorrowerStrategy { @Override public int getMaxBorrowNum() { return 30; } @Override public User borrowBook(User user, Book book) { if (user.getCurrentNum() >= getMaxBorrowNum()) { throw new CannotBorrowBookException(); } final var books = new ArrayList<>(user.getBooks()); books.add(book); return new User(user.getName(), user.getPositionName(), books); } @Override public User returnBook(User user, Book book) { if (user.getCurrentNum() == 0 || !user.getBooks().contains(book)) { throw new CannotReturnBookException(book.getName()); } final var books = new ArrayList<>(user.getBooks()); books.remove(book); return new User(user.getName(), user.getPositionName(), books); } @Override public PositionName getPositionName() { return PositionName.GRADUATE_STUDENT; } }Factoryの実装
Factoryは主にサービスから呼ばれることを想定しているので、
@Component
アノテーションをクラスに付与します。このクラスはSet<BorrowStrategy> brrorwerStrategySet
をAutowireしているのですが、これを記述することで BrrorwerStrategyを継承したBeanのSetを注入することができます 。BorrowerStrategyFactory.java@Component public class BorrowerStrategyFactory { private Map<PositionName, BorrowerStrategy> borrowerStrategyMap; @Autowired public BorrowerStrategyFactory(Set<BorrowerStrategy> borrowerStrategySet) { createStrategy(borrowerStrategySet); } public BorrowerStrategy findStrategy(PositionName positionName) { return borrowerStrategyMap.get(positionName); } private void createStrategy(Set<BorrowerStrategy> borrowerStrategySet) { borrowerStrategyMap = borrowerStrategySet.stream() .collect(Collectors.toMap(BorrowerStrategy::getPositionName, borrowerStrategy -> borrowerStrategy)); } }実装の確認
今回の実装をユニットテストで確認していきます。ここでは一部をご紹介します。
BorrowerStrategyFactoryTest.java@SpringBootTest public class BorrowerStrategyFactoryTest { static final String PROFESSOR = "Professor"; static final String GRADUATE_STUDENT = "GraduateStudent"; static final String COLLEGE_STUDENT = "CollegeStudent"; @Autowired BorrowerStrategyFactory borrowerStrategyFactory; @Test void canGetAllStrategy() { final var professorStrategy = borrowerStrategyFactory.findStrategy(PositionName.PROFESSOR); assertThat(professorStrategy).isInstanceOf(ProfessorStrategy.class); final var graduateStudentStrategy = borrowerStrategyFactory.findStrategy(PositionName.GRADUATE_STUDENT); assertThat(graduateStudentStrategy).isInstanceOf(GraduateStudentStrategy.class); final var collegeStudentStrategy = borrowerStrategyFactory.findStrategy(PositionName.COLLEGE_STUDENT); assertThat(collegeStudentStrategy).isInstanceOf(CollegeStudentStrategy.class); } @ParameterizedTest @MethodSource void successBorrowBook(User user) { final var borrowerStrategy = borrowerStrategyFactory.findStrategy(user.getPositionName()); user = borrowerStrategy.borrowBook(user, new Book("Book1")); assertThat(user.getCurrentNum()).isEqualTo(1); } static Stream<Arguments> successBorrowBook() { return Stream.of( Arguments.arguments(new User(PROFESSOR, PositionName.PROFESSOR)), Arguments.arguments(new User(GRADUATE_STUDENT, PositionName.GRADUATE_STUDENT)), Arguments.arguments(new User(COLLEGE_STUDENT, PositionName.COLLEGE_STUDENT)) ); } // 中略 @ParameterizedTest @MethodSource void failedReturnBookWhenNotFoundBook(User user) { final var borrowerStrategy = borrowerStrategyFactory.findStrategy(user.getPositionName()); final var failedUser = borrowerStrategy.borrowBook(user, new Book("Book1")); assertThrows(CannotReturnBookException.class, () -> borrowerStrategy.returnBook(failedUser, new Book("Book2"))); assertThat(failedUser.getCurrentNum()).isEqualTo(1); } static Stream<Arguments> failedReturnBookWhenNotFoundBook() { return Stream.of( Arguments.arguments(new User(PROFESSOR, PositionName.PROFESSOR)), Arguments.arguments(new User(GRADUATE_STUDENT, PositionName.GRADUATE_STUDENT)), Arguments.arguments(new User(COLLEGE_STUDENT, PositionName.COLLEGE_STUDENT)) ); } }結果
Strategy パターンを適用するメリット
- 同じようなswitch文が乱立しない
- パターンの増減にも対応しやすい
2つ目のメリットに関しては、いらなくなったらそのクラスを削除することで対応できます。今回のケースでは「期間限定で市民が本を借りれるようにしたい」といった要望に答えることができます。
さいごに
Strategy パターンを適用するとswitch文の多用で煩雑になりそうなケースが回避できる場合があります。Strategy パターンに似たデザインパターンであるState パターンもあるので、気になる方は調べてみてください。
ソースコード
参考サイト