20210321のJavaに関する記事は12件です。

Javaで地図を書く時に使うGeoToolsのリポジトリが変わっていました(←勘違い)

Javaで地図を書きたくてネット上のサンプルを写経したらGeoToolsライブラリがダウンロードできない問題に遭遇しました。
末尾に挙げた「Gradleを介してGeoToolsdependencisを解決するための正しいURLは何ですか」に記されているとおり依存ライブラリのダウンロード先を変更したら解決しました。

遭遇した問題

$ ./gradlew build
> Task :compileJava FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':compileJava'.
> Could not resolve all files for configuration ':compileClasspath'.
   > Could not find org.geotools:gt-shapefile:22.2.
     Searched in the following locations:
       - https://repo.maven.apache.org/maven2/org/geotools/gt-shapefile/22.2/gt-shapefile-22.2.pom
(以下省略)

解決方法

GeoToolsを使うために、https://repo.osgeo.org/repository/releaseをリポジトリに追加しました。
後からわかったことですが、GeoTools公式HPをよく見たら、Mavenの例ですが先のリポジトリが必要であると書いてありました。

plugins {
    id 'java'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    // このリポジトリを追加したら解決しました。
    maven {url "https://repo.osgeo.org/repository/release"}
    mavenCentral()
}

dependencies {
    compile 'org.geotools:gt-shapefile:22.2'
    compile 'org.geotools:gt-swing:24.2'

    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
}

test {
    useJUnitPlatform()
}

写経したJavaプログラム

package hellogeotools;

import java.io.File;
import java.io.IOException;
//import org.geotools.data.CachingFeatureSource;
import org.geotools.data.FileDataStore;
import org.geotools.data.FileDataStoreFinder;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.styling.SLD;
import org.geotools.styling.Style;
import org.geotools.swing.JMapFrame;
import org.geotools.swing.data.JFileDataStoreChooser;

public class HelloGeoTools {

    public static void main(String[] args) throws IOException {
        System.out.print("Javaバージョン(java.version):");
        System.out.println(System.getProperty("java.version"));

        System.out.print("オペレーティングシステム名(os.name):");
        System.out.println(System.getProperty("os.name"));

        File file = JFileDataStoreChooser.showOpenFile("shp", null);
        if (file == null) {
            System.err.println("No such file.");
            return;
        }

        FileDataStore store = FileDataStoreFinder.getDataStore(file);
        SimpleFeatureSource featureSource = store.getFeatureSource();

        MapContent map = new MapContent();
        map.setTitle("Quickstart");

        Style style = SLD.createSimpleStyle(featureSource.getSchema());
        Layer layer = new FeatureLayer(featureSource, style);
        map.addLayer(layer);

        JMapFrame.showMap(map);
    }
}

参考

環境

  • Ubuntu 18.04
  • Java 11.0.10
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

改めてpure javaでHello World

はじめに

今やどこの開発現場でも何かしらのフレームワーク・ビルドツールを使うのが主流になっているかと思います。が、javacとかいうコマンドを使ってコンパイルする方法、みなさん覚えていますか?(私は覚えておりませんでした)
先日ふと「研修で習ったjavacコマンドってどうやって使うんだっけ?」と思い復習しましたので記録しておきます。

基本の流れ

  1. javaファイルを作成
  2. コンパイル
  3. 実行

さっそくHello World

javaファイルを作成

Main.javaというファイルを作成しました。
標準出力するだけの簡単な内容です。
スクリーンショット 2021-03-21 19.16.21.png

コンパイル

先ほど作成したMain.javaをコンパイルします。

コンパイルとは、ソースコードの内容をコンピューターが実行できる形式に変換することです。
Javaにおいてはクラスファイル(バイナリコードのファイル)に変換することを指します。

実行コマンド.
javac ソースファイル名.java
今回はこんな感じ.
javac src/main/Main.java

そうすると、同じディレクトリに.classファイルが作成されます

$ ls src/main/
Main.class      Main.java

実行

コンパイルしたプログラムをコンピューターに解釈させ、動作させます。

実行コマンド.
java クラス名
今回はこんな感じ.
java src/main/Main

# 実行結果
Hello World
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

EC2にJavaWebアプリ環境を構築 #2

EC2にJavaWebアプリ環境を構築 #1
EC2にJavaWebアプリ環境を構築 #2
EC2にJavaWebアプリ環境を構築 #3
EC2にJavaWebアプリ環境を構築 #4

目次

1.はじめに
2.インストール

はじめに

ざっくり説明するとeclipseで作ったアプリをEC2で動かす

  • 前回
    • インスタンスの作成が完了

インストール

JDK

開発環境と同じjdk11を使用する
インストールはamazonが提供しているパッケージからおこなう

インストール

sudo amazon-linux-extras install java-openjdk11 -y

-y というコマンドは全部yesと回答するオプションみたいなやつなのでコマンドを打てばインストールが完了する

java -version

でバージョン確認が可能


nginx

WEBサーバはnginxを使用

Apacheでも可能だが私は、
nginxのロゴがカッコイイ
という理由からnginxを採用

小規模なアプリを稼働させるくらいなので好みで選んで良いと思う

インストール

sudo amazon-linux-extras install nginx1 -y

nginxの起動と自動起動の設定も行う

#起動
sudo systemctl start nginx
#自動起動
sudo systemctl enable nginx

基本的には最新版がインストールされる(多分)

ステータス、バージョン確認は下記コマンド

#ステータス確認
systemctl status nginx
#バージョン確認
nginx -v

nginxをインストールできたらアクセスしてみる

インスタンス画面から
パブリックIPv4 DNSをコピーしアクセス
パブリックIPv4 DNS

nginxのページが表示されればOK
nginxページ


Tomcat

記事では最新のver.10を使用する
ver.9などでも基本的にやり方は変わらないので真似してOK(だと思う)

雑だが10と9の違いは

//Tomcat 10
import jakarta.servlet
//Tomcat 9
import javax.servlet

インポートの記述が違うこと
あとファイルの配置が違うくらい

Tomcat 10 Tomcat 9

公式サイトより

  • Core
    • tar.gz

のリンクをコピー

ダウンロード

wget https://downloads.apache.org/tomcat/tomcat-10/v10.0.4/bin/apache-tomcat-10.0.4.tar.gz

URLはコピーを貼り付ける

下記コマンド入力

ver.9をダウンロードした場合は
tomcat-10を
tomcat-9に変更するのが良いと思う

#解凍
tar -xzvf ~/apache-tomcat-10.0.4.tar.gz
#ファイル移動&リネーム
sudo mv ~/apache-tomcat-10.0.4 /opt/tomcat-10
#Windowsでいうショートカット作成
sudo ln -s /opt/tomcat-10 /opt/tomcat
#ユーザー追加
sudo useradd -s /sbin/nologin tomcat
#所有権変更
sudo chown -R tomcat:tomcat /opt/tomcat-10
#viが起動する
sudo vi /etc/systemd/system/tomcat.service

【vi操作(適当)】
i貼り付けESC:wqエンター

Ver.10

[Unit]
Description=Apache Tomcat 10
After=network.target

[Service]
User=tomcat
Group=tomcat
Type=oneshot
PIDFile=/opt/tomcat-10/tomcat.pid
RemainAfterExit=yes

ExecStart=/opt/tomcat-10/bin/startup.sh
ExecStop=/opt/tomcat-10/bin/shutdown.sh
ExecReStart=/opt/tomcat-10/bin/shutdown.sh;/opt/tomcat-10/bin/startup.sh

[Install]
WantedBy=multi-user.target

Ver.9

[Unit]
Description=Apache Tomcat 9
After=network.target

[Service]
User=tomcat
Group=tomcat
Type=oneshot
PIDFile=/opt/tomcat-9/tomcat.pid
RemainAfterExit=yes

ExecStart=/opt/tomcat-9/bin/startup.sh
ExecStop=/opt/tomcat-9/bin/shutdown.sh
ExecReStart=/opt/tomcat-9/bin/shutdown.sh;/opt/tomcat-9/bin/startup.sh

[Install]
WantedBy=multi-user.target

vi終了後

#実行権限付与
sudo chmod 755 /etc/systemd/system/tomcat.service
#起動
sudo systemctl start tomcat
#自動起動
sudo systemctl enable tomcat

ステータス確認は下記コマンド

systemctl status tomcat

Tomcatをインストールできたらアクセスしてみる

nginxと同様、パブリックIPv4 DNSアドレスを入力し、
末尾に「:8080」を追加する

イメージ

http://amazonaws.com:8080

Tomcatの画面が表示されればOK
Tomcatページ


長くなってきたのでここまで

次へ:EC2にJavaWebアプリ環境を構築 #3

参考サイト

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

public static void mainとは何なのか(Java学習1日目)

はじめに

java初学者による備忘録になります。
なにか間違っている事などございましたらご指導して頂けると幸いです。

エントリーポイントとは

プログラムを開始する場所をエントリーポイント(開始点)と呼びます。
javaのエントリーポイントはmainメソッドです。
mainメソッド=開始点ですから、これが存在しない場合や正しく記述されていない場合は
他のメソッドを使用する事が出来ません。
さて。このmainメソッドですが、定義する為のルールがいくつか存在します。

mainメソッドを定義する上でのルール

ルールは5つあります。

1.アクセス修飾子はpublic
2.staticメソッド
3.メソッドの戻り値はvoid
4.メソッド名はmain(小文字)
5.メソッドの引数はStringの配列(またはStringの可変長引数1)のみ

上記のルールを守った記述をした場合、以下の様なコードになるでしょう。

class Main {
    public static void main (String[] args) { 
        // 処理
    }
}

アクセス修飾子とpublic

ルールの1つ目に出てきたアクセス修飾子とは、(Mainメソッドを)利用できるプログラムの範囲を指定する修飾子です。publicを指定した場合、他の全クラスが(Mainメソッドを)利用できるようになります。このような設定をする理由は、メソッドを利用時に、開始点であるmainメソッドにアクセスする必要があるからです。

staticメソッド

staticメソッドとは、インスタンスが存在しなくても使用できるメソッドです。エントリーポイントは、最初にプログラムを起動する場所です。なので、起動した時点ではインスタンスが存在しない可能性があります。その為、このような設定が必要になるわけです。

メソッドの戻り値はvoid

voidとは空を意味する英単語です。戻り値はvoidとは戻り値が無いことを意味します。ただ、なぜこのような規約を設けたのか調べてもわからなかったので、ご存じの方がいましたら是非教えてください。

4.メソッド名はmain(小文字)

javaはC言語の後継言語なので、C言語と同様の規約を流用したという説が
あります。

メソッドの引数はStringの配列のみ

mainメソッドは最初に実行されるメソッドです。したがって、他のクラスからメソッドを呼び引数を渡すと2重で呼び出されることになってしまいエラーが出てしまいます。そこで、特別な方法を使用して引数を渡す必要があります。それがコマンドライン引数です。コマンドラインに入力した情報を引数として渡します。そして、コマンドラインには人間が打ち込んだ情報が表示され、人間が打ち込む情報は文字列であるという考えからstringの配列を使用しなければならないと定められたようです。

最後に

これから可能な限り備忘録として記事投稿をしていきたいと思います。未熟者ではありますがよろしくお願いします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

実はJrubyはgradleだけで管理できる

はじめに

Jruby覚えてますか?JRubyはまだ現役です。こういう言語を扱うと必ずと言っていいほど、「javaでいいじゃん」と言われます。確かにJRubyでできることはJVMでできることです。
現状ではRubyが好きな人がたまに使うといった感覚でしょうか。

ですが、私はJrubyでJava開発のテストを書いてます。理由は生産性が高いからです。

この記事では普段Java使いのみなさんがJrubyで楽できるtipsを紹介します。

依存環境

ですが、全て意識なくていいです。管理はすべてgradleで行います。ここでは一旦、jrubyのコマンドも忘れてしまいましょう。

初期プロジェクトを立ち上げる

必要なのは2つのファイルだけです。

terminal
mkdir sample
cd sample
nvim build.gradle #neovimはいいぞ
nvim main.rb
build.gradle
/*
 * This is sample programs under the jruby.
 *@ak.f
 */

buildscript {
  repositories {
    jcenter()
  }
  dependencies {
    classpath 'com.github.jruby-gradle:jruby-gradle-plugin:2.0.0'
    classpath 'com.github.jruby-gradle:jruby-gradle-jar-plugin:2.0.0'
    classpath 'com.github.jengelman.gradle.plugins:shadow:5.0.0'
  }
}

apply plugin: 'com.github.jruby-gradle.jar'

repositories {
  jcenter()
  ruby.gems()
}

dependencies {
  jrubyJar  'org.slf4j:slf4j-simple:1.7.12'
  jrubyJar "rubygems:colorize:0.7.7+"
}

jrubyJar {
  initScript "./main.rb"
}

task jrubyRun(type: Exec){
  dependsOn jrubyJar
  workingDir "./build/libs"
  commandLine 'java', '-jar', jrubyJar.outputs.files.singleFile.absolutePath
}

main.rb
require 'colorize'

java_import 'org.slf4j.Logger'
java_import 'org.slf4j.LoggerFactory'

logger = LoggerFactory.getLogger('demo')

puts "-" * 20
logger.info "Ruby version: #{RUBY_VERSION}"
logger.info "Ruby platform: #{RUBY_PLATFORM}"
logger.info "Current file: #{__FILE__}"
puts "-" * 20

puts "Roses are red".red
puts "Violets are blue".blue
puts "I can use JRuby/Gradle".green
puts "And now you can too!".yellow

以上がサンプルプロジェクトになります。
自分の管理するJavaライブラリを呼び出す場合は、

repositories {
jcenter()
ruby.gems()
maven {url 'mavenリポジトリのURL'}}
dependencies {
jrubyJar 'xxx.org:name:version'
}

として参照してやればgradleが全部やってくれます。

コマンドチートシート

  • とりあえず実行する gradle jrubyRun
  • jarファイルにビルドする gradle jrubyJar
  • ビルドファイルを削除する gradle clean
  • ビルドしたものを実行する java -jar build/libs/sample-jruby.jar

最後に

今どきのJRubyでした。
最近は日本語に記事であまり話題に上がらないですが、実務ではjavaライブラリのテストケースや、使い捨てのメインクラスを作成するぐらいなら私はJrubyで書くようになりました。
gradleで管理できるのが最高です。

ほとんど、http://jruby-gradle.org/ で紹介されているとおりですが、
build.gradleにはjrubyRunのタスクを定義してあります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Android] Jetpack DataBinding

Android Studio 4.1.3(windows版) での流れとなります

Jetpack DataBindingでボタンイベントを設定する方法を例にして説明します

Android DeveloperのJetpack DataBindingの説明は以下です

準備

以下を参考にJetpack Navigation + ViewModelを作成してください

DataBindingを使用するための設定

build.gradleに以下を追加します
DataBindingを有効にするとBindingクラスが自動生成されます

build.gradle
android {
    dataBinding {
        enabled = true
    }
}

ViewModelでの準備

ボタンイベントを受けるメソッドを用意します

MainViewModel.java
public void onClickButton() {
}

FragmentのLayoutの準備

ここではFrameLayoutをConstraintLayoutにしてid:buttonのボタンを追加します

image.png
image.png
※ Convert FraneLayout to ConstraintLayoutでも変更できます

Codeを開きConstraintLayoutタグの箇所でwindowsの場合はAlt+Enterなどで
Convert to data bindinglayout を選択します
image.png

追加されたdataタグにViewModelを追加します

main_fragment.xml
<data>
    <variable name="viewModel" type="com.xxx.sample.MainViewModel" />
</data>

ボタンのonClickに以下を追加します
※DesignまたはCodeで直接追加してください

@{() -> viewModel.onClickButton()}

image.png

Fragmentでの準備

DataBindingの準備とDataBindingにViewModelをセットします

MainFragment.java
// ViewModelはFragment作成時のテンプレートで自動生成されたものです
private MainViewModel mViewModel;
// 自動生成されます(main_fragment.xml)
private MainFragmentBinding binding;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
    binding = DataBindingUtil.inflate(inflater, R.layout.main_fragment, container, false);
    final View view = binding.getRoot();
    return view;
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    mViewModel = new ViewModelProvider(this).get(MainViewModel.class);
    // TODO: Use the ViewModel
    binding.setViewModel(mViewModel);
}

この記事は以下の記事の補足です

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

仮引数と実引数

実引数

渡す値の事

Sample.java
 List<String> list = new ArrayList<>();
finalSample(list);//これ

add(100,200)//これ

仮引数

受け取る変数(変数名はなんでもいい)

Sample.java
 public static void finalSample(List<String> list)//これ
public static void add(int x , int y) //これ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

参照渡し+値渡し+String+オブジェクトの場合+List

Stringクラスのオブジェクトの場合

Stringクラスのオブジェクトをメソッドに渡した場合、メソッド側の引数には同じオブジェクトの位置が渡されるのは配列と同じですが、文字列変数に新しい文字列を代入するとまったく別の場所にオブジェクトが新しく作成され、その新しい場所の位置がメソッド内の変数に代入されます。その為、元の場所には元の文字列が格納されたままなのでメソッド呼び出し元の文字列には何も影響を与えません。

String.java
    public static void main(String[] args) {
        String str = "abc";
        test(str);//先にここのtext()が実行される
        System.out.println(str);
    }
    private static void test(String str) {
        str = "def";
        System.out.println(str);
    }
実行
def
abc


List

参照型の変数の場合は、final修飾子をつけても値の変更が可能になってしまいます。
このように参照型の変数を引数に使う場合は、呼び出し元の値が変わるため注意してください。

List.java
 public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        System.out.println(list);

        finalSample(list);
        System.out.println(list);
    }

    public static void finalSample(final List<String> list) {
// public static void finalSample(List<String> list)
        list.set(0, "A");
        list.set(1, "B");
        list.set(2, "C");
    }
実行
[a, b, c]
[A, B, C]

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

参照渡し+Method

参照渡し(引数にアドレスを渡す)

基本型変数などではなく配列など引数としてアドレスを渡していることを参照渡し、参照渡しを行うと呼び出し先で加えた変更が呼び出し元にも影響を与える

『配列をメソッド呼び出しで渡すと』
.呼び出し元の配列のアドレスが、呼び出し先の引数にコピーされる
.呼び出し先で配列の実体を書き換えると、呼び出し元にも影響を与える

値渡し(値そのものが渡されること)

『基本型の変数をメソッド呼び出しで渡すと』
.呼び出し元の変数の内容が、呼び出し先の引数にコピーされる
.呼び出し先で引数の内容を書き換えても呼び出し元の変数は変化しない

参照渡しと値渡しの例

MethodArray.java
    public static void main(String[] args) {
              int num = 8;
              int array[] = {10, 4};

              System.out.println(num);
              System.out.println(array[0]);

              test(num, array);

              System.out.println(num);
              System.out.println(array[0]);
            }

            private static void test(int num, int array[]){
              num = 5;//ここで代入しても変わらない値渡し
              array[0] = 12;//アドレスを渡すので変わる
実行
8
10
8
12

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javaアクセス修飾子表

アクセス修飾子表

時々忘れてしまうことがあるのでメモしておきます。

アクセス修飾子 同じクラスからのアクセス 同じパッケージからのアクセス サブクラスからのアクセス その他のアクセス
public
protected ×
記述なし × ×
private × × ×
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

血圧測定結果管理プログラムのソースコードを公開いたします

血圧測定結果管理プログラムのソースコードを公開いたします

//
//
//    血圧記録プログラム
//  新規作成日  2021/03/19(Ver1.0)
//    作成者   乃木坂46好きのITエンジニア
//
//
//
//
//
//

//パッケージの定義
package Ketsuatsu;

//importするAPIを定義
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;



//標準入力クラス
class top_ketsuatsudata{
    //日付オブジェクトを呼び出す
    //Date d = new Date();
    //日付フォーマットオブジェクトを呼び出す
    //SimpleDateFormat d1 = new SimpleDateFormat("yyyy/MM/dd");
    //文字列型のデータに変換する
    //private String hiduke = d1.format(d);
    //上の血圧
    private int t_blood;
    //下の血圧
    //private int u_blood;
    //ループフラグ
    private boolean flag = false;
    //Scannerオブジェクトを呼び出す
    Scanner sc = new Scanner(System.in);
    //上の血圧を入力するメソッド
  public int t_blood_data() {
    while ( flag == false) {
        System.out.println("上の血圧を入力してください");
        String line1 = sc.nextLine();
        //例外処理判定
        try {
            t_blood = Integer.parseInt(line1);
            flag = true;

        //文字列を入力された場合の処理
        } catch (NumberFormatException e){
            System.out.println("数値を入力してください");
        }
    }
    return t_blood;

  }
}
class under_ketsuatsudata{
    //下の血圧フィールド
    private int u_blood;
    //ループフラグ
    private boolean flag = false;
    //Scannerオブジェクトを呼び出す
    Scanner sc = new Scanner(System.in);
    //上の血圧を入力するメソッド
    public int u_blood_data() {
        while ( flag == false) {
            System.out.println("下の血圧を入力してください");
            String line2 = sc.nextLine();
            //例外処理判定
            try {
                u_blood = Integer.parseInt(line2);
                flag = true;

            //文字列を入力された場合の処理
            } catch (NumberFormatException e){
                System.out.println("数値を入力してください");
            }
        }
        return u_blood;
    }
}
//出力結果クラス
class kekka{
  public static void kekka(int a,int b) {
    //日付オブジェクトを呼び出す
    Date d = new Date();
    //日付フォーマットオブジェクトを呼び出す
    SimpleDateFormat d1 = new SimpleDateFormat("yyyy/MM/dd");
    String c = d1.format(d);
    String msg = "";

    //System.out.println(c);


    //厚生労働省のBMI値の基準でやせすぎ、普通、肥満度を判定する。
        if ((a < 120) && (b < 80)) {
            msg = "Low-blood";
            //System.out.println("低血圧です");
        } else if (((a>=120) && (a<140)) || ((b >= 80) &&( b < 90))) {
            msg = "Normal-Blood";
            //System.out.println("正常血圧です");
        } else if (((a>=140) && (a<160)) || ((b >= 90) &&( b < 100))) {
            msg = "High-blood(First)";
            //System.out.println("高血圧(1度)です");
        }else if (((a>=160) && (a<180)) || ((b >= 100) &&( b < 110))) {
            msg = "High-blood(Second)";
            //System.out.println("高血圧(2度)です");

        } else {
            msg = "High-blood(Critical)";
            //System.out.println("高血圧(3度)です");
        }
        //ファイルの書き込み処理
        FileWriter fw = null;
        //書き込みエラーが出た場合、書き込みエラーをメッセージ表示する
        try {
            fw = new FileWriter("ketsuatsu.csv",true);
            fw.write(c + "," + a + "," + b + "," + msg + "\n");
            fw.flush();
        } catch (IOException e) {
            System.out.println("ファイル書き込みエラーです");
        } finally {
            //ファイルを閉じる処理。閉じるのに失敗した場合、エラーメッセージを表示する。
            if (fw !=null) {
                try {
                    if (fw != null) {
                        fw.close();
                    }
                } catch(IOException e2) {
                    System.out.println("ファイルを閉じるのに失敗しました");
                }
                System.out.println("ファイルの書き込みに成功しました");
            }
        }
    }

}

//メインクラス
public class Ketsuatsu {
    public static void main(String[] args) {
        //上の血圧データオブジェクトを呼び出す
        top_ketsuatsudata t = new top_ketsuatsudata();
        //下の血圧データオブジェクトを呼び出す
        under_ketsuatsudata u = new under_ketsuatsudata();
        //戻り値フィールドを定義する。
        int top_blood;
        int under_blood;
        //上の血圧を返す。
        top_blood = t.t_blood_data();
        //下の血圧を返す。
        under_blood = u.u_blood_data();
        //結果表示メソッドを呼び出す
        //KEKKAオブジェクトを呼び出す。
        kekka k = new kekka();
        k.kekka(top_blood,under_blood);
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java: EasyBuggy - 30分でわかる!SQLインジェクションの脆弱性

概要

EasyBuggyEasyBuggy Bootを利用して、SQLインジェクションの脆弱性をシミュレートし、その修正を試みます。

環境構築はこちらの記事から。

注意事項

本記事では脆弱性をついた攻撃手法について解説しています。

しかし、実際に第三者が運用しているWebサービスなどに対して、勝手に脆弱性の検査をおこなうのは違法行為となる可能性が非常に高いです。無害な検査用文字列を送信しているだけのつもりであっても、意図しない破壊を招いたり、監視システムによって攻撃と勘違いされる可能性があります。

絶対にやめましょう。

参考事例: 脆弱性検査について

SQLインジェクションの怖さ

SQLインジェクションの怖さを理解するには、下記の動画がおすすめです。

これは、実際に経済産業省からも注意喚起がされている脆弱性ですね。

脆弱性を確認する

EasyBuggyを起動し、脆弱性 > SQLインジェクションを選択します。

image.png

画面の指示に従い、パスワードに ' OR '1'='1 を入力します。
すると、ほかのユーザーすべての情報が表示できてしまいます。

脆弱性を修正する

XSS脆弱性の根本的解決のためには、いくつかの方法があります。
そのうち、今回は「SQL文の組み立ては全てプレースホルダで実装する」という方法での修正を試みます。

ただ、上記のページの文章を読むだけでは、ちょっと意味がつかみにくいかと思います。
実際のコードで見てみましょう。

EasyBuggyの場合

SQLInjectionServletクラスに問題となる実装があります。

SQLInjectionServlet
import java.sql.Statement;
// (中略)
Statement stmt = null;
// (中略)
stmt = conn.createStatement();
rs = stmt.executeQuery("SELECT name, secret FROM users WHERE ispublic = 'true' AND name='" + name
        + "' AND password='" + password + "'");

SQLインジェクションの原因

さきほど確認したSQLインジェクションの原因ですが、このSQL文にパスワードを渡すときの実装に問題があります。
プログラムが実行されると、下記のようなSQL文が実行されます。

SELECT name, secret FROM users WHERE ispublic = 'true' AND name='[入力された名前]' AND password='[入力されたパスワード]'

このSQL文の[入力した名前]Mark[入力されたパスワード]' OR '1'='1で置き換えてみます。

SELECT name, secret FROM users WHERE ispublic = 'true' AND name='Mark' AND password='' OR '1'='1'

SQLの演算子はANDが先に処理されます。
このため、WHERE句のispublic = 'true' AND name='Mark' AND password=''の部分が先に処理されます。
name='Mark' AND password='' を満たすレコードはないため、結果はFALSEとなります。

SELECT name, secret FROM users WHERE FALSE OR '1'='1'

'1'='1' の結果は TRUE です。

SELECT name, secret FROM users WHERE FALSE OR TRUE

FALSE OR TRUE の結果は TRUE です。

SELECT name, secret FROM users WHERE TRUE

つまり、すべてのレコード行を返すSQL文になってしまいました。

順を追って修正していきましょう。

1. Statement から PreparedStatementへの変更

SQL文を組み立てるときは、java.sql.Statementではなくjava.sql.PreparedStatementを使いましょう。

StatementからPreparedStatementへの変更
import java.sql.PreparedStatement; // import java.sql.Statement; から変更
// (中略)
PreparedStatement stmt = null; // Statement stmt = null; から変更
// (中略)
stmt = conn.createStatement();
rs = stmt.executeQuery("SELECT name, secret FROM users WHERE ispublic = 'true' AND name='" + name
        + "' AND password='" + password + "'");

2. Connection#createStatement から Connection#prepareStatement への変更

先ほどの変更により、コンパイルエラーとなる箇所を修正します。
Connection#prepareStatementの引数にはとりあえず空文字を入れ、コンパイルエラーが解消されることを確認します。

StatementからPreparedStatementへの変更
import java.sql.PreparedStatement;
// (中略)
PreparedStatement stmt = null;
// (中略)
stmt = conn.prepareStatement(""); // stmt = conn.createStatement(); から変更
rs = stmt.executeQuery("SELECT name, secret FROM users WHERE ispublic = 'true' AND name='" + name
        + "' AND password='" + password + "'");

3. Connection#prepareStatement の引数にSQLを渡す

とりあえずコンパイルエラーは解消しました。

しかし、JavaDocに書いてある通り、 Connection#prepareStatement の引数にはSQLを渡す必要があります。このため、Statement#executeQuery の引数に渡しているSQLを移動させます。

Connection#prepareStatementの引数にSQLを渡す
import java.sql.PreparedStatement;
// (中略)
PreparedStatement stmt = null;
// (中略)
stmt = conn.prepareStatement("SELECT name, secret FROM users WHERE ispublic = 'true' AND name='" + name
        + "' AND password='" + password + "'");
rs = stmt.executeQuery();

ここまでは、脆弱性を修正するまでの準備段階です。

4. パラメータ・プレースホルダーを設定する

ここからが、実際に脆弱性を修正する部分になります。

パラメータ・プレースホルダーを設定することで、機械的な処理でSQL文が組み立てられます。これにより、SQLインジェクションの脆弱性を解消できます。

まず、パラメータ部分を?に置換します。

パラメータ・プレースホルダーを設定する
import java.sql.PreparedStatement;
// (中略)
PreparedStatement stmt = null;
// (中略)
stmt = conn.prepareStatement("SELECT name, secret FROM users WHERE ispublic = 'true' AND name=? AND password=?");
rs = stmt.executeQuery();

5. 設定したプレースホルダーにパラメータを割り当てる

PreparedStatement#setStringを呼び出して、さきほど設定したプレースホルダーにパラメータを割り当てます。

パラメータの数値は1から始まります。
1番目のプレースホルダーに変数nameの値を、2番目のプレースホルダーに変数passwordの値を割り当てます。

設定したプレースホルダーにパラメータを割り当てる
import java.sql.PreparedStatement;
// (中略)
PreparedStatement stmt = null;
// (中略)
stmt = conn.prepareStatement("SELECT name, secret FROM users WHERE ispublic = 'true' AND name=? AND password=?");
stmt.setString(1, name);
stmt.setString(2, password);
rs = stmt.executeQuery();

ここまできたら、再度、動作を確認してみましょう。
修正方法が正しければ、ほかのユーザーの情報が表示されることはなくなったはずです。

EasyBuggy Bootの場合

SQLInjectionServletクラスの実装は、SQLInjectionControllerクラスに移植されています。

Spring Frameworkを利用しているため、PreparedStatementではなくJdbcTemplateを利用してSQLを発行しています。こちらのほうが、簡単に修正することができます。

1. パラメータ・プレースホルダーを設定する

EasyBuggyと同じように、パラメータ・プレースホルダーを設定したSQL文字列に置き換えましょう。

2. 設定したプレースホルダーにパラメータを割り当てる

JdbcTemplate#queryでは、第3引数以降にパラメータを指定できます。
メソッドを呼び出す際の引数として、パラメータを順番に渡しましょう。

おつかれさまでした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む