読者です 読者をやめる 読者になる 読者になる

Spring Security入門した

はじめに

今回の記事では以下の実装を行ったのでメモ書きとして残しておきます。

  • EclipseでのSpring Loadedを使ったHot Swap
  • Thymeleaf3とSpring Securityによるフォーム認証

EclipseでのSpring Loadedを使ったHot Swap

公式に見にいくとIDEAしか書いてません。禿げた。

build.gradleに以下の設定を追加します。

apply plugin: 'eclipse'

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.4.2.RELEASE'
        classpath 'org.springframework:springloaded:1.2.6.RELEASE'
    }
}

eclipse{
    classpath {
        defaultOutputDir = file("${project.buildDir}/classes/main/")
    }
}

できました。
ここで1つ問題があります。

eclipseのbuildshipプラグインを使って、gradleのプロジェクトをリフレッシュすると
なぜかクラスの生成先がbinフォルダに固定されてしまいます。

Gradle IDEを使うとgradleのeclipseコマンドが動いているようなので
期待どおりに.classpathファイルが生成されます。

ホットスワップサイコー!!!

Thymeleaf 3とSpring Securityを使う設定を追加します。

build.gradleに以下の記述を追加します。

dependencies {
    // ....
    compile 'org.springframework.boot:spring-boot-starter-security'
    compile 'org.thymeleaf:thymeleaf:3.0.0.RELEASE'
    compile 'org.thymeleaf:thymeleaf-spring4:3.0.0.RELEASE'
    compile 'org.thymeleaf.extras:thymeleaf-extras-springsecurity4:3.0.0.RELEASE'
}

Spring Securityによるフォーム

まずは画面のファイルです。

src/main/resources/templates/login.htmlというパスに配置します。

<!DOCTYPE html>
<html lang="ja">

<head>
  <title></title>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
  hello out friend.
  <form id="login_form" method="post" action="/login/post">
    <label>ログインID</label>
    <input type="text" id="login_id" name="login_id" placeholder="ログインIDを入力してください" autofocus="" required="" />
    <label>パスワード</label>
    <input type="password" id="login_password" name="login_password" placeholder="パスワードを入力してください" required="" />
    <input id="login_button" type="submit" value="ログイン" />
    <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
  </form>
</body>

</html>

th:actionとか使うと勝手にtokenが入るみたいですが
とりあえず今回は自分で書きました。
後で試します。

ここでハマったのはth:nameじゃなくてname属性で記述して
アルェーオキカワラナイゾーって一人ハマってました。

loginフォームの表示のためにtemplateを使うので
なんかアホらしいですが、Controllerを書きます。

@Controller
public class AuthController {
  @GetMapping("/login")
  public String login(Model model) {
    // テンプレート名
    return "login";
  }
}

Security周りの設定をJavaで記述します。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.csrf()
      .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
    // ログインページは誰でも見れないといけない
    http.authorizeRequests()
      .antMatchers("/login")
      .permitAll()
      .anyRequest()
      .authenticated();
    // ログインはフォーム認証, ログイン成功後topに戻る
    http.formLogin()
      .loginProcessingUrl("/login/auth")
      .loginPage("/login")
      .defaultSuccessUrl("/")
      .usernameParameter("login_id")
      .passwordParameter("login_password");

    // ログアウト処理ページとその後の遷移
    http.logout()
      .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
      .logoutSuccessUrl("/login")
      .permitAll();
  }

}

application.ymlに以下の設定を追加しておきます。テスト用です。

security:
  user:
    name: test
    password: test

http://localhost:8080/にアクセスすると
http://localhost:8080/loginにリダイレクトされると思います。
loginした後はhttp://localhost:8080/に飛ばされ
http://localhost:8080/logoutにアクセスすると
もとのログインページに戻される動きが確認できると思います。

まとめ

サクッとSpring Securityに入門してみました。

この上になんか建ててみる予定です。

最後にめちゃくちゃ省略してしまったbuild.gradleを晒しておきます。

apply plugin: 'java'
apply plugin: 'jacoco'
apply plugin: 'com.diffplug.gradle.spotless'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

sourceCompatibility = 1.8
targetCompatibility = 1.8

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.diffplug.spotless:spotless-plugin-gradle:3.0.0'
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.4.2.RELEASE'
        classpath 'org.springframework:springloaded:1.2.6.RELEASE'
    }
}

repositories {
    mavenCentral()
}

// alias
task format(dependsOn: 'spotlessApply')
spotless {
    def headerFile = "/** "+project.file('../LICENSE.md').text+"*/"

    java {
        licenseHeader headerFile, '(package|import) '
        eclipseFormatFile project.file('eclipse-format-setting.xml')

        trimTrailingWhitespace()
        endWithNewline()
    }
}

eclipse{
    classpath {
        defaultOutputDir = file("${project.buildDir}/classes/main/")
    }
}


dependencies {
    compile 'org.springframework.boot:spring-boot-starter-data-jpa'
    compile 'org.springframework.boot:spring-boot-starter-data-rest'
    compile 'org.springframework.boot:spring-boot-starter-security'
    
    compile 'org.springframework.boot:spring-boot-starter-web'
    compile 'org.thymeleaf:thymeleaf:3.0.0.RELEASE'
    compile 'org.thymeleaf:thymeleaf-spring4:3.0.0.RELEASE'
    compile 'org.thymeleaf.extras:thymeleaf-extras-springsecurity4:3.0.0.RELEASE'
    
    compile 'com.h2database:h2'

    testCompile 'org.springframework.boot:spring-boot-starter-test'
    testCompile 'org.jmockit:jmockit:1.21'
    testCompile 'junit:junit:4.12'
    testCompile 'org.assertj:assertj-core:3.2.0'
    
    compileOnly  'org.projectlombok:lombok:1.16.14'
    testCompileOnly   'org.projectlombok:lombok:1.16.14'
}

追記:

今回の記事ではSpringloadedを使っていますが、Spring Boot Dev Toolが強いそうです。 Application.ymlを変更したら自動でアプリが再起動しました。びっくりします。

spring-boot-devtoolsで開発効率上げようぜ、的な。 (Spring Boot 1.3) - Qiita