우리는 이미 논의했습니다. OAuth 2.0 인증을 구성하는 방법 그리고 사용자 지정 토큰 저장소를 구축하는 방법. 이 시리즈의 마지막 글에서는 다음을 사용하여 사용자 지정 동적 클라이언트 등록을 구현하는 방법에 대해 알아봅니다. 스프링 보안 인증 2. 다음을 읽어 보시기 바랍니다. 1부 그리고 파트 2 먼저, 중단한 부분부터 계속 진행하겠습니다.
클라이언트 데이터 저장을 담당하는 엔티티를 만드는 것부터 시작하겠습니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
import com.couchbase.client.java.repository.annotation.Id; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.data.couchbase.core.mapping.Document; import javax.validation.constraints.NotNull; import java.util.*; @NoArgsConstructor @Data @Document public class CustomClientDetails extends BasicEntity { @Id @NotNull private String id; @NotNull private String clientId; private String clientSecret; private Set<String> resourceIds = new HashSet<>(); private boolean secretRequired; private boolean scoped; private Set<String> scope = new HashSet<>(); private Set<String> authorizedGrantTypes = new HashSet<>(); private Set<String> registeredRedirectUri = new HashSet<>(); private Collection<String> authorities = new HashSet<>(); private Integer accessTokenValiditySeconds; private Integer refreshTokenValiditySeconds; private boolean autoApprove; private Map<String, Object> additionalInformation = new HashMap<>(); } |
각 리포지토리는 다음과 같습니다:
|
1 2 3 4 5 6 7 8 9 10 11 |
import org.springframework.data.couchbase.core.query.N1qlPrimaryIndexed; import org.springframework.data.couchbase.core.query.ViewIndexed; import org.springframework.data.couchbase.repository.CouchbasePagingAndSortingRepository; @N1qlPrimaryIndexed @ViewIndexed(designDoc = "customClientDetails") public interface CustomClientDetailsRepository extends CouchbasePagingAndSortingRepository<CustomClientDetails, String> { CustomClientDetails findByClientId(String clientId); } |
이제 우리는 클라이언트 세부 정보 서비스 인터페이스를 사용할 수 있습니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.security.oauth2.provider.ClientDetailsService; import org.springframework.security.oauth2.provider.ClientRegistrationException; import org.springframework.security.oauth2.provider.client.BaseClientDetails; import org.springframework.stereotype.Service; import java.util.stream.Collectors; @Service public class CouchbaseClientDetailsService implements ClientDetailsService { @Autowired private CustomClientDetailsRepository customClientDetailsRepository; @Override public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException { CustomClientDetails client = customClientDetailsRepository.findByClientId(clientId); String resourceIds = client.getResourceIds().stream().collect(Collectors.joining(",")); String scopes = client.getScope().stream().collect(Collectors.joining(",")); String grantTypes = client.getAuthorizedGrantTypes().stream().collect(Collectors.joining(",")); String authorities = client.getAuthorities().stream().collect(Collectors.joining(",")); BaseClientDetails base = new BaseClientDetails(client.getClientId(), resourceIds, scopes, grantTypes, authorities); base.setClientSecret(client.getClientSecret()); base.setAccessTokenValiditySeconds(client.getAccessTokenValiditySeconds()); base.setRefreshTokenValiditySeconds(client.getRefreshTokenValiditySeconds()); base.setAdditionalInformation(client.getAdditionalInformation()); base.setAutoApproveScopes(client.getScope()); return base; } } |
저는 BaseClientDetails 클래스를 구현하는 대신 클라이언트 세부 정보 인터페이스를 사용합니다. 표준 JDBC 구현에서도 이 인터페이스를 사용하기 때문에 이것이 최선의 선택인 것 같습니다.
마지막으로, 우리는 AuthorizationServerConfig 를 사용하려면 CouchbaseClientDetailsService:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private CouchbaseClientDetailsService couchbaseClientDetailsService; @Override public void configure(ClientDetailsServiceConfigurer configurer) throws Exception { configurer.withClientDetails(couchbaseClientDetailsService); } } |
전체 수업의 모습은 다음과 같습니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; import org.springframework.security.oauth2.provider.token.TokenStore; @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private TokenStore tokenStore; @Autowired private AuthenticationManager authenticationManager; @Autowired private CouchbaseClientDetailsService couchbaseClientDetailsService; @Autowired private PasswordEncoder passwordEncoder; @Override public void configure(ClientDetailsServiceConfigurer configurer) throws Exception { configurer.withClientDetails(couchbaseClientDetailsService); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(tokenStore) .authenticationManager(authenticationManager); } @Override public void configure( AuthorizationServerSecurityConfigurer oauthServer) throws Exception { oauthServer .tokenKeyAccess("permitAll()") .checkTokenAccess("isAuthenticated()"); } } |
이제 데이터베이스에 새 클라이언트를 삽입하고 이러한 자격 증명을 사용하여 OAuth를 통해 인증할 수 있습니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
static final String GRANT_TYPE_PASSWORD = "password"; static final String AUTHORIZATION_CODE = "authorization_code"; static final String REFRESH_TOKEN = "refresh_token"; static final String IMPLICIT = "implicit"; static final String SCOPE_READ = "read"; static final String SCOPE_WRITE = "write"; static final String TRUST = "trust"; private void createOauthClients() { CustomClientDetails client = new CustomClientDetails(); client.setId("someId"); client.setResourceIds(new HashSet<>(Arrays.asList("resource_id")) ); client.setClientId("android-client"); client.setClientSecret("android-secret"); client.setAuthorizedGrantTypes(new HashSet<>(Arrays.asList(GRANT_TYPE_PASSWORD, AUTHORIZATION_CODE, REFRESH_TOKEN, IMPLICIT))); client.setScope(new HashSet<>(Arrays.asList(SCOPE_READ, SCOPE_WRITE, TRUST))); client.setSecretRequired(true); client.setAccessTokenValiditySeconds(50000); client.setRefreshTokenValiditySeconds(50000); client.setScoped(false); customClientDetailsRepository.save(client); } |
TL;DR - 비결은 org.springframework.security.oauth2.provider.ClientDetailsService 인터페이스에 매개변수로 전달하여 클라이언트 세부 정보 서비스 구성자:
|
1 2 3 4 |
@Override public void configure(ClientDetailsServiceConfigurer configurer) throws Exception { configurer.withClientDetails(couchbaseClientDetailsService); } |
궁금한 점이 있으면 다음 주소로 트윗해 주세요. @deniswsrosa