본문으로 바로가기

728x90

Spring Boot with OAuth2 - 1. OAuth2 Server 에서 구현한 코드를 확장하는 방법에 대해서 설명합니다.

 

OAuth2 Server 확장

실제 운영을 위해서는 InMemory방식에서 데이터베이스를 이용한 확장이 필요합니다.

본 글에서는 OAuth Server와 데이터베이스 연결을 설명합니다.

 

  • 데이터베이스 연결(MariaDB + myBatis)
    • AuthorizationCodeServices 데이터베이스 연결
    • ApprovalStore 데이터베이스 연결
    • TokenStore 데이터베이스 연결
    • ClientDetailsService 데이터베이스 연결

 

데이터베이스 테이블 생성(MariaDB + MyBatis)

MyBatis를 통하여 MariaDB에 접속하는 예제를 구현합니다. 이 후의 글에서는 본 프로젝트를 기준으로 설명합니다.

 

데이터베이스 연결 및 설정은 "Spring Boot - MyBatis & log4jdbc 설정"를 참고하십시오.

 

아래 SQL문을 실행하여 데이터베이스 테이블을 생성합니다.

CREATE TABLE `oauth_access_token` (
  `token_id` varchar(256) DEFAULT NULL,
  `token` varchar(4096) DEFAULT NULL,
  `authentication_id` varchar(256) NOT NULL,
  `username` varchar(256) DEFAULT NULL,
  `client_id` varchar(256) DEFAULT NULL,
  `authentication` varchar(4096) DEFAULT NULL,
  `refresh_token` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`authentication_id`)
);

CREATE TABLE `oauth_approvals` (
  `userId` varchar(256) DEFAULT NULL,
  `clientId` varchar(256) DEFAULT NULL,
  `scope` varchar(256) DEFAULT NULL,
  `status` varchar(10) DEFAULT NULL,
  `expiresAt` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  `lastModifiedAt` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00'
);

CREATE TABLE `oauth_client_details` (
  `client_id` varchar(256) NOT NULL,
  `resource_ids` varchar(256) DEFAULT NULL,
  `client_secret` varchar(256) DEFAULT NULL,
  `scope` varchar(256) DEFAULT NULL,
  `authorized_grant_types` varchar(256) DEFAULT NULL,
  `redirect_uris` varchar(256) DEFAULT NULL,
  `authorities` varchar(256) DEFAULT NULL,
  `access_token_validity` int(11) DEFAULT NULL,
  `refresh_token_validity` int(11) DEFAULT NULL,
  `additional_information` varchar(4096) DEFAULT NULL,
  `auto_approve` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`client_id`)
);

CREATE TABLE `oauth_code` (
  `code` varchar(256) DEFAULT NULL,
  `authentication` varchar(4096) DEFAULT NULL
);

CREATE TABLE `oauth_refresh_token` (
  `token_id` varchar(256) DEFAULT NULL,
  `token` varchar(4096) DEFAULT NULL,
  `authentication` varchar(4096) DEFAULT NULL
);

CREATE TABLE `oauth_user_details` (
  `username` varchar(256) NOT NULL,
  `password` varchar(256) NOT NULL,
  `enabled` tinyint(1) DEFAULT NULL,
  `authority` varchar(256) DEFAULT NULL,
  `account_non_expired` tinyint(1) DEFAULT NULL,
  `account_non_locked` tinyint(1) DEFAULT NULL,
  `credentials_non_expired` tinyint(1) DEFAULT NULL,
  `name` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`username`)
);

아래의 SQL문을 수행하여 기본 데이터를 삽입합니다.

insert  into `oauth_client_details`(`client_id`,`resource_ids`,`client_secret`,`scope`,`authorized_grant_types`,`redirect_uris`,`authorities`,`access_token_validity`,`refresh_token_validity`,`additional_information`,`auto_approve`) values ('client',NULL,'{bcrypt}$2a$10$goA9F/Q./Ml8lYvuO1tj6OKA5K6VVM/jmUcdIp1AMzqtXHsuo68/W','read, write, read_profile','authorization_code, implicit, password, client_credentials, refresh_token','http://localhost:9000/callback','ROLE_YOUR_CLIENT',36000,2592000,NULL,'true');

insert  into `oauth_user_details`(`username`,`password`,`enabled`,`authority`,`account_non_expired`,`account_non_locked`,`credentials_non_expired`,`name`) values ('admin','{noop}pass',1,'user,admin',1,1,1,'관리자');
insert  into `oauth_user_details`(`username`,`password`,`enabled`,`authority`,`account_non_expired`,`account_non_locked`,`credentials_non_expired`,`name`) values ('user','{noop}pass',1,'user',1,1,1,'사용자');

 

ClientDetailsService 구현

ClientDetailsService 는 OAuth를 사용하려는 제3의 어플리케이션을 관리하는 서비스입니다. 아래의 내용에서는 제3의 어플리케이션을 데이터베이스를 통해 관리하는 방법에 대해 설명합니다.

ClientDetailsService 생성

ClientDetailsService 인터페이스를 확장하여 새로운 어플리케이션관리 서비스를 생성합니다.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.ClientRegistrationException;
import org.springframework.stereotype.Service;

import kr.ejsoft.oauth2.server.dao.OAuthClientDetailsDAO;
import kr.ejsoft.oauth2.server.model.OAuthClientDetails;

@Service("oauthClientDetailsService")
public class OAuthClientDetailsService implements ClientDetailsService {
	private static final Logger log = LoggerFactory.getLogger(OAuthClientDetailsService.class);

	@Autowired
	@Qualifier("oauthClientDetailsDAO")
	private OAuthClientDetailsDAO clientDetailsDAO;

	@Override
	public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
		log.debug("Client Id : {}", clientId);
		OAuthClientDetails client = clientDetailsDAO.getClientById(clientId);
		if (client == null) {
			log.debug("Client : null");
			throw new ClientRegistrationException(clientId);
		}
		
		log.debug("Client : {}", client.toString());
		return client;
	}
}

ClientDetailsDAO 생성

데이터베이스에서 어플리케이션 정보를 읽어오는 코드를 작성합니다.

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import kr.ejsoft.oauth2.server.model.OAuthClientDetails;
import kr.ejsoft.oauth2.server.model.OAuthUserDetails;

@Repository("oauthClientDetailsDAO")
public class OAuthClientDetailsDAO {
	@Autowired
	private SqlSessionTemplate sqlSession;
	
	public OAuthClientDetails getClientById(String username) {
		return sqlSession.selectOne("client.selectClientById", username);
	}
}

ClientDetails 생성

어플리케이션 정보를 저장하기위한 ClientDetails 인터페이스를 구현한 model 객체를 생성합니다.

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.provider.ClientDetails;

@SuppressWarnings("serial")
public class OAuthClientDetails implements ClientDetails {

	private String clientId;
	private String clientSecret;
	private String scope;
	private String resourceIds;
	private boolean secretRequired;
	private boolean scoped;
	private String authorizedGrantTypes;
	private String redirectUris ;
	private String authorities;
	private String additionalInformation;
	private int accessTokenValidity;
	private int refreshTokenValidity;
	private boolean autoApprove;
	
	@Override
	public String getClientId() {
		return clientId;
	}

	@Override
	public String getClientSecret() {
		return clientSecret;
	}
	
	public String getPassword() {
		return clientSecret;
	}

	@Override
	public Set<String> getResourceIds() {
		return toSet(resourceIds);
	}

	@Override
	public boolean isSecretRequired() {
		return secretRequired;
	}

	@Override
	public boolean isScoped() {
		return scoped;
	}

	@Override
	public Set<String> getScope() {
		return toSet(scope);
	}

	@Override
	public Set<String> getAuthorizedGrantTypes() {
		return toSet(authorizedGrantTypes);
	}

	@Override
	public Set<String> getRegisteredRedirectUri() {
		return toSet(redirectUris);
	}
	
	@Override
	public Collection<GrantedAuthority> getAuthorities() {
		ArrayList<GrantedAuthority> auth = new ArrayList<GrantedAuthority>();
		auth.add(new SimpleGrantedAuthority(authorities));
		return auth;
	}

	@Override
	public Integer getAccessTokenValiditySeconds() {
		return accessTokenValidity;
	}

	@Override
	public Integer getRefreshTokenValiditySeconds() {
		return refreshTokenValidity;
	}

	@Override
	public boolean isAutoApprove(String scope) {
		return autoApprove;
	}

	@Override
	public Map<String, Object> getAdditionalInformation() {
//		return additionalInformation;
		return null;
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder
			.append("OAuthClientDetails [")
			.append("clientId=").append(clientId)
			.append(", clientSecret=").append(clientSecret)
			.append(", scope=").append(scope)
			.append(", resourceIds=").append(resourceIds)
			.append(", secretRequired=").append(secretRequired)
			.append(", scoped=").append(scoped)
			.append(", authorizedGrantTypes=").append(authorizedGrantTypes)
			.append(", authorities=").append(authorities)
			.append(", redirectUris=").append(redirectUris)
			.append(", accessTokenValidity=").append(accessTokenValidity)
			.append(", refreshTokenValidity=").append(refreshTokenValidity)
			.append(", additionalInformation=").append(additionalInformation)
			.append("]");
		return builder.toString();
	}

	
	private static Set<String> toSet(String data) {
		if(data == null) return null;
		String[] arr = data.split(",");
		if(arr != null && arr.length > 0) {
			Set<String> set = new HashSet<>();
//			Collections.addAll(set, arr);
			
			for(String e : arr) {
				if(e == null || "".equals(e)) continue;
				set.add(e.trim());
			}
			return set;
		}
		return null;
	}
}

client_sql.xml 생성

어플리케이션 정보를 읽어오는 데이터베이스 질의문를 생성합니다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="client">
	<select id="selectClientById" resultType="kr.ejsoft.oauth2.server.model.OAuthClientDetails">
		<![CDATA[
		SELECT
		    *
		FROM
		    oauth_client_details
		WHERE
		    client_id = #{clientId}
		]]>
	</select>
</mapper>

AuthorizationServerConfig 수정

어플리케이션 관리 서비스에 접속하기 위한 코드를 아래와 같이 수정합니다. inMemory방식은 더 이상 사용하지 않습니다.

	@Autowired
	@Qualifier("oauthClientDetailsService")
	private ClientDetailsService clientDetailsService;
   
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//		아래의 코드는 더 이상 사용되지 않습니다.
//		clients
//			.inMemory()
//			.withClient("client")
////			.secret("{bcrypt}$2a$10$goA9F/Q./Ml8lYvuO1tj6OKA5K6VVM/jmUcdIp1AMzqtXHsuo68/W")		// secret
//			.secret("{noop}secret")		// secret
//			.redirectUris("http://localhost:9000/callback")
////			.authorizedGrantTypes("authorization_code")
////			.authorizedGrantTypes("authorization_code", "implicit")
////			.authorizedGrantTypes("authorization_code", "implicit", "password")
////			.authorizedGrantTypes("authorization_code", "implicit", "password", "client_credentials")
//			.authorizedGrantTypes("authorization_code", "implicit", "password", "client_credentials", "refresh_token")
//			.accessTokenValiditySeconds(120)
//			.refreshTokenValiditySeconds(240)
//			.scopes("read_profile");
		clients.withClientDetails(clientDetailsService);
	}

 

UserDetailsService 구현

UserDetailsService 는 OAuth에 등록된 사용자의 인증정보를 관리하는 서비스입니다. 아래의 내용에서는 데이터베이스를 통해 사용자를 관리하는 방법에 대해 설명합니다.

UserDetailsService 생성

UserDetailsService 인터페이스를 구현한 사용자 관리 서비스를 아래와 같이 생성합니다.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import kr.ejsoft.oauth2.server.dao.OAuthUserDetailsDAO;
import kr.ejsoft.oauth2.server.model.OAuthUserDetails;

@Service("oauthUserDetailsService")
public class OAuthUserDetailsService implements UserDetailsService {
	private static final Logger log = LoggerFactory.getLogger(OAuthUserDetailsService.class);

	@Autowired
	@Qualifier("oauthUserDetailsDAO")
	private OAuthUserDetailsDAO userAuthDAO;

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		OAuthUserDetails user = userAuthDAO.getUserById(username);
		if (user == null) {
			throw new UsernameNotFoundException(username);
		}
		log.debug("User : {}", user.toString());
		return user;
	}
}

UserDetailsDAO 생성

데이터베이스에서 사용자정보를 읽어오는 객체를 생성합니다.

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import kr.ejsoft.oauth2.server.model.OAuthUserDetails;

@Repository("oauthUserDetailsDAO")
public class OAuthUserDetailsDAO {
	@Autowired
	private SqlSessionTemplate sqlSession;
	
	public OAuthUserDetails getUserById(String username) {
		return sqlSession.selectOne("user.selectUserById", username);
	}
}

UserDetails 생성

UserDetails 인터페이스를 구현한 사용자정보 저장객체를 생성합니다.

import java.util.ArrayList;
import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import lombok.Data;

@SuppressWarnings("serial")
public @Data class OAuthUserDetails implements UserDetails {

	private String username;
	private String password;
	private String authority;
	private boolean enabled;
	private boolean accountNonExpired;
	private boolean accountNonLocked;
	private boolean credentialsNonExpired;
	private String name;

	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		ArrayList<GrantedAuthority> auth = new ArrayList<GrantedAuthority>();
		auth.add(new SimpleGrantedAuthority(authority));
		return auth;
	}

	@Override
	public String getPassword() {
		return password;
	}

	@Override
	public String getUsername() {
		return username;
	}

	@Override
	public boolean isAccountNonExpired() {
		return accountNonExpired;
	}

	@Override
	public boolean isAccountNonLocked() {
		return accountNonLocked;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		return credentialsNonExpired;
	}

	@Override
	public boolean isEnabled() {
		return enabled;
	}

	public String getName() {
		return name;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder
			.append("OAuthUserDetails [")
			.append("username=").append(username)
			.append(", password=").append(password)
			.append(", authority=").append(authority)
			.append(", enabled=").append(enabled)
			.append(", accountNonExpired=").append(accountNonExpired)
			.append(", accountNonLocked=").append(accountNonLocked)
			.append(", credentialsNonExpired=").append(credentialsNonExpired)
			.append(", name=").append(name)
			.append("]");
		return builder.toString();
	}

}

user_sql.xml 생성

사용자 정보를 읽어오기 위한 데이터베이스 질의문을 생성합니다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="user">
	<select id="selectUserById" resultType="kr.ejsoft.oauth2.server.model.OAuthUserDetails">
		<![CDATA[
		SELECT
		    *
		FROM
		    oauth_user_details
		WHERE
		    username = #{username}
		]]>
	</select>
</mapper>

WebSecurityConfig 수정

기존의 InMemory방식은 더 이상 사용하지 않습니다. 기존 코드를 주석처리하거나 삭제하십시오.

//	아래의 코드들은 더 이상 사용되지 않습니다.
//	@Bean
//	public PasswordEncoder passwordEncoder() {
//		return PasswordEncoderFactories.createDelegatingPasswordEncoder();
//	}

//	@Bean
//	public UserDetailsService userDetailsService() {
////		PasswordEncoder encoder = passwordEncoder();
////		String password = encoder.encode("pass");
////		log.debug("PasswordEncoder password : [{}] ", password);					// {bcrypt}$2a$10$q6JJMlG7Q7Gt4n/76ydvp.Vk9pWVcTfCQ4NtWyBzNtWOmefYNw/wO
////
////		InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
////		manager.createUser(User.withUsername("user").password(password).roles("USER").build());
////		manager.createUser(User.withUsername("admin").password("{noop}pass").roles("USER", "ADMIN").build());
////		return manager;
//		return new OAuthUserDetailsService();
//	}

AuthorizationServerConfig 수정

AuthorizationServer 환경설정에 사용자관리 서비스를 등록합니다.

	@Autowired
	@Qualifier("oauthUserDetailsService")
	private UserDetailsService userDetailsService;
	
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
		endpoints
			.authenticationManager(authenticationManager)
			.userDetailsService(userDetailsService);
	}

 

TokenStore, CodeService, ApprovalStore 구현

TokenStore, CodeService, ApprovalStore 는 OAuth의 허가정보를 관리하는 서비스들로 토큰에 대한 만료 및 접근 토큰 발급과 관련된 승인코드 들을 관리합니다.

TokenStoreService 생성

TokenService 인터페이스를 구현하는 접근 토큰, 갱신 토큰을 관리 서비스를 구현합니다.

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator;
import org.springframework.security.oauth2.provider.token.DefaultAuthenticationKeyGenerator;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Service;

import kr.ejsoft.oauth2.server.dao.OAuthTokenStoreDAO;
import kr.ejsoft.oauth2.server.model.OAuthAccessToken;
import kr.ejsoft.oauth2.server.model.OAuthRefreshToken;

@Service("oauthTokenStoreService")
public class OAuthTokenStoreService implements TokenStore {

	private static final Logger log = LoggerFactory.getLogger(OAuthTokenStoreService.class);

	@Autowired
	@Qualifier("oauthTokenStoreDAO")
	private OAuthTokenStoreDAO tokenStoreDAO;

	private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();

	@Override
	public OAuth2Authentication readAuthentication(OAuth2AccessToken accessToken) {
		return readAuthentication(accessToken.getValue());
	}

	@Override
	public OAuth2Authentication readAuthentication(String token) {
		OAuthAccessToken accessToken = tokenStoreDAO.findByTokenId(extractTokenKey(token));
		if (accessToken != null) {
			return accessToken.getAuthenticationObject();
		}
		return null;
	}

	@Override
	public void storeAccessToken(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
		String refreshToken = null;
		if (accessToken.getRefreshToken() != null) {
			refreshToken = accessToken.getRefreshToken().getValue();
		}

		if (readAccessToken(accessToken.getValue()) != null) {
			this.removeAccessToken(accessToken);
		}

		OAuthAccessToken cat = new OAuthAccessToken();
		cat.setId(UUID.randomUUID().toString() + UUID.randomUUID().toString());
		cat.setTokenId(extractTokenKey(accessToken.getValue()));
		cat.setTokenObject(accessToken);
		cat.setAuthenticationId(authenticationKeyGenerator.extractKey(authentication));
		cat.setUsername(authentication.isClientOnly() ? null : authentication.getName());
		cat.setClientId(authentication.getOAuth2Request().getClientId());
		cat.setAuthenticationObject(authentication);
		cat.setRefreshToken(extractTokenKey(refreshToken));

		tokenStoreDAO.saveAccessToken(cat);
	}

	@Override
	public OAuth2AccessToken readAccessToken(String tokenValue) {
		OAuthAccessToken accessToken = tokenStoreDAO.findByTokenId(extractTokenKey(tokenValue));
		if (accessToken != null) {
			return accessToken.getTokenObject();
		}
		return null;
	}

	@Override
	public void removeAccessToken(OAuth2AccessToken oAuth2AccessToken) {
		OAuthAccessToken accessToken = tokenStoreDAO.findByTokenId(extractTokenKey(oAuth2AccessToken.getValue()));
		if (accessToken != null) {
			tokenStoreDAO.deleteAccessToken(accessToken);
		}
	}

	@Override
	public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {
		OAuthRefreshToken crt = new OAuthRefreshToken();
		crt.setId(UUID.randomUUID().toString() + UUID.randomUUID().toString());
		crt.setTokenId(extractTokenKey(refreshToken.getValue()));
		crt.setTokenObject(refreshToken);
		crt.setAuthenticationObject(authentication);
		tokenStoreDAO.saveRefreshToken(crt);
	}

	@Override
	public OAuth2RefreshToken readRefreshToken(String tokenValue) {
		OAuthRefreshToken refreshToken = tokenStoreDAO.findRefreshTokenByTokenId(extractTokenKey(tokenValue));
		return refreshToken != null ? refreshToken.getTokenObject() : null;
	}

	@Override
	public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken refreshToken) {
		OAuthRefreshToken rtk = tokenStoreDAO.findRefreshTokenByTokenId(extractTokenKey(refreshToken.getValue()));
		return rtk != null ? rtk.getAuthenticationObject() : null;
	}

	@Override
	public void removeRefreshToken(OAuth2RefreshToken refreshToken) {
		OAuthRefreshToken rtk = tokenStoreDAO.findRefreshTokenByTokenId(extractTokenKey(refreshToken.getValue()));
		if (rtk != null) {
			tokenStoreDAO.deleteRefreshToken(rtk);
		}
	}

	@Override
	public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) {
		OAuthAccessToken token = tokenStoreDAO.findByRefreshToken(extractTokenKey(refreshToken.getValue()));
		if (token != null) {
			tokenStoreDAO.deleteAccessToken(token);
		}
	}

	@Override
	public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
		OAuth2AccessToken accessToken = null;
		String authenticationId = authenticationKeyGenerator.extractKey(authentication);
		OAuthAccessToken token = tokenStoreDAO.findByAuthenticationId(authenticationId);

		if (token != null) {
			accessToken = token.getTokenObject();
			if (accessToken != null && !authenticationId.equals(this.authenticationKeyGenerator.extractKey(this.readAuthentication(accessToken)))) {
				this.removeAccessToken(accessToken);
				this.storeAccessToken(accessToken, authentication);
			}
		}
		return accessToken;
	}

	@Override
	public Collection<OAuth2AccessToken> findTokensByClientIdAndUserName(String clientId, String userName) {
		Collection<OAuth2AccessToken> tokens = new ArrayList<OAuth2AccessToken>();
		List<OAuthAccessToken> result = tokenStoreDAO.findByClientIdAndUsername(clientId, userName);
		result.forEach(e -> tokens.add(e.getTokenObject()));
		return tokens;
	}

	@Override
	public Collection<OAuth2AccessToken> findTokensByClientId(String clientId) {
		Collection<OAuth2AccessToken> tokens = new ArrayList<OAuth2AccessToken>();
		List<OAuthAccessToken> result = tokenStoreDAO.findByClientId(clientId);
		result.forEach(e -> tokens.add(e.getTokenObject()));
		return tokens;
	}

	private String extractTokenKey(String value) {
		if (value == null) {
			return null;
		} else {
			MessageDigest digest;
			try {
				digest = MessageDigest.getInstance("MD5");
			} catch (NoSuchAlgorithmException var5) {
				throw new IllegalStateException("MD5 algorithm not available.  Fatal (should be in the JDK).");
			}

			try {
				byte[] e = digest.digest(value.getBytes("UTF-8"));
				return String.format("%032x", new Object[] { new BigInteger(1, e) });
			} catch (UnsupportedEncodingException var4) {
				throw new IllegalStateException("UTF-8 encoding not available.  Fatal (should be in the JDK).");
			}
		}
	}
}

CodeService 생성

RandomValueAuthorizationCodeServices를 상속하는 코드 관리 서비스를 구현합니다.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.code.RandomValueAuthorizationCodeServices;
import org.springframework.stereotype.Service;

import kr.ejsoft.oauth2.server.dao.OAuthTokenStoreDAO;
import kr.ejsoft.oauth2.server.model.OAuthCode;

@Service("oauthCodeService")
public class OAuthCodeService extends RandomValueAuthorizationCodeServices {

	@Autowired
	@Qualifier("oauthTokenStoreDAO")
	private OAuthTokenStoreDAO dao;

	@Override
	protected void store(String code, OAuth2Authentication authentication) {
		OAuthCode approval = new OAuthCode();
		approval.setCode(code);
		approval.setAuthenticationObject(authentication);
		dao.save(approval);
	}

	public OAuth2Authentication remove(String code) {
		OAuth2Authentication authentication = null;

		try {
			OAuthCode oauthcode = dao.findByCode(code);
			authentication = oauthcode != null ? oauthcode.getAuthenticationObject() : null;
		} catch (Exception e) {
			return null;
		}

		if (authentication != null) {
			dao.delete(code);
		}

		return authentication;
	}
}

ApprovalService 생성

ApprovalStore 인터페이스를 구현하는 승인 관리 서비스를 구현합니다.

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import static org.springframework.security.oauth2.provider.approval.Approval.ApprovalStatus.APPROVED;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.oauth2.provider.approval.Approval;
import org.springframework.security.oauth2.provider.approval.Approval.ApprovalStatus;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.stereotype.Service;

import kr.ejsoft.oauth2.server.dao.OAuthTokenStoreDAO;
import kr.ejsoft.oauth2.server.model.OAuthApproval;

@Service("oauthApprovalStoreService")
public class OAuthApprovalStoreService implements ApprovalStore {

	private static final Logger log = LoggerFactory.getLogger(OAuthTokenStoreService.class);

	@Autowired
	@Qualifier("oauthTokenStoreDAO")
	private OAuthTokenStoreDAO dao;

	private boolean handleRevocationsAsExpiry = false;
	
	public void setHandleRevocationsAsExpiry(boolean handleRevocationsAsExpiry) {
		this.handleRevocationsAsExpiry = handleRevocationsAsExpiry;
	}
	
	@Override
	public boolean addApprovals(Collection<Approval> approvals) {
		if (log.isDebugEnabled()) {
			log.debug(String.format("adding approvals: [%s]", approvals));
		}
		boolean success = true;
		for (Approval approval : approvals) {
			OAuthApproval appr = new OAuthApproval();
			appr.setExpiresAt(approval.getExpiresAt().getTime());
			appr.setStatus((approval.getStatus() == null ? APPROVED : approval.getStatus()).toString());
			appr.setLastModifiedAt(approval.getLastUpdatedAt().getTime());
			appr.setUserId(approval.getUserId());
			appr.setClientId(approval.getClientId());
			appr.setScope(approval.getScope());
			
			if (!refreshApproval(appr)) {
				if (!saveApproval(appr)) {
					success = false;
				}
			}
		}
		return success;
	}

	private boolean refreshApproval(final OAuthApproval approval) {
		if (log.isDebugEnabled()) {
			log.debug(String.format("refreshing approval: [%s]", approval));
		}
		int refreshed = dao.refreshApproval(approval);
		if (refreshed != 1) {
			return false;
		}
		return true;
	}

	private boolean saveApproval(final OAuthApproval approval) {
		if (log.isDebugEnabled()) {
			log.debug(String.format("refreshing approval: [%s]", approval));
		}
		int refreshed = dao.saveApproval(approval);
		if (refreshed != 1) {
			return false;
		}
		return true;
	}
	
	@Override
	public boolean revokeApprovals(Collection<Approval> approvals) {
		if (log.isDebugEnabled()) {
			log.debug(String.format("Revoking approvals: [%s]", approvals));
		}
		boolean success = true;
		for (final Approval approval : approvals) {
			if (handleRevocationsAsExpiry) {
				OAuthApproval appr = new OAuthApproval();
				appr.setExpiresAt(System.currentTimeMillis());
				appr.setUserId(approval.getUserId());
				appr.setClientId(approval.getClientId());
				appr.setScope(approval.getScope());
				
				int refreshed = dao.expireApproval(appr);
				if (refreshed != 1) {
					success = false;
				}
			}
			else {
				OAuthApproval appr = new OAuthApproval();
				appr.setUserId(approval.getUserId());
				appr.setClientId(approval.getClientId());
				appr.setScope(approval.getScope());
				
				int refreshed = dao.deleteApproval(appr);
				if (refreshed != 1) {
					success = false;
				}
			}
		}
		return success;
	}

	@Override
	public Collection<Approval> getApprovals(String userId, String clientId) {
		List<Approval> coll = null;
		List<OAuthApproval> list = dao.findByUserIdAndClientId(userId, clientId);
		if(list != null && list.size() > 0) {
			coll = new ArrayList<Approval>();
			for(OAuthApproval approval : list) {
				String userId1 = approval.getUserId();
				String clientId1 = approval.getClientId();
				String scope = approval.getScope();
				Date expiresAt = new Date(approval.getExpiresAt().getTime());
				String status = approval.getStatus();
				Date lastUpdatedAt = new Date(approval.getLastModifiedAt().getTime());
				
				coll.add(new Approval(userId1, clientId1, scope, expiresAt, ApprovalStatus.valueOf(status), lastUpdatedAt));
			}
		}
		return coll;
	}
}

AccessToken 생성

접근 토큰을 저장하는 객체를 생성합니다. 데이터베이스 저장시 데이터는 객체를 직렬화한 상태로 저장합니다.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;

import kr.ejsoft.oauth2.server.util.SerializableObjectConverter;

public class OAuthAccessToken {
	private String id;
	private String tokenId;
	private String token;
	private String authenticationId;
	private String username;
	private String clientId;
	private String authentication;
	private String refreshToken;

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getTokenId() {
		return tokenId;
	}

	public void setTokenId(String tokenId) {
		this.tokenId = tokenId;
	}

	public String getToken() {
		return token;
	}
	public OAuth2AccessToken getTokenObject() {
		return token != null ? SerializableObjectConverter.deserializeAccessToken(token) : null;
	}
	
	public String getAuthenticationId() {
		return authenticationId;
	}

	public void setAuthenticationId(String authenticationId) {
		this.authenticationId = authenticationId;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getClientId() {
		return clientId;
	}

	public void setClientId(String clientId) {
		this.clientId = clientId;
	}

	public String getRefreshToken() {
		return refreshToken;
	}

	public void setRefreshToken(String refreshToken) {
		this.refreshToken = refreshToken;
	}

	public void setTokenObject(OAuth2AccessToken token) {
		this.token = SerializableObjectConverter.serializeAccessToken(token);
	}

	public void setToken(String token) {
		this.token = token;
	}
	
	public OAuth2Authentication getAuthenticationObject() {
		return SerializableObjectConverter.deserializeAuthentication(authentication);
	}

	public void setAuthenticationObject(OAuth2Authentication authentication) {
		this.authentication = SerializableObjectConverter.serializeAuthentication(authentication);
	}
	
	public String getAuthentication() {
		return authentication;
	}

	public void setAuthentication(String authentication) {
		this.authentication = authentication;
	}
}

RefreshToken 생성

갱신 토큰을 저장하는 객체를 생성합니다.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;

import kr.ejsoft.oauth2.server.util.SerializableObjectConverter;

public class OAuthRefreshToken {
	private static final Logger log = LoggerFactory.getLogger(OAuthRefreshToken.class);

	private String id;
	private String tokenId;
	private String token;
	private String authentication;
	
	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getTokenId() {
		return tokenId;
	}

	public void setTokenId(String tokenId) {
		this.tokenId = tokenId;
	}

	public String getToken() {
		return token;
	}

	public void setToken(String token) {
		this.token = token;
	}
	
	public OAuth2RefreshToken getTokenObject() {
		return token != null ? SerializableObjectConverter.deserializeRefreshToken(token) : null;
	}

	public void setTokenObject(OAuth2RefreshToken token) {
		this.token = SerializableObjectConverter.serializeRefreshToken(token);
	}

	public OAuth2Authentication getAuthenticationObject() {
		return SerializableObjectConverter.deserializeAuthentication(authentication);
	}

	public void setAuthenticationObject(OAuth2Authentication authentication) {
		this.authentication = SerializableObjectConverter.serializeAuthentication(authentication);
	}

	public String getAuthentication() {
		return this.authentication;
	}

	public void setAuthentication(String authentication) {
		this.authentication = authentication;
	}
}

Approval 생성

승인정보를 저장하는 객체를 생성합니다.

import java.sql.Timestamp;

public class OAuthApproval {
	private String userId;
	private String clientId;
	private String scope;
	private String status;
	private Timestamp expiresAt;
	private Timestamp lastModifiedAt;
	
	public String getUserId() {
		return userId;
	}
	public void setUserId(String userId) {
		this.userId = userId;
	}
	public String getClientId() {
		return clientId;
	}
	public void setClientId(String clientId) {
		this.clientId = clientId;
	}
	public String getScope() {
		return scope;
	}
	public void setScope(String scope) {
		this.scope = scope;
	}
	public String getStatus() {
		return status;
	}
	public void setStatus(String status) {
		this.status = status;
	}
	public Timestamp getExpiresAt() {
		return expiresAt;
	}
	public void setExpiresAt(Timestamp expiresAt) {
		this.expiresAt = expiresAt;
	}
	public void setExpiresAt(long time) {
		this.expiresAt = new Timestamp(time);
		
	}
	public Timestamp getLastModifiedAt() {
		return lastModifiedAt;
	}
	public void setLastModifiedAt(Timestamp lastModifiedAt) {
		this.lastModifiedAt = lastModifiedAt;
	}
	public void setLastModifiedAt(long time) {
		this.lastModifiedAt = new Timestamp(time);
	}
}

Code 생성

코드 정보를 저장하는 객체를 생성합니다.

import org.springframework.security.oauth2.provider.OAuth2Authentication;

import kr.ejsoft.oauth2.server.util.SerializableObjectConverter;

public class OAuthCode {
	private String code;
	private String authentication;
	public String getCode() {
		return code;
	}
	public void setCode(String code) {
		this.code = code;
	}
	public String getAuthentication() {
		return authentication;
	}
	public void setAuthentication(String authentication) {
		this.authentication = authentication;
	}
	
	public OAuth2Authentication getAuthenticationObject() {
		return SerializableObjectConverter.deserializeAuthentication(authentication);
	}

	public void setAuthenticationObject(OAuth2Authentication authentication) {
		this.authentication = SerializableObjectConverter.serializeAuthentication(authentication);
	}
	
}

TokenStoreDAO 생성

Token, Approval, Code를 데이터베이스에 질의하는 객체를 생성합니다.

import java.util.List;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import kr.ejsoft.oauth2.server.model.OAuthAccessToken;
import kr.ejsoft.oauth2.server.model.OAuthApproval;
import kr.ejsoft.oauth2.server.model.OAuthCode;
import kr.ejsoft.oauth2.server.model.OAuthRefreshToken;

@Repository("oauthTokenStoreDAO")
public class OAuthTokenStoreDAO{
	@Autowired
	private SqlSessionTemplate sqlSession;

	public List<OAuthAccessToken> findByClientId(String clientId) {
		return sqlSession.selectList("oauth.findTokenByClientId", clientId);
	}

	public List<OAuthAccessToken> findByClientIdAndUsername(String clientId, String username) {
		OAuthAccessToken token = new OAuthAccessToken();
		token.setClientId(clientId);
		token.setUsername(username);
		return sqlSession.selectList("oauth.findTokenByClientIdAndUsername", token);
	}

	public OAuthAccessToken findByTokenId(String tokenId) {
		return sqlSession.selectOne("oauth.findTokenByTokenId", tokenId);
	}

	public OAuthAccessToken findByRefreshToken(String refreshToken) {
		return sqlSession.selectOne("oauth.findTokenByRefreshToken", refreshToken);
	}

	public OAuthAccessToken findByAuthenticationId(String authenticationId) {
		return sqlSession.selectOne("oauth.findTokenByAuthenticationId", authenticationId);
	}

	public int saveAccessToken(OAuthAccessToken oauthAccessToken) {
		return sqlSession.insert("oauth.saveAccessToken", oauthAccessToken);
	}

	public int deleteAccessToken(OAuthAccessToken oauthAccessToken) {
		return sqlSession.delete("oauth.deleteAccessToken", oauthAccessToken);
	}

	public OAuthRefreshToken findRefreshTokenByTokenId(String tokenId) {
		return sqlSession.selectOne("oauth.findRefreshTokenByTokenId", tokenId);
	}

	public int saveRefreshToken(OAuthRefreshToken refreshToken) {
		return sqlSession.insert("oauth.saveRefreshToken", refreshToken);
	}

	public int deleteRefreshToken(OAuthRefreshToken refreshToken) {
		return sqlSession.delete("oauth.deleteRefreshToken", refreshToken);
	}

	
	
	
	public OAuthCode findByCode(String code) {
		return sqlSession.selectOne("oauth.findAuthenticationByCode", code);
	}

	public int save(OAuthCode approval) {
		return sqlSession.insert("oauth.saveCode", approval);
	}

	public int delete(String code) {
		return sqlSession.delete("oauth.deleteCode", code);
	}
	
	

	public List<OAuthApproval> findByUserIdAndClientId(String userId, String clientId) {
		OAuthApproval approval = new OAuthApproval();
		approval.setClientId(clientId);
		approval.setUserId(userId);
		return sqlSession.selectList("oauth.findByUserIdAndClientId", approval);
	}

	public int saveApproval(OAuthApproval approval) {
		return sqlSession.insert("oauth.saveApproval", approval);
	}

	public int refreshApproval(OAuthApproval approval) {
		return sqlSession.update("oauth.refreshApproval", approval);
	}

	public int expireApproval(OAuthApproval approval) {
		return sqlSession.update("oauth.expireApproval", approval);
	}

	public int deleteApproval(OAuthApproval approval) {
		return sqlSession.delete("oauth.deleteApproval", approval);
	}
}

SerializableObjectConverter 생성

각종 객체를 직렬화/반직렬화하는 코드를 생성합니다.

import java.util.Base64;

import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.common.util.SerializationUtils;
import org.springframework.security.oauth2.provider.OAuth2Authentication;

public class SerializableObjectConverter {
	public static String serializeAccessToken(OAuth2AccessToken object) {
		try {
			byte[] bytes = SerializationUtils.serialize(object);
			return Base64.getEncoder().encodeToString(bytes);
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
	}

	public static OAuth2AccessToken deserializeAccessToken(String encodedObject) {
		try {
			byte[] bytes = Base64.getDecoder().decode(encodedObject);
			return (OAuth2AccessToken) SerializationUtils.deserialize(bytes);
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
	}

	public static String serializeRefreshToken(OAuth2RefreshToken object) {
		try {
			byte[] bytes = SerializationUtils.serialize(object);
			return Base64.getEncoder().encodeToString(bytes);
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
	}

	public static OAuth2RefreshToken deserializeRefreshToken(String encodedObject) {
		try {
			byte[] bytes = Base64.getDecoder().decode(encodedObject);
			return (OAuth2RefreshToken) SerializationUtils.deserialize(bytes);
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
	}
	
	public static String serializeAuthentication(OAuth2Authentication object) {
		try {
			byte[] bytes = SerializationUtils.serialize(object);
			return Base64.getEncoder().encodeToString(bytes);
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
	}

	public static OAuth2Authentication deserializeAuthentication(String encodedObject) {
		try {
			byte[] bytes = Base64.getDecoder().decode(encodedObject);
			return (OAuth2Authentication) SerializationUtils.deserialize(bytes);
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
	}
}

oauth_sql.xml 생성

데이터베이스 질의문을 정의합니다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="oauth">
	<select id="findTokenByClientId" resultType="kr.ejsoft.oauth2.server.model.OAuthAccessToken">
		<![CDATA[
		SELECT
		    token_id, token, authentication_id, username, client_id, authentication, refresh_token
		FROM
		    oauth_access_token
		WHERE
		    client_id = #{clientId}
		]]>
	</select>
	
	<select id="findTokenByClientIdAndUsername" resultType="kr.ejsoft.oauth2.server.model.OAuthAccessToken">
		<![CDATA[
		SELECT
		    token_id, token, authentication_id, username, client_id, authentication, refresh_token
		FROM
		    oauth_access_token
		WHERE
		    client_id = #{clientId}
		    AND username = #{username}
		]]>
	</select>
	
	<select id="findTokenByTokenId" resultType="kr.ejsoft.oauth2.server.model.OAuthAccessToken">
		<![CDATA[
		SELECT
		    token_id, token, authentication_id, username, client_id, authentication, refresh_token
		FROM
		    oauth_access_token
		WHERE
		    token_id = #{tokenId}
		]]>
	</select>
	
	<select id="findTokenByRefreshToken" resultType="kr.ejsoft.oauth2.server.model.OAuthAccessToken">
		<![CDATA[
		SELECT
		    token_id, token, authentication_id, username, client_id, authentication, refresh_token
		FROM
		    oauth_access_token
		WHERE
		    refresh_token = #{refresToken}
		]]>
	</select>
	
	<select id="findTokenByAuthenticationId" resultType="kr.ejsoft.oauth2.server.model.OAuthAccessToken">
		<![CDATA[
		SELECT
		    token_id, token, authentication_id, username, client_id, authentication, refresh_token
		FROM
		    oauth_access_token
		WHERE
		    authentication_id = #{authenticationId}
		]]>
	</select>
	
	<insert id="saveAccessToken" keyProperty="token_id">
		insert into oauth_access_token
		    (token_id, token, authentication_id, username, client_id, authentication, refresh_token)
		values
		    (#{tokenId}, #{token}, #{authenticationId}, #{username}, #{clientId}, #{authentication}, #{refreshToken})
	</insert>

	<delete id="deleteAccessToken">
		delete from oauth_access_token where token_id = #{tokenId}
	</delete>
	<select id="findRefreshTokenByTokenId" resultType="kr.ejsoft.oauth2.server.model.OAuthRefreshToken">
		<![CDATA[
		SELECT
		    *
		FROM
		    oauth_refresh_token
		WHERE
		    token_id = #{tokenId}
		]]>
	</select>
	
	<insert id="saveRefreshToken" keyProperty="token_id">
		insert into oauth_refresh_token
		    (token_id, token, authentication)
		values
		    (#{tokenId}, #{token}, #{authentication})
	</insert>


	<delete id="deleteRefreshToken">
		delete from oauth_refresh_token where token_id = #{tokenId}
	</delete>
	



	<select id="findAuthenticationByCode" resultType="kr.ejsoft.oauth2.server.model.OAuthCode">
		<![CDATA[
		select code, authentication from oauth_code where code = #{code}
		]]>
	</select>
	
	<insert id="saveCode" keyProperty="token_id">
		insert into oauth_code (code, authentication) values (#{code}, #{authentication})
	</insert>
	
	<delete id="deleteCode">
		delete from oauth_code where code = #{code}
	</delete>
	
	
	
	
	<select id="findByUserIdAndClientId" resultType="kr.ejsoft.oauth2.server.model.OAuthApproval">
		<![CDATA[
		SELECT
		    expiresAt, status, lastModifiedAt, userId, clientId, scope
		FROM
		    oauth_approvals
		WHERE 1=1
		  AND userId = #{userId}
		  AND clientId = #{clientId}
		]]>
	</select>
	
	<insert id="saveApproval" keyProperty="token_id">
		insert into oauth_approvals
		    (expiresAt, status, lastModifiedAt, userId, clientId, scope)
		values
		    (#{expiresAt, javaType=java.sql.Timestamp, jdbcType=TIMESTAMP},
		     #{status},
		     #{lastModifiedAt, javaType=java.sql.Timestamp, jdbcType=TIMESTAMP},
		     #{userId},
		     #{clientId},
		     #{scope})
	</insert>
	
	<update id="refreshApproval">
		UPDATE oauth_approvals
		   SET expiresAt=#{expiresAt, javaType=java.sql.Timestamp, jdbcType=TIMESTAMP},
		       status=#{status},
		       lastModifiedAt=#{lastModifiedAt, javaType=java.sql.Timestamp, jdbcType=TIMESTAMP}
		 WHERE 1=1
		   AND userId = #{userId}
		   AND clientId = #{clientId}
		   AND scope = #{scope}
	</update>

	<update id="expireApproval">
		UPDATE oauth_approvals
		   SET expiresAt = #{expiresAt}
		 WHERE 1=1
		   AND userId = #{userId}
		   AND clientId = #{clientId}
		   AND scope = #{scope}
	</update>
	
	<delete id="deleteApproval">
		DELETE FROM oauth_approvals
		WHERE 1=1
		  AND userId = #{userId}
		  AND clientId = #{clientId}
		  AND scope = #{scope}
	</delete>
	
</mapper>

AuthorizationServerConfig 수정

생성한 서비스들을 AuthorizationServerConfig에 등록합니다.

	@Autowired
	@Qualifier("oauthTokenStoreService")
	private TokenStore tokenStoreService;

	@Autowired
	@Qualifier("oauthApprovalStoreService")
	private ApprovalStore approvalStore;

	@Autowired
	@Qualifier("oauthCodeService")
	private AuthorizationCodeServices authorizationCodeServices;
	
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
		endpoints
			.authenticationManager(authenticationManager)
			.tokenStore(tokenStoreService)
			.approvalStore(approvalStore)
			.authorizationCodeServices(authorizationCodeServices)
			.userDetailsService(userDetailsService);
	}

 

참고사이트

 

소스코드

소스코드는 여기에서 다운로드 가능합니다.

728x90