시작하면서

  • MSA에서 보안을 위해서 Oauth2를 일반적으로 사용한다.
  • keycloack를 이용해서 Oauth2 서버를 사용할수도 있다.
  • Spring Authorization Server을 이용하여 구현도 가능하다.
  • MSA 서비스를 위한 Oauth2 서버를 구현해 보자
  • 기본적으로 Spring Security의 개념이 필요하다!

dependencies

  • build.gradle
	implementation 'org.springframework.boot:spring-boot-starter-oauth2-authorization-server'
	implementation 'com.nimbusds:nimbus-jose-jwt'
}
  • 다음의 종속성이 필요하다.

기본설정하기

  • application.properties
spring.application.name=spring-authorization-server

server.port=9000
logging.level.org.springframework.security=trace
  • properties를 이용해서 많은 것을 설정할 수 있지만, 여기서는 포트와 로그 수준만 설정하였다.

Configuration 파일 작성하기

  • security.java

설정파일 명시하기

@Configuration
@EnableWebSecurity
public class AuthorizationServerConfig {
}

Protocol EndPoint를 위한 filter chain

    @Bean
    @Order(1)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
            throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
        http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(Customizer.withDefaults());
        http.exceptionHandling((exceptions) ->exceptions
                .defaultAuthenticationEntryPointFor(
                        new LoginUrlAuthenticationEntryPoint("/login"),
                        new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
                ));
        http.oauth2ResourceServer((resourceServer) -> resourceServer.jwt(Customizer.withDefaults()));

        return http.build();
    }
  • OAuth2AuthorizationServerConfiguration을 통해서 oauth2 서버의 기본 구성을 적용한다.
  • oidc를 이용해서 OpenId Connect 지원을 추가함
  • 예외 처리는 인증되지 않은 사용자에게 login URL로 리다이렉트됨
  • 이외에도 여러 엔드포인트의 전처리, 후처리, 컨버터등을 처리할 수 있음

Authentication 설정

    @Bean
    @Order(2)
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
            throws Exception {
        http
                .authorizeHttpRequests((authorize) -> authorize
                        .anyRequest().authenticated()
                )
                .formLogin(Customizer.withDefaults());

        return http.build();
    }
  • 모든 URL에 인증을 추가하였고, formLogin 방식을 사용하였다.

User 추가하기

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails userDetails = User.withDefaultPasswordEncoder()
                .username("user")
                .password("password")
                .roles("USER")
                .build();

        return new InMemoryUserDetailsManager(userDetails);
    }

인증에 사용할 User를 추가하였다.

Client Repository 추가하기

    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("my-client")
                .clientSecret("{noop}mypassword")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
//                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
//                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
//                .redirectUri("http://localhost:3000")
//                .postLogoutRedirectUri("http://localhost:3000/")
                .scope(OidcScopes.OPENID)
//                .scope(OidcScopes.PROFILE)
                .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
                .tokenSettings(TokenSettings.builder().accessTokenTimeToLive(Duration.ofSeconds(360)).build())
                .build();

        return new InMemoryRegisteredClientRepository(oidcClient);
    }
  • 이는 다음에서 추가 설명을 하겠다.

JWT 키 설정

        @Bean
        public JWKSource<SecurityContext> jwkSource() {
            KeyPair keyPair = generateRsaKey();
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
            RSAKey rsaKey = new RSAKey.Builder(publicKey)
                    .privateKey(privateKey)
                    .keyID(UUID.randomUUID().toString())
                    .build();
            JWKSet jwkSet = new JWKSet(rsaKey);
            return new ImmutableJWKSet<>(jwkSet);
        }

        private static KeyPair generateRsaKey() {
            KeyPair keyPair;
            try {
                KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
                keyPairGenerator.initialize(2048);
                keyPair = keyPairGenerator.generateKeyPair();
            }
            catch (Exception ex) {
                throw new IllegalStateException(ex);
            }
            return keyPair;
        }

        @Bean
        public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
            return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
        }
  • Decoder와 RSA를 활용하여 키를 생성하는 코드이다.
  • 개인키와 퍼블릭키가 생성됨
  • 실사용 시 키를 저장해야함

서버 구성하기

        @Bean
        public AuthorizationServerSettings authorizationServerSettings() {
            return AuthorizationServerSettings.builder().build();
        }

실행하고 엔드포인트 구경하기

profile
백엔드 개발자가 꿈인 컴공과

0개의 댓글

Powered by GraphCDN, the GraphQL CDN