2-2. OIDC 연동 - Spring Framework
이번 게시글은 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. 생성
먼저, WSO2 의 콘솔로 접근하여 Service Provider (이하 SP) 를 등록해줍니다.
해당 과정에서는 SP를 등록하여 Client ID / Secret 값을 발급받아야 합니다.
사진과 같이, 콘솔 좌측의 표시된 부분인 Service Providers - Add를 클릭하면 그림과 같은 창이 뜨는데,
이후 예제로 사용할 이름을 입력해준 뒤, Register를 클릭해 해당 SP를 등록해줍니다.
1-2. 조회
이후 List를 클릭하면 다음과 같이 SP가 등록된걸 확인할 수 있는데, 이후 Edit를 눌러 해당 SP의 설정탭에 접근합니다.
1-3. 수정
기타 설정은 수정하지 않고, 하단의 Inbound Authentication Configuration - OAuth/OpenID Connect Configuration 탭의 Configure를 클릭해주면 우측 사진과 같은 화면이 출력되는데, 하단의 'Callback Url' 탭에 리다이렉트 할 URL을 작성해줍니다. 해당 예제에서는 "http://localhost:8080/login/oauth2/code/wso2" 를 입력해 주었습니다.
이후 설정값을 저장하면 다음과 같이 Client Key, Secret값이 발급됩니다.
2. 샘플소스 생성
2-1. 프로젝트 생성
다음 구성과 같이, 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"> </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. 메인페이지 접속
IDE 내에서 'Run as Spring boot App' 을 통해 해당 소스를 실행시킨 뒤 http://localhost:8080/login 으로 접속하면, 다음과 같은 화면이 출력됩니다. OAuth2LoginConfig.java 작성 당시 Repository 에 clientName 으로 등록한 'WSO2'를 확인할 수 있습니다. WSO2를 클릭하여 다음 단계인 로그인을 진행합니다.
3-2. 로그인
로그인 계정을 입력하면 다음과 같이 접근권한을 묻는 창이 나타납니다. OAuth2LoginConfig.java 작성 당시 Repository 에 scope 로 등록한 4개의 항목에 대한 접근권한을 묻는걸 확인할 수 있습니다. Allow를 눌러 다음 단계로 진행합니다.
3-3. 로그인 확인
정상적으로 로그인이 진행되었다면, 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