25
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Spring Security 5.xでOAuth 2.0のリソースサーバーを作る(JWT編)

Last updated at Posted at 2018-09-08

Spring Security 5.1から、OAuth 2.0のリソースサーバー作成機能が追加されましたので、紹介します。

2018-11-06 改訂: Spring Boot 2.1が正式リリースされましたので改訂しました!
2019-12-03 改訂: Spring Boot 2.2が正式リリースされましたので改訂しました!

依存ライブラリ

今回はSpring Bootで作っていきます。

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
    ...

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <java.version>11</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

特に重要なのがこれです。Spring Boot 2.2から、リソースサーバー用のStarterライブラリが追加されました。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
        </dependency>

クライアント用のStarterもあります(spring-boot-starter-oauth2-client)

application.propertiesの設定

必要な設定は2つだけです。

application.properties
# 認可サーバーのIssuer Identifier
spring.security.oauth2.resourceserver.jwt.issuer-uri: http://localhost:9000/auth/realms/todo-api
# 認可サーバーのJWK Setが返ってくるURL
spring.security.oauth2.resourceserver.jwt.jwk-set-uri: http://localhost:9000/auth/realms/todo-api/protocol/openid-connect/certs

手元の認可サーバーは、Keycloakサーバーをポート番号9000で立てています。

これらのプロパティは、どちらか一方でOKです。 issuer-uri が使える認可サーバー(OpenID Connectに対応したサーバー)を使うと、こちらだけ設定すれば jwk-set-uri は自動で設定されます。

(リソースサーバー起動時に認可サーバーにアクセスして、 jwk-set-uri をもらいます。)

Java Configの記述

SecurityConfig.java
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .mvcMatchers(HttpMethod.GET, "/hello").hasAuthority("SCOPE_hello:read")
                .anyRequest().authenticated();
        http.oauth2ResourceServer()
                .jwt();
    }
}

特に重要なポイントは、URLとスコープのマッピングの部分です。

.mvcMatchers(HttpMethod.GET, "/hello").hasAuthority("SCOPE_hello:read")

hasAuthority() メソッドでスコープを指定します。

スコープは SCOPE_スコープ名 と指定します。

実行

「Hello!」を返すだけの簡単なRestControllerを作っておきます。

HelloController.java
@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello() {
        return "Hello!";
    }
}

アクセストークン無しで実行すると401が返ってきます。

アクセストークン無しで実行
$ curl -v -X GET http://localhost:8090/hello
> GET /hello HTTP/1.1
> Host: localhost:8090
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 401
< WWW-Authenticate: Bearer
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Length: 0
< Date: Sat, 08 Sep 2018 07:27:50 GMT
<

アクセストークンを指定して実行すると、200で「Hello!」が返ってきます。

アクセストークン(JWT)を指定して実行
$ curl -v -X GET -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJzbGcyMG9uOVg2czZFOExmNDZfQmRuaExHQy1xZnIyMVlvWE9nQVFKRlIwIn0.eyJqdGkiOiJlNzhkYTg0Yy1iZDYxLTQyY2YtOGJlYi05MGFhYTQ0NzQ5YzUiLCJleHAiOjE1MzYzOTE3NDEsIm5iZiI6MCwiaWF0IjoxNTM2MzkxNDQxLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjkwMDAvYXV0aC9yZWFsbXMvaGVsbG8tYXBpIiwiYXVkIjoidHJhaW5pbmc2LWZyb250LXNlcnZpY2UiLCJzdWIiOiIxYWI5Yjg4Ny0yNDRhLTRjZTktYTBjMy1iZTc2ZGE4NzZiMTQiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0cmFpbmluZzYtZnJvbnQtc2VydmljZSIsImF1dGhfdGltZSI6MCwic2Vzc2lvbl9zdGF0ZSI6ImJlMTQ4MmFkLTc0YjAtNGY3OS1iNjkwLWEzOTFmOTliYzkxZiIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cDovL2xvY2FsaG9zdDo4MDgwIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJoZWxsbzpyZWFkIHByb2ZpbGUiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ1c2VyIn0.IfSf25zltXHu550TMrnp8O1We-vLx4O8b74ooLFUC5CLsWSHQ8rqG4JwqUX_LYDhVaameSy5ix3eRLNTAkjXc24WqsS856zGy2ULxxh7YItQ0CZX3qa7GxZ2Acv7nAkJeAE6eG_6B68o7H4MqdSywDA4qy4tNL4UF7wKQ5IJcMggYPQYUh45GchsDiF1h27ePDeUaUPLMHW04sqxhBHzsOaaVubglYWIG4BCkDwEk4JLNmd0mYBS4mXmYgmKx9_gW1NQasXfd4vLkYVksJIyncY7xk2QPDImmu6ip0_QXH5pIYr7K13DVe_Tl8Xc2ob_9OWODIYOBqYJzDU5XI9ClA" http://localhost:8090/hello
> GET /hello HTTP/1.1
> Host: localhost:8090
> User-Agent: curl/7.54.0
> Accept: */*
> Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJzbGcyMG9uOVg2czZFOExmNDZfQmRuaExHQy1xZnIyMVlvWE9nQVFKRlIwIn0.eyJqdGkiOiJlNzhkYTg0Yy1iZDYxLTQyY2YtOGJlYi05MGFhYTQ0NzQ5YzUiLCJleHAiOjE1MzYzOTE3NDEsIm5iZiI6MCwiaWF0IjoxNTM2MzkxNDQxLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjkwMDAvYXV0aC9yZWFsbXMvaGVsbG8tYXBpIiwiYXVkIjoidHJhaW5pbmc2LWZyb250LXNlcnZpY2UiLCJzdWIiOiIxYWI5Yjg4Ny0yNDRhLTRjZTktYTBjMy1iZTc2ZGE4NzZiMTQiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0cmFpbmluZzYtZnJvbnQtc2VydmljZSIsImF1dGhfdGltZSI6MCwic2Vzc2lvbl9zdGF0ZSI6ImJlMTQ4MmFkLTc0YjAtNGY3OS1iNjkwLWEzOTFmOTliYzkxZiIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cDovL2xvY2FsaG9zdDo4MDgwIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJoZWxsbzpyZWFkIHByb2ZpbGUiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ1c2VyIn0.IfSf25zltXHu550TMrnp8O1We-vLx4O8b74ooLFUC5CLsWSHQ8rqG4JwqUX_LYDhVaameSy5ix3eRLNTAkjXc24WqsS856zGy2ULxxh7YItQ0CZX3qa7GxZ2Acv7nAkJeAE6eG_6B68o7H4MqdSywDA4qy4tNL4UF7wKQ5IJcMggYPQYUh45GchsDiF1h27ePDeUaUPLMHW04sqxhBHzsOaaVubglYWIG4BCkDwEk4JLNmd0mYBS4mXmYgmKx9_gW1NQasXfd4vLkYVksJIyncY7xk2QPDImmu6ip0_QXH5pIYr7K13DVe_Tl8Xc2ob_9OWODIYOBqYJzDU5XI9ClA
>
< HTTP/1.1 200
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 6
< Date: Sat, 08 Sep 2018 07:26:02 GMT
<
Hello!

サンプルコード

参考資料

25
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?