Spring Boot 1.5.x環境でのGitHub OAuthを用いたSSO

# やりたいこと
Spring Boot 1.5.15環境でOAuth2.0によるシングルサインオン(SSO)を実現したい．
認可サーバとしてGitHub OAuthを利用したい．

# 環境
Spring Boot 1.5.15 + JDK 1.8 + Maven

# 利用した技術について
## Spring Security Web
https://spring.io/projects/spring-security
クライアントとWebアプリケーションの間のSecurity Filter Chainを提供する．
各Filterでは認証されていないユーザのアクセス拒否などのフィルタリングが可能．
Security Filter ChainはFilter Chain Proxyで管理する．

Spring Securityの設定はWebSecurityConfigurerAdapterというデフォルト設定クラスを継承し，必要なものをオーバーライドする．

公式リファレンス
https://docs.spring.io/spring-security/site/docs/4.2.5.RELEASE/apidocs/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.html

```java:SecurityConfig.java
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
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;

@Configuration // 設定クラスを自動的に読み込む
@EnableWebSecurity // Spring Securityを有効化
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(WebSecurity web) throws Exception {
        // WebSecurityのメソッドチェーンでFilter Chain Proxyの設定（≒全体的な設定）を記述
        ...
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // HttpSecurityのメソッドチェーンでFilter Chainの設定（≒細かい設定）を記述
        ...
    }
}
```

Thymeleafでユーザ情報を利用するにはthymeleaf-extras-springsecurityを利用する．
https://github.com/thymeleaf/thymeleaf-extras-springsecurity

## OAuth2.0
https://tools.ietf.org/html/rfc6749
https://openid-foundation-japan.github.io/rfc6749.ja.html

認可（Authorization, AuthZ）のためのフレームワーク．
OAuth2.0の登場人物は**ユーザ**，**認可サーバ**，**リソースサーバ**，**クライアント**の４つ．
**ユーザ**は**認可サーバ**を通じて，**リソースサーバ**にあるリソースの使用を**クライアント**に認可する．

以下の記事が非常にわかりやすくまとめている．
https://qiita.com/busyoumono99/items/1092fdc64d5a64d021d5

## OAuth2.0によるSSO
OAuth2.0において，リソースサーバからユーザ情報を取得して照合することで認証（Authentication, AuthN）が実現できる．
GitHub OAuthによるSSOの場合は

* **ユーザ ＝ 人**
* **認可サーバ ＝ GitHub**
* **リソースサーバ ＝ GitHub**
* **クライアント ＝ Webアプリケーション**

となる．

Spring Securityで利用するにはSpring Security OAuthを利用する．
`application.properties`の`security.oauth2.resource.*`にリソースの設定を，`security.oauth2.client.*`にクライアントの設定を記述する．
https://spring.io/projects/spring-security-oauth

# 実装
リソースURIは`/demo`．

```xml:pom.xml#dependencies
	<dependencies>
		<!-- Web application -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<!-- OAuth2.0 SSO -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.security.oauth</groupId>
			<artifactId>spring-security-oauth2</artifactId>
		</dependency>
		<dependency>
			<groupId>org.thymeleaf.extras</groupId>
			<artifactId>thymeleaf-extras-springsecurity4</artifactId>
		</dependency>
		<!-- Lombok -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
	</dependencies>
```
```java:DemoApplication.java
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);
	}
}
```
```java:DemoController.java
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class DemoController {
    @GetMapping("/")
    public String home() {
        return "home";
    }
}
```
```xml:home.html
<html xmlns:th="http://www.thymeleaf.org"
  xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="utf-8"/>
<title>Home</title>
</head>
<body>
  <h1>Hello, <span sec:authentication="name"></span></h1>
</body>
</html>
```
```java:SecurityConfig.java
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
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;

@Configuration
@EnableWebSecurity
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .anyRequest().authenticated(); // 全てのURIへのアクセスにログインが必要
    }
}
```
```properties:application.properties
# 改行までが値と見なされることに注意

server.port=8080
server.context-path=/demo

# OAuth2クライアント（本アプリケーション）の設定

# クライアントクレデンシャル
security.oauth2.client.client-id=${DEMO__GITHUB_OAUTH_CLIENT_ID}
security.oauth2.client.client-secret=${DEMO__GITHUB_OAUTH_CLIENT_SECRET}
# アクセストークン取得URI
security.oauth2.client.access-token-uri=https://github.com/login/oauth/access_token
# 認可URI
security.oauth2.client.user-authorization-uri=https://github.com/login/oauth/authorize
# 認証スキーマ
security.oauth2.client.client-authentication-scheme=form

# OAuth2リソースサーバ（GitHub）の設定

# ユーザ情報取得URI
security.oauth2.resource.user-info-uri=https://api.github.com/user
# アクセストークンではなくリソースからユーザ情報を取得
security.oauth2.resource.prefer-token-info=false

# OAuth2によるSSOの設定

# SSOログインURI（非認証時のリダイレクトURI）
security.oauth2.sso.login-path=/login
```

# ハマったこと
* application.propertiesでは行頭が`#`または`!`のときコメント行と見なされる．
  `key=value # comment`のように値に続けてコメントを書くことはできない．
* spring-security-oauth2-autoconfigureはSpring Boot 1系では使えない．