Spring Security を使って Basic 認証 を実装してみた

Spring Boot で実装している Web API に、Spring Security を使って認証を追加することにした。 最終的には OAuth にする予定だけど、今はまだ検証用のプロトタイプを作っている段階なので、とりあえず Basic 認証で。

まず build.gradle を修正して、Spring Security を追加する。

buildscript {
    ext {
        springBootVersion = '1.5.4.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    testCompile('org.springframework.boot:spring-boot-starter-test')

    // Spring Security を追加
    compile('org.springframework.boot:spring-boot-starter-security')    
}

Basic 認証を組み込んだ、最小の Spring Boot アプリケーションがこちら。

package jp.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class BasicAuthExampleApplication {

    public static void main(String[] args) {
        SpringApplication.run(BasicAuthExampleApplication.class, args);
    }
    
    @RestController
    public static class HomeController {
        @GetMapping("/api/goodmorning")
        public String goodMorning() {
            return "Good morning";
        }
        
        @GetMapping("/api/hello")
        public String hello() {
            return "Hello world";
        }
    }
    
    @Configuration
    @EnableWebSecurity
    public static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
            authenticationManagerBuilder.inMemoryAuthentication()
                .withUser("test_user").password("test_pass").roles("USER");
        }
        
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.httpBasic() // Basic 認証を有効にする
                .and()
                .authorizeRequests()
                    .antMatchers("/api/goodmorning") // /api/goodmorning は認証不要
                    .permitAll()                
                .anyRequest()
                    .authenticated(); // それ以外は認証必須
        }        
    }
}

Python の REPL で動作確認。

>>> import requests
>>> requests.get("http://localhost:8080/api/goodmorning").text
u'Good morning'
>>> requests.get("http://localhost:8080/api/hello").text
u'{"timestamp":1498106201915,"status":401,"error":"Unauthorized","message":"Full authentication is required to access this resource","path":"/api/hello"}'
>>> requests.get("http://localhost:8080/api/hello", auth=("test_user", "test_pass")).text
u'Hello world'