Tutorials & Tips/WSO2 Identity Server

2-2. OIDC 연동 - Spring Framework

PHAROS IAM 2022. 5. 12. 09:37

이번 게시글은 Spring Framework 기반의 어플리케이션을 WSO2의 OIDC와 연동하여 로그인을 진행하기 위한 과정입니다. 예제 실행 환경은 다음과 같습니다.

 

- Eclipse - Spring Framework 사용

- JDK 1.8

- Embedded Apache Tomcat (spring-boot-starter-tomcat-2.5.14)

- WSO2 IS 5.11, 샘플 소스 : 로컬환경 내 구축

 

1. Service Provider 등록

1-1. 생성

<그림 1> Service Provider - Add

먼저, WSO2 의 콘솔로 접근하여 Service Provider (이하 SP) 를 등록해줍니다.

해당 과정에서는 SP를 등록하여 Client ID / Secret 값을 발급받아야 합니다.

 

사진과 같이, 콘솔 좌측의 표시된 부분인 Service Providers - Add를 클릭하면 그림과 같은 창이 뜨는데,

이후 예제로 사용할 이름을 입력해준 뒤, Register를 클릭해 해당 SP를 등록해줍니다.

 

1-2. 조회

<그림 2> Service Provider - List

이후 List를 클릭하면 다음과 같이 SP가 등록된걸 확인할 수 있는데, 이후 Edit를 눌러 해당 SP의 설정탭에 접근합니다.

 

1-3. 수정

<그림 3> Service Provider - Edit (좌) / <그림 4> OAuth/OpenID Configuration (우)

기타 설정은 수정하지 않고, 하단의 Inbound Authentication Configuration - OAuth/OpenID Connect Configuration 탭의 Configure를 클릭해주면 우측 사진과 같은 화면이 출력되는데, 하단의 'Callback Url' 탭에 리다이렉트 할 URL을 작성해줍니다. 해당 예제에서는 "http://localhost:8080/login/oauth2/code/wso2" 를 입력해 주었습니다.

 

<그림 5> Client ID, Client Secret

이후 설정값을 저장하면 다음과 같이 Client Key, Secret값이 발급됩니다.

 

2. 샘플소스 생성

2-1. 프로젝트 생성

<그림 6> 프로젝트 생성

 다음 구성과 같이, Spring Web, OAuth2 Client, Thymeleaf가 포함된 프로젝트를 생성하고, workspace로 import 합니다. 해당 예제에서는 start.spring.io 에서 프로젝트를 생성했습니다.

 

2-2. 라이브러리 추가 (pom.xml)

<!--Additional Dependencies-->
<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-oauth2-client</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
<dependency>
	<groupId>org.thymeleaf.extras</groupId>
	<artifactId>thymeleaf-extras-springsecurity4</artifactId>
	<version>2.1.2.RELEASE</version>
</dependency>

pom.xml에서 다음과 같이 라이브러리를 추가해줍니다.

 

 

2-3. Controller 생성 (IndexController.java)

package com.pharosinfo.oauth2loginsample;

import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class IndexController {
    @GetMapping("/")
    public String getUserName(Model model, OAuth2AuthenticationToken token) {
        model.addAttribute("userName", token.getPrincipal().getName());
        return "index";
    }
}

로그인 후 화면을 출력하기 위해 indexController class를 생성해줬습니다. index 페이지에서는 로그인한 사용자의 ID를 출력하고자 하므로, View에 매핑할 userName에 token.getPrincipal().getName() 을 작성해줍니다.

 

2-4. View 생성 (index.html)

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>OAuth2 Login </title>
</head>
<body>
<div style="float: right">
    <div style="float:left">
        <span style="font-weight:bold">User: <span th:text=${userName}></span> </span>
    </div>
    <div style="float:none">&nbsp;</div>
    <div style="float:right">
        <form th:action="@{/logout}" method="post">
            <input type="submit" value="Logout"/>
        </form>
    </div>
</div>
<h1>OAuth 2.0 Login with Spring Security</h1>
<div>
    You are successfully logged in as <span style="font-weight:bold" th:text="${userName}"></span>
</div>
</body>
</html>

로그인 성공 시 리턴될 View 입니다. 예시문 하단의 span 태그 안에 IndexController.java 로부터 userName이 매핑되도록 작성해줍니다.

 

2-5. 환경변수 추가 (application.properties)

#필수값--------------------------
spring.thymeleaf.cache=false
spring.security.oauth2.client.registration.wso2.client-id=<발급받은 Client ID 입력>
spring.security.oauth2.client.registration.wso2.client-secret=<발급받은 Client Secret 입력>
spring.security.oauth2.client.provider.wso2.authorization-uri=http://localhost:9763/oauth2/authorize
spring.security.oauth2.client.provider.wso2.token-uri=http://localhost:9763/oauth2/token
spring.security.oauth2.client.provider.wso2.user-info-uri=http://localhost:9763/oauth2/userinfo
spring.security.oauth2.client.provider.wso2.jwk-set-uri=http://localhost:9763/oauth2/jwks

#선택값--------------------------for tomcat logfiles
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.suffix=.log
server.tomcat.accesslog.prefix=access_log
server.tomcat.accesslog.file-date-format=.yyyy-MM-dd
server.tomcat.basedir=tomcat
server.tomcat.accesslog.directory=logs
server.tomcat.accesslog.pattern=common

#선택값--------------------------for view internal logs of tomcat
logging.level.org.apache.tomcat=DEBUG
logging.level.org.apache.catalina=DEBUG

프로젝트 내의 application.properties 파일에 다음과 같이 Client ID, Secret, url 등 연결에 필요한 내용을 추가해줍니다. 선택값으로 표시된 server.tomcat과 관련된 설정은 디버깅을 위한 옵션으로, 필수사항은 아닙니다.

 

2-6. Repository 작성 (OAuth2LoginConfig.java)

package com.pharosinfo.oauth2loginsample;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;

@Configuration
public class OAuth2LoginConfig {
    private static String CLIENT_PROPERTY_KEY = "spring.security.oauth2.client.registration.wso2.";
    private static String PROVIDER_PROPERTY_KEY = "spring.security.oauth2.client.provider.wso2.";
    
    @Autowired
    private Environment env;

    @Bean
    public ClientRegistrationRepository
    clientRegistrationRepository() {
        return new InMemoryClientRegistrationRepository(this.WSO2ClientRegistration());
    }

    private ClientRegistration WSO2ClientRegistration() {
        return ClientRegistration.withRegistrationId("wso2")
                .clientId(env.getProperty(CLIENT_PROPERTY_KEY + "client-id"))
                .clientSecret(env.getProperty(CLIENT_PROPERTY_KEY + "client-secret"))
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .redirectUri("{baseUrl}/login/oauth2/code/{registrationId}")
                .scope("openid", "profile", "email", "address", "phone")
                .authorizationUri(env.getProperty(PROVIDER_PROPERTY_KEY + "authorization-uri"))
                .tokenUri(env.getProperty(PROVIDER_PROPERTY_KEY + "token-uri"))
                .userInfoUri(env.getProperty(PROVIDER_PROPERTY_KEY + "user-info-uri"))
                .userNameAttributeName(IdTokenClaimNames.SUB)
                .jwkSetUri(env.getProperty(PROVIDER_PROPERTY_KEY + "jwk-set-uri"))
                .clientName("WSO2")
                .build();
    }
}

WSO2와 연결하기 위해 ClientRegistration 라이브러리를 사용, 환경변수를 참고하여 각 필수값을 설정해줍니다. 해당 함수에서는 scope, 로그인 페이지에 출력되는 clientName 등을 입력할 수 있습니다.

 

3. 실행

3-1. 메인페이지 접속

<그림 7> Client 선택 페이지

IDE 내에서 'Run as Spring boot App' 을 통해 해당 소스를 실행시킨 뒤 http://localhost:8080/login 으로 접속하면, 다음과 같은 화면이 출력됩니다. OAuth2LoginConfig.java 작성 당시 Repository 에 clientName 으로 등록한 'WSO2'를 확인할 수 있습니다. WSO2를 클릭하여 다음 단계인 로그인을 진행합니다.

 

 

3-2. 로그인

<그림 8> WSO2 로그인 페이지 (좌) / <그림 9> 접근권한 확인 페이지 (우)

로그인 계정을 입력하면 다음과 같이 접근권한을 묻는 창이 나타납니다. OAuth2LoginConfig.java 작성 당시 Repository 에 scope 로 등록한 4개의 항목에 대한 접근권한을 묻는걸 확인할 수 있습니다. Allow를 눌러 다음 단계로 진행합니다.

 

3-3. 로그인 확인

<그림 10> 메인 페이지 (index.html)

정상적으로 로그인이 진행되었다면, index.html로 이동하며 사용자 ID가 페이지에 출력되는걸 확인할 수 있습니다.

 

 

 

4. 참고링크

 

OAuth2 Login for Spring Boot Application with WSO2 Identity Server

Authentication and authorization is a crosscutting concern of application development. Outsourcing the authentication to 3 rd party…

medium.com