y_zho
40
2020-09-20 23:31:52
0
136

[질문] Spring Boot Oauth2 + Zuul Gateway


Oauth2와 Zuul을 연동해서 구현했습니다.

Access Token을 가져올때는 문제가 없는데 Resource Server에 접근하면 에러가 나고 있습니다.


curl --location --request POST 'http://localhost:8080/oauth/token?grant_type=password&username=john&password=123' \
--header 'Authorization: Basic Zm9vQ2xpZW50SWRQYXNzd29yZDpzZWNyZXQ=' \
--data-raw ''

{
    "access_token""eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJqb2huIiwic2NvcGUiOlsiZm9vIiwicmVhZCIsIndyaXRlIl0sIm9yZ2FuaXphdGlvbiI6ImpvaG5DalNDIiwiZXhwIjoxNjAwNjE1NjIzLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiOWJmNGU4ZjctYmZkMy00ZTYyLTkwMGItYmNiZTg4Zjk3YjMwIiwiY2xpZW50X2lkIjoiZm9vQ2xpZW50SWRQYXNzd29yZCJ9.Y-UdQg-PFJ6uZR328DAD4oaJBv_c06HGzIVHp6o2RYzDaqBLqCHXK-g_6gJO5dxQ9Q-Yb5eH_xQS5fm4jqiqQa_T4L3kVG7PgMfMq3gj2L1j98Bc-I4dQKDW8Qmmcy7cg1wqb1JnqdAXHfzeU8YjQ0w0dohJW_Ypug_yg07LcHxFSWb5m2V0ncpVUaXn0ghMD_ZMWcR85kesj82jeGjwEopoXKUmzHgLf8QUqmOnrJ5w0-jJpzphvMT1kpmpKctvnOz0Jy5NCGmpD-jpISbz8unGVRvdn-unBBr4E5Qd9O6NQaVpiTjfy36z1BiIEE6BqcHIihRAzAeYg-KT42wxJw",
    "token_type""bearer",
    "refresh_token""eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJqb2huIiwic2NvcGUiOlsiZm9vIiwicmVhZCIsIndyaXRlIl0sIm9yZ2FuaXphdGlvbiI6ImpvaG5DalNDIiwiYXRpIjoiOWJmNGU4ZjctYmZkMy00ZTYyLTkwMGItYmNiZTg4Zjk3YjMwIiwiZXhwIjoxNjAzMjA0MDIzLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiNDkxYmNiMDAtNTYxZS00Nzc5LTgwNWUtNzNlYzVkMGQ0NDA0IiwiY2xpZW50X2lkIjoiZm9vQ2xpZW50SWRQYXNzd29yZCJ9.g1ve27IkqA-BoTRU69Ud8YF2kUEVxQejNg0xA97SYq7GK722qE-Uh9-VMRZB5DFDcGi5ZVTY3tofr60ix9Noflf5VgzgRJ-HwMbIAiRl_XG6nas4vYnSwQY15HU7ERG7ZZUuYv_UgoVDdx1FRIU7Yz5CevJlKBqIVRbN4qcQgRmg7iCkupxuK5Iu6ZHFMkQlgiZGh3TbrMMoX0htneHq3dv3eFD3cuo9lf3pWED4cs-1X_r5RYF9W_vizpalXhiKdTVp-R6gx-Q1wrw4ya1V-BlyI-Dh2a7uS_2yuoyfyfuPO-3K5IBrNUcZCvTUGa5irzxcWfN8BaRVKuIhOuEe3Q",
    "expires_in"3599,
    "scope""foo read write",
    "organization""johnCjSC",
    "jti""9bf4e8f7-bfd3-4e62-900b-bcbe88f97b30"
}

curl --location --request GET 'http://localhost:8080/spring-security-oauth-resource/foos/15' \
--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJqb2huIiwic2NvcGUiOlsiZm9vIiwicmVhZCIsIndyaXRlIl0sIm9yZ2FuaXphdGlvbiI6ImpvaG5DalNDIiwiZXhwIjoxNjAwNjE1NjIzLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiOWJmNGU4ZjctYmZkMy00ZTYyLTkwMGItYmNiZTg4Zjk3YjMwIiwiY2xpZW50X2lkIjoiZm9vQ2xpZW50SWRQYXNzd29yZCJ9.Y-UdQg-PFJ6uZR328DAD4oaJBv_c06HGzIVHp6o2RYzDaqBLqCHXK-g_6gJO5dxQ9Q-Yb5eH_xQS5fm4jqiqQa_T4L3kVG7PgMfMq3gj2L1j98Bc-I4dQKDW8Qmmcy7cg1wqb1JnqdAXHfzeU8YjQ0w0dohJW_Ypug_yg07LcHxFSWb5m2V0ncpVUaXn0ghMD_ZMWcR85kesj82jeGjwEopoXKUmzHgLf8QUqmOnrJ5w0-jJpzphvMT1kpmpKctvnOz0Jy5NCGmpD-jpISbz8unGVRvdn-unBBr4E5Qd9O6NQaVpiTjfy36z1BiIEE6BqcHIihRAzAeYg-KT42wxJw'

{
    "error""invalid_token",
    "error_description""Cannot convert access token to JSON"
}


아래에 Configuration 추가 하였습니다..


Zuul Config

package org.sjm.config;

import java.io.IOException;

import org.springframework.core.io.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import io.micrometer.core.instrument.util.IOUtils;

@Configuration
@EnableResourceServer
public class GatewayConfiguration extends ResourceServerConfigurerAdapter {
	
    @Autowired
    private CustomAccessTokenConverter customAccessTokenConverter;
	
	@Override
	public void configure(final HttpSecurity http) throws Exception {
		http.authorizeRequests().antMatchers("/oauth/**").permitAll().antMatchers("/**")
				.access("hasAuthority('ROLE_USER') and #oauth2.hasScope('read')");

//            authenticated();
	}

    @Override
    public void configure(final ResourceServerSecurityConfigurer config) {
        config.tokenServices(tokenServices());
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        return defaultTokenServices;
    }
    
    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }
	
	/*
	 * @Bean public TokenStore tokenStore() { return new
	 * JwtTokenStore(accessTokenConverter()); }
	 */
    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        Resource resource = new ClassPathResource("public.txt");
        String publicKey = null;
        try {
            publicKey = IOUtils.toString(resource.getInputStream());
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }
        converter.setVerifierKey(publicKey);
        return converter;
    }

}




Authorization Server Config

package com.sjm.config;

import java.util.Arrays;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

/**
 * @EnableAuthorizationServer
 * oauth에 필요한 기본 설정 세팅
 * (/oauth/token, /oauth/authorize 등 endpoint 생성)
 * 
 * @author 6792291
 *
 */
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfigJwt extends AuthorizationServerConfigurerAdapter {

    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;

    /**
     * 토큰 엔트포인터(/oauth/token)에 대한 보안 설정을 할 수 있다.
     */
    @Override
    public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
    }
    
    /**
     * ClienDetailServiceConfigurer
     * 클라이언트에 대한 설정을 할 수 있다 (DB, Inmemory)
     * 클라이언트 별 제공할 Scope 나 권한을 정의
     */
    @Override
    public void configure(final ClientDetailsServiceConfigurer clients) throws Exception { // @formatter:off
        clients.inMemory()
                .withClient("sampleClientId")
                .authorizedGrantTypes("implicit")
                .scopes("read", "write", "foo", "bar")
                .autoApprove(false)
                .accessTokenValiditySeconds(3600)
                .redirectUris("http://localhost:8083/","http://localhost:8086/")
                .and()
                .withClient("fooClientIdPassword")
                .secret(passwordEncoder().encode("secret"))
                .authorizedGrantTypes("password", "authorization_code", "refresh_token", "client_credentials")
                .scopes("foo", "read", "write")
                .accessTokenValiditySeconds(3600)       // 1 hour
                .refreshTokenValiditySeconds(2592000)  // 30 days
                .redirectUris("http://www.example.com","http://localhost:8089/","http://localhost:8080/login/oauth2/code/custom","http://localhost:8080/ui-thymeleaf/login/oauth2/code/custom", "http://localhost:8080/authorize/oauth2/code/bael", "http://localhost:8080/login/oauth2/code/bael")
                .and()
                .withClient("barClientIdPassword")
                .secret(passwordEncoder().encode("secret"))
                .authorizedGrantTypes("password", "authorization_code", "refresh_token")
                .scopes("bar", "read", "write")
                .accessTokenValiditySeconds(3600)       // 1 hour
                .refreshTokenValiditySeconds(2592000)  // 30 days
                .and()
                .withClient("testImplicitClientId")
                .authorizedGrantTypes("implicit")
                .scopes("read", "write", "foo", "bar")
                .autoApprove(true)
                .redirectUris("http://www.example.com");
    } // @formatter:on

    /**
     * 인증, 토큰 엔드포인트, 토큰 서비스를 정의
     */
    @Override
    public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        
    	final TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter()));
        
        endpoints.tokenStore(tokenStore()).tokenEnhancer(tokenEnhancerChain).authenticationManager(authenticationManager);
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        final JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
		
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new FileSystemResource("src/main/resources/oauth2jwt.jks"), "oauth2jwtpass".toCharArray());
        converter.setKeyPair(keyStoreKeyFactory.getKeyPair("oauth2jwt"));
        return converter;
    }

    @Bean
    public TokenEnhancer tokenEnhancer() {
        return new CustomTokenEnhancer();
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    }

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




Resource Server Config

package com.sjm.config;

import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import java.io.IOException;
import org.springframework.core.io.Resource;

@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfigJwt extends ResourceServerConfigurerAdapter {

    @Autowired
    private CustomAccessTokenConverter customAccessTokenConverter;

    @Override
    public void configure(final HttpSecurity http) throws Exception {
        // @formatter:off
                http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                    .and()
                    .authorizeRequests().anyRequest().permitAll();
        // @formatter:on                
    }

    @Override
    public void configure(final ResourceServerSecurityConfigurer config) {
        config.tokenServices(tokenServices());
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        final JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setAccessTokenConverter(customAccessTokenConverter);

         final Resource resource = new ClassPathResource("public.txt");
         String publicKey = null;
         try {
         publicKey = IOUtils.toString(resource.getInputStream());
         } catch (final IOException e) {
         throw new RuntimeException(e);
         }
         converter.setVerifierKey(publicKey);
         converter.setSigningKey(publicKey);
        return converter;
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        return defaultTokenServices;
    }

}



0
  • 답변 0

  • 로그인을 하시면 답변 을 등록할 수 있습니다.