3
7

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 + OAuth2でシングルサインオンSSO機能を実装してみる

Last updated at Posted at 2020-08-15

今日はシングルサインオンSSOのことをサンプルを作りながら説明します。

SSOとは

Single Sign Onの略。1つのIDとパスワードを入力して、複数のWebサービスやアプリケーションにログインする仕組み。入力や管理の手間を省き、セキュリティを強化することができる。
イメージは以下のように参照しましょう。
SSO流程图.png

SS0機能実装

本記事はサンプルプロジェクトを作って説明します。主にSpring Security Oauth2を活用してSSO機能を実現します。

環境準備

予めspring security、oauth2.0などの知識を持っていればよいです。

  • Eclipse
  • Java8
  • Spring
  • OAuth2.0

親プロジェクトを作る

mavenで親のプロジェクトを作りました。sso-oauth2-demoをネーミングします。
pom.xmlに下記のように配置します。

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>sso-oauth2-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
        <spring-boot.version>2.1.9.RELEASE</spring-boot.version>
        <spring-security.version>2.1.9.RELEASE</spring-security.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
            <version>${spring-security.version}</version>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

SSO認証サーバを作る

SSO認証サーバ配置

(1).上記の親プロジェクトの下に子供のモジュールを作って、auth-serverをネーミングします。
auth-serverプロジェクトのpom.xmlに下記のように配置します。

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>sso-oauth2-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <artifactId>auth-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>auth-server</name>
    <description>Demo project for Spring Boot</description>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

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

</project>

(2).application.ymlを作成して下記のように配置します。
ここでは、sso認証サーバーのポート(8300)を指定します。

application.yml
server:
  port: 8300
  servlet:
    context-path: '/auth'
SSO認証サーバ実装

(1).mainクラスを実装
   下記のとおり、実装してみよう。
@EnableResourceServerアノテーションをmainクラスの上に付けます。このアノテーションが重要です。

AuthServerApplication.java
@SpringBootApplication
@EnableResourceServer
public class  AuthServerApplication 
{
    public static void main( String[] args )
    {
     SpringApplication.run(AuthServerApplication.class, args);
    }
}

(2).authクラスを実装

AuthServerConfig.java

/**
 * SSO認証サーバー
 * @author hyman
 */
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    @Override
    public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.tokenKeyAccess("permitAll")
                .checkTokenAccess("isAuthenticated()");
    }

    @Override
    public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("SampleClientId")
                .secret(passwordEncoder.encode("secret"))
                .authorizedGrantTypes("authorization_code")
                .scopes("user_info")
                .autoApprove(true)
                .redirectUris("http://localhost:8301/login", "http://localhost:8302/login");
    }


}

(3).securityクラスを実装
ここでユーザidとパスワードはソースの中で定義します。

ユーザーID:hyman
パスワード:hyman123

SecurityConfig.java

/**
 * security配置
 * ここでユーザidとパスワードはソースの中で定義します。
 * @author hyman
 *
 */
@Configuration
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.requestMatchers()
                .antMatchers("/login", "/oauth/authorize")
                .and()
                .authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .permitAll()
                .and().csrf().disable();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("hyman") // ユーザーID:hyman
                .password(passwordEncoder().encode("hyman123"))  // パスワード:hyman123
                .roles("USER");
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

(3).SSO認証APIクラスを実装
SSO認証サーばーへアクセスURLはhttp://localhost:8300/auth/user/me となります。

UserAction.java
@RestController
@RequestMapping(value = "user")
public class UserAction {

    @GetMapping(value = "me")
    public Principal me(Principal principal) {
        System.out.println("アクセスユーザー情報:" + principal);
        return principal;
    }
}

ここまで、SSO認証サーバーの実装は終りました。パッケージなどの階層は以下のとおり、参照ください。

image.png

クライアントアプリケーションを作る

ここから、クライアントアプリケーションを作り始めます。本記事は二つのクライアントアプリケーションをつくります。client-aとclient-bです。

client-aアプリケーション
client-a配置

 (1).親のsso-oauth2-demoプロジェクトの下にclient-aモジュールを作成します。
 client-aモジュールのpom.xmlに下記のように配置します。

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>sso-oauth2-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <artifactId>client-a</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>client-a</name>
    <description>Demo project for Spring Boot</description>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        </dependency>
    </dependencies>

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

</project>

(2).application.xml配置
下記のとおり、client-aを配置します。
ここでclient-aのポートを8301で定義します。

application.xml
server:
  port: 8301
  servlet:
    session:
      cookie:
        name: CLIENT_A_SESSION

security:
  oauth2:
    client:
      client-id: SampleClientId
      client-secret: secret
      access-token-uri: http://localhost:8300/auth/oauth/token
      user-authorization-uri: http://localhost:8300/auth/oauth/authorize
    resource:
      user-info-uri: http://localhost:8300/auth/user/me # SSO認証サーバーのアドレス

spring:
  thymeleaf:
    cache: false
client-a実装

(1).securityクラスの実装
@EnableOAuth2Ssoアノテーションが重要ですから、忘れずに、クラスの上に付けます。

MySecurityConfig.java
@EnableOAuth2Sso
@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/**")
            .authorizeRequests()
            .antMatchers("/", "/login**")
            .permitAll()
            .anyRequest()
            .authenticated();
    }

}

(2).画面の実装
resourcesフォルダの下にtemplaesフォルダを作って、画面表示用のindex.htmlファイルを作ります。
下記の内容をindex.htmlに書き込みます。

index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Spring Security SSO</title>
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
</head>

<body>
<div class="container">
	<div class="col-sm-12">
		<h1>Spring Security SSO クライアントアプリケーションA</h1>
		<a class="btn btn-primary" href="securedPage">Login</a>
	</div>
</div>
</body>
</html>

上記と同じの場所でsecuredPage.htmlを作って下記のように実装します。

securedPage.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Spring Security SSO</title>
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
</head>

<body>
<div class="container">
	<div class="col-sm-12">
		<h1>Secured Page, Client A</h1>
		Welcome, <span th:text="${#authentication.name}">Name</span>
	</div>
</div>
</body>
</html>

(2).Controllerクラス実装

IndexAction.java
@Controller
public class IndexAction {

    @GetMapping(value = "")
    public String index() {
        System.out.println("ClientAのindex画面へ遷移する");
        return "index.html";
    }

    @GetMapping(value = "securedPage")
    public String home() {
        System.out.println("ClientAのsecuredPage画面へ遷移する");
        return "securedPage.html";
    }
}

ここまで行くと、client-aは終りました。

client-bアプリケーション

client-bアプリケーションの作り方はclient-aと同じですから、ここで略です。
ただ、client-bアプリケーションを作成する際にあるところでclient-bの名称を書き換えるが必要です。
たとえば、client-bのポートを8302に変わってます。

ここでSS0機能実装は全部完了しました。

SS0機能検証

まず、それぞれ三つのプロジェクトを起動します。

  • auth-server
  • client-a
  • client-b

あと、client-aをアクセスします。urlはhttp://localhost:8301となります。

image.png

Loginボタンをクリックします。Login画面でユーザーID(hyman)とパスワード(hyman123)を入力してログインします。
image.png

ログインができたら、securedPage画面へ行きます。
image.png

ここまで、client-aを経由してログインできましたので、SSO機能の影響でclient-bをアクセスするときにclient-bのindex画面でLoginボタンをクリックしてユーザーIDとパスワードを入力ずに、ログインできるはずです。

じゃあ、検証してみましょう。

http://localhost:8302/ を経由してclient-bをアクセスします。
image.png

Loginボタンをクリックすると、ログイン画面へ遷移しないでclient-bのsecuredPage画面にいきます。

image.png

だから、SSOの機能はうまくいけました。

最後に

最後まで読んでいただき、ありがとうございます。
ソースコードはこちらでダウンロードできます。

ソースコード:https://github.com/Hyman1993/Demo-Projects/tree/master/sso-oauth2-demo

3
7
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
3
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?