> Java > java지도 시간 > 유연한 데이터 기반 인증을 위한 사용자 정의 사용자 세부 정보 서비스를 갖춘 Spring 인증 서버 스프링 보안

유연한 데이터 기반 인증을 위한 사용자 정의 사용자 세부 정보 서비스를 갖춘 Spring 인증 서버 스프링 보안

Susan Sarandon
풀어 주다: 2025-01-23 12:07:09
원래의
568명이 탐색했습니다.

스프링 인증 서버

Spring 인증 서버는 기타 관련 표준과 함께 OAuth 2.1 및 OpenID Connect 1.0 사양을 구현하도록 설계된 프레임워크입니다. Spring Security를 ​​기반으로 구축되어 OpenID Connect 1.0 및 OAuth2 인증 서버 솔루션을 준수하는 ID 공급자를 생성하기 위한 안전하고 가벼우며 사용자 정의 가능한 기반을 제공합니다.

기능 목록

스프링 시큐리티란 무엇이고 어떻게 작동하나요?

단답형
Spring Security는 강력하고 사용자 정의가 가능한 인증 및 액세스 제어 프레임워크입니다. 이는 Spring 기반 애플리케이션 보안을 위한 사실상의 표준입니다.

Spring Security의 핵심은 강력한 인증 및 권한 부여 기능으로 웹 애플리케이션을 향상시키도록 설계된 서블릿 필터 모음입니다.

Spring Security는 또한 Spring Web MVC 또는 Spring Boot와 같은 프레임워크와 잘 어울리며 OAuth2 및 SAML과 같은 표준을 지원합니다. 자동으로 로그인 및 로그아웃 인터페이스를 생성하고 CSRF와 같은 일반적인 보안 취약성으로부터 애플리케이션을 보호합니다.

글쎄 별로 도움이 안 되는군요.

웹 보안에 대해 자세히 알아보고 보안 워크플로우의 핵심을 파악해 보겠습니다.

Spring Security 전문가가 되려면 먼저 다음 세 가지 핵심 개념을 이해해야 합니다.

  • 인증
  • 승인
  • 서블릿 필터

참고 - 이 섹션을 건너뛰지 마세요. 이는 모든 Spring Security 기능의 기반을 마련합니다.

입증

잔고를 확인하거나 거래하려면 온라인으로 은행 계좌에 접속해야 합니다. 일반적으로 이는 사용자 이름과 비밀번호를 사용하여 수행됩니다

사용자: "저는 John Doe입니다. 제 사용자 이름은 johndoe1985입니다."
은행 시스템: "신원을 확인하세요. 비밀번호는 무엇입니까?"
사용자: "내 비밀번호는 secureB@nk2023입니다."
은행 시스템: "환영합니다, John Doe. 계좌 개요는 다음과 같습니다."

권한 부여

기본 애플리케이션의 경우 인증만으로 충분할 수 있습니다. 사용자가 로그인하면 애플리케이션의 모든 영역에 대한 액세스 권한이 부여됩니다.

그러나 대부분의 애플리케이션에는 권한이나 역할이 있습니다.

사용자: "그 거래를 플레이해 보겠습니다…​."
은행 시스템: "잠깐만요, 먼저 귀하의 권한을 확인해야 합니다…
사용자: "100만 이체하겠습니다 하하하…​ 농담 농담"

서블릿 필터

이제 서블릿 필터를 살펴보겠습니다. 인증 및 승인과 어떤 관련이 있나요?

서블릿 필터를 사용하는 이유는 무엇입니까?
모든 Spring 웹 애플리케이션은 단일 서블릿, 즉 신뢰할 수 있는 DispatcherServlet을 중심으로 회전합니다. 주요 역할은 들어오는 HTTP 요청(예: 브라우저의 요청)을 적절한 @Controller 또는 @RestController로 라우팅하여 처리하는 것입니다.

여기에 거래가 있습니다. DispatcherServlet 자체에는 보안 기능이 내장되어 있지 않으며 @Controller에서 직접 원시 HTTP 기본 인증 헤더를 처리하고 싶지 않을 것입니다. 이상적으로는 요청이 @Controller에 도달하기 전에 인증 및 승인이 처리되어야 합니다

다행히도 Java 웹 환경에서는 서블릿 앞에 필터를 배치하여 이를 달성할 수 있습니다. 즉, SecurityFilter를 생성하고 이를 Tomcat(서블릿 컨테이너/애플리케이션 서버)에 설정하여 들어오는 모든 HTTP 요청이 서블릿에 도달하기 전에 가로채서 처리할 수 있습니다.

Security context

SecurityFilter에는 대략 4가지 작업이 있습니다.

  1. 먼저 필터는 요청에서 사용자 이름/비밀번호를 추출해야 합니다. 기본 인증 HTTP 헤더, 양식 필드, 쿠키 등을 통해 이루어질 수 있습니다.
  2. 그런 다음 필터는 데이터베이스와 같은 항목에 대해 해당 사용자 이름/비밀번호 조합을 검증해야 합니다.
  3. 필터는 인증에 성공한 후 사용자가 요청된 URI에 액세스할 수 있는 권한이 있는지 확인해야 합니다.
  4. 요청이 모든 검사를 통과한 경우 필터는 다음을 수행할 수 있습니다. 요청은 DispatcherServlet, 즉 @Controller로 전달됩니다.

필터체인

실제로는 단일 필터를 여러 개로 나눈 다음 서로 연결합니다.

수신 HTTP 요청이 이동하는 방식은 다음과 같습니다.

  1. 먼저 LoginMethodFilter를 통과합니다...
  2. 다음으로 AuthenticationFilter를 통과합니다...
  3. 그런 다음 AuthorizationFilter로 이동합니다...
  4. 마침내 서블릿에 도달합니다.

이 설정을 FilterChain이라고 합니다.

필터(또는 필터 체인)를 사용하면 @RestController 또는 @Controller의 핵심 구현을 변경하지 않고도 애플리케이션의 모든 인증 및 권한 부여 문제를 효과적으로 관리할 수 있습니다.

Spring의 DefaultSecurityFilterChain

Spring Security를 ​​올바르게 구성하고 웹 애플리케이션을 시작했다고 상상해 보세요. 다음과 같은 로그 메시지가 표시됩니다.

2020-02-25 10:24:27.875  INFO 11116 --- [           main] o.s.s.web.DefaultSecurityFilterChain     : Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@46320c9a, org.springframework.security.web.context.SecurityContextPersistenceFilter@4d98e41b, org.springframework.security.web.header.HeaderWriterFilter@52bd9a27, org.springframework.security.web.csrf.CsrfFilter@51c65a43, org.springframework.security.web.authentication.logout.LogoutFilter@124d26ba, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@61e86192, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@10980560, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@32256e68, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@52d0f583, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@5696c927, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@5f025000, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@5e7abaf7, org.springframework.security.web.session.SessionManagementFilter@681c0ae6, org.springframework.security.web.access.ExceptionTranslationFilter@15639d09, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@4f7be6c8]|
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

한 줄을 확장하면 Spring Security가 단지 하나의 필터를 추가하는 것이 아니라 15(!)개의 서로 다른 필터로 전체 필터 체인을 설정한다는 것을 알 수 있습니다.

HTTP 요청이 도착하면 @RestController에 도달하기 전에 순서대로 15개의 필터를 각각 통과합니다. 요청이 체인의 위에서 아래로 처리되므로 이러한 필터의 순서는 매우 중요합니다.

security chain

Spring의 FilterChain 분석하기

체인의 모든 필터에 대해 자세히 알아보려면 너무 많은 시간이 걸리겠지만 다음은 몇 가지 주요 필터에 대한 설명입니다. 다른 것에 대한 더 깊은 이해를 위해 Spring Security의 소스 코드를 탐색할 수 있습니다.

  1. BasicAuthenticationFilter: 요청에서 기본 인증 HTTP 헤더를 찾으려고 시도하고, 발견되면 헤더의 사용자 이름과 비밀번호로 사용자를 인증하려고 시도합니다.
  2. UsernamePasswordAuthenticationFilter: 사용자 이름/비밀번호 요청 매개변수/POST 본문을 찾으려고 시도하고, 발견되면 해당 값으로 사용자를 인증하려고 시도합니다.
  3. DefaultLoginPageGeneratingFilter: 해당 기능을 명시적으로 비활성화하지 않는 경우 로그인 페이지를 생성합니다. 이 필터는 Spring Security를 ​​활성화할 때 기본 로그인 페이지를 얻는 이유입니다.
  4. DefaultLogoutPageGeneratingFilter: 해당 기능을 명시적으로 비활성화하지 않는 경우 로그아웃 페이지를 생성합니다.
  5. FilterSecurityInterceptor: 귀하의 승인을 받았습니다.

농담

질문 - HTTP 요청이 왜 Spring Security 필터와 충돌하나요?
답변 - 가까워지려고 할 때마다 필터가 "잠깐! 먼저 확인해볼게!"라고 했기 때문이죠. ?

Spring 인증 서버 설정

Spring Authorization Server 사용을 시작하는 가장 쉬운 방법은 Spring Boot 기반 애플리케이션을 만드는 것입니다. start.spring.io를 사용하여 기본 프로젝트를 생성할 수 있습니다.

필요한 유일한 종속성은 구현("org.springframework.boot:spring-boot-starter-oauth2-authorization-server")입니다

더 많은 액션을 위해 2개를 더 추가할 예정입니다


2020-02-25 10:24:27.875  INFO 11116 --- [           main] o.s.s.web.DefaultSecurityFilterChain     : Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@46320c9a, org.springframework.security.web.context.SecurityContextPersistenceFilter@4d98e41b, org.springframework.security.web.header.HeaderWriterFilter@52bd9a27, org.springframework.security.web.csrf.CsrfFilter@51c65a43, org.springframework.security.web.authentication.logout.LogoutFilter@124d26ba, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@61e86192, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@10980560, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@32256e68, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@52d0f583, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@5696c927, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@5f025000, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@5e7abaf7, org.springframework.security.web.session.SessionManagementFilter@681c0ae6, org.springframework.security.web.access.ExceptionTranslationFilter@15639d09, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@4f7be6c8]|
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
Spring Security 구성 방법

최신 Spring Security 및/또는 Spring Boot 버전에서 Spring Security를 ​​구성하는 방법은 다음과 같은 클래스를 사용하는 것입니다. @EnableWebSecurity로 주석이 추가됩니다.


(1) : 프로토콜 엔드포인트를 위한 Spring Security 필터 체인입니다.
dependencies {
    implementation("org.springframework.boot:spring-boot-starter-oauth2-authorization-server")
    implementation("org.springframework.boot:spring-boot-starter-webflux")
    implementation("org.springframework.boot:spring-boot-starter-validation")
}
로그인 후 복사
로그인 후 복사
로그인 후 복사
(2) : 인증을 위한 Spring Security 필터 체인입니다.

(3) : 액세스 토큰 서명을 위한 com.nimbusds.jose.jwk.source.JWKSource의 인스턴스.
(4) : 서명된 액세스 토큰을 디코딩하기 위한 JwtDecoder의 인스턴스입니다.
(5) : Spring Authorization Server를 구성하기 위한 AuthorizationServerSettings의 인스턴스입니다.

애플리케이션에 특정 URL을 허용하도록 CORS를 구성할 수 있습니다

2020-02-25 10:24:27.875  INFO 11116 --- [           main] o.s.s.web.DefaultSecurityFilterChain     : Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@46320c9a, org.springframework.security.web.context.SecurityContextPersistenceFilter@4d98e41b, org.springframework.security.web.header.HeaderWriterFilter@52bd9a27, org.springframework.security.web.csrf.CsrfFilter@51c65a43, org.springframework.security.web.authentication.logout.LogoutFilter@124d26ba, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@61e86192, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@10980560, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@32256e68, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@52d0f583, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@5696c927, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@5f025000, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@5e7abaf7, org.springframework.security.web.session.SessionManagementFilter@681c0ae6, org.springframework.security.web.access.ExceptionTranslationFilter@15639d09, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@4f7be6c8]|
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

Cors구성
이 클래스는 CORS 규칙을 정의하는 데 사용됩니다. 이 경우:

  • addAllowedOrigin("http://localhost:3000/"): http://localhost:3000의 요청을 허용합니다. 이는 프런트엔드가 다른 포트에서 실행 중인 경우 로컬 개발에 유용합니다. 프로덕션에서는 이를 실제 도메인으로 바꾸세요.
  • addAllowedMethod("*"): 모든 HTTP 메서드(예: GET, POST, PUT, DELETE 등)를 허용합니다.
  • addAllowedHeader("*"): 요청의 모든 HTTP 헤더를 허용합니다.

UrlBasedCorsConfigurationSource

  • URL 패턴(예: /**)을 특정 CORS 구성에 매핑하는 클래스입니다.
  • registerCorsConfiguration("/", 구성): 정의된 CORS 규칙(구성)을 애플리케이션의 모든 엔드포인트(/)에 적용합니다.

와, 구성이 정말 많네요! 하지만 이것이 바로 Spring Framework의 마법입니다. 이는 뒤에서 모든 무거운 작업을 처리합니다.

이제 클라이언트를 구성할 차례입니다.

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-oauth2-authorization-server")
    implementation("org.springframework.boot:spring-boot-starter-webflux")
    implementation("org.springframework.boot:spring-boot-starter-validation")
}
로그인 후 복사
로그인 후 복사
로그인 후 복사

위에서 수행한 몇 가지 작업

  1. clientId: 액세스를 허용하는 고유 식별자
  2. clientAuthenticationMethod : 인증 방법 정의
  3. redirectUris 정의된 URL만 허용
  4. authorizationGrantTypes 인증_코드

사용자 세부정보서비스

UserDetailsService는 사용자 이름, 비밀번호 및 사용자 이름과 비밀번호로 인증하기 위한 기타 속성을 검색하기 위해 DaoAuthenticationProvider에서 사용됩니다. Spring Security는 UserDetailsService의 인메모리, JDBC 및 캐싱 구현을 제공합니다.

사용자 정의 UserDetailsService를 Bean으로 노출하여 사용자 정의 인증을 정의할 수 있습니다.

InMemoryUserDetailsManager

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    private static final String[] ALLOW_LIST = {"/oauth2/token", "/userinfo"};
    //This is primarily configured to handle OAuth2 and OpenID Connect specific endpoints. It sets up the security for the authorization server, handling token endpoints, client authentication, etc.
    @Bean (1)
    @Order(1)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = OAuth2AuthorizationServerConfigurer.authorizationServer();
        http
                .cors(Customizer.withDefaults())
                .authorizeHttpRequests(authz -> authz
                        .requestMatchers(ALLOW_LIST).permitAll()
                        .requestMatchers("/**", "/oauth2/jwks/").hasAuthority("SCOPE_keys.write")
                        .anyRequest()
                        .authenticated())
                .securityMatchers(matchers ->
                        matchers.requestMatchers(antMatcher("/oauth2/**"), authorizationServerConfigurer.getEndpointsMatcher()))
                .with(authorizationServerConfigurer, (authorizationServer) ->
                        authorizationServer
                        .oidc(Customizer.withDefaults()))    // Enable OpenID Connect 1.0

                // Redirect to the login page when not authenticated from the
                // authorization endpoint
                .exceptionHandling((exceptions) -> exceptions
                        .defaultAuthenticationEntryPointFor(
                                new LoginUrlAuthenticationEntryPoint("/login"),
                                new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
                        ))
                // Accept access tokens for User Info and/or Client Registration
                .oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults()));
        return http.build();
    }

    // This configuration is set up for general application security, handling standard web security features like form login for paths not specifically managed by the OAuth2 configuration.
    @Bean (2)
    @Order(2)
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
            throws Exception {
        http
                .authorizeHttpRequests((authorize) -> authorize
                        .requestMatchers("/login", "/error", "/main.css")
                        .permitAll()
                        .anyRequest()
                        .authenticated()
                )
                // Form login handles the redirect to the login page from the
                // authorization server filter chain
                .formLogin((login) -> login.loginPage("/login"));

        return http.build();
    }

    @Bean (3)
    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 (4)
    public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
        return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
    }

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

}
로그인 후 복사
로그인 후 복사

애플리케이션을 실행하면 Spring Authorization Server를 사용한 OIDC 및 OAuth2 설정이 올바르게 작동해야 합니다. 그러나 데모나 프로토타입 제작에 적합한 InMemoryUserDetailsManager를 사용했다는 것을 알 수 있습니다. 하지만 프로덕션 환경에서는 애플리케이션을 다시 시작하면 모든 데이터가 사라지므로 권장되지 않습니다.

스프링 보안의 JdbcUserDetailsManager

JdbcUserDetailsManager는 JDBC를 사용하여 관계형 데이터베이스에 연결하여 사용자 자격 증명과 역할을 처리하는 Spring Security의 기능입니다. 귀하의 애플리케이션이 Spring Security가 기대하는 사용자 테이블에 대한 표준 스키마와 함께 작동할 수 있을 때 이상적입니다.

Spring 보안 org/springframework/security/core/userdetails/jdbc/users.ddl에서 사용 가능한 스키마

@Configuration
public class CorsConfig {

    @Bean
    public UrlBasedCorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.addAllowedOrigin("http://localhost:3000/"); // Change to specific domains in production
        configuration.addAllowedMethod("*");
        configuration.addAllowedHeader("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}
로그인 후 복사
로그인 후 복사

InMemoryUserDetailsManager에서 JdbcUserDetailsManager로 전환하는 데 필요한 유일한 조정

2020-02-25 10:24:27.875  INFO 11116 --- [           main] o.s.s.web.DefaultSecurityFilterChain     : Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@46320c9a, org.springframework.security.web.context.SecurityContextPersistenceFilter@4d98e41b, org.springframework.security.web.header.HeaderWriterFilter@52bd9a27, org.springframework.security.web.csrf.CsrfFilter@51c65a43, org.springframework.security.web.authentication.logout.LogoutFilter@124d26ba, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@61e86192, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@10980560, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@32256e68, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@52d0f583, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@5696c927, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@5f025000, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@5e7abaf7, org.springframework.security.web.session.SessionManagementFilter@681c0ae6, org.springframework.security.web.access.ExceptionTranslationFilter@15639d09, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@4f7be6c8]|
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

이 구성은 Spring Security의 표준 테이블 스키마를 고수하는 애플리케이션에 효과적입니다. 그러나 사용자 정의가 필요한 경우(예: 로그인에 사용자 이름 대신 이메일을 사용하는 경우) 사용자 정의 UserDetailsService를 구현하면 필요한 적응성이 제공됩니다.

고객 엔터티가 있는 사용자 지정 UserDetailsService

공급자에 맞춤 CustomUserDetailsService를 추가해 보겠습니다. AuthenticationProvider에서 setUserDetailsService
를 사용하여 사용자 정의 서비스를 설정합니다.

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-oauth2-authorization-server")
    implementation("org.springframework.boot:spring-boot-starter-webflux")
    implementation("org.springframework.boot:spring-boot-starter-validation")
}
로그인 후 복사
로그인 후 복사
로그인 후 복사

맞춤 서비스

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    private static final String[] ALLOW_LIST = {"/oauth2/token", "/userinfo"};
    //This is primarily configured to handle OAuth2 and OpenID Connect specific endpoints. It sets up the security for the authorization server, handling token endpoints, client authentication, etc.
    @Bean (1)
    @Order(1)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = OAuth2AuthorizationServerConfigurer.authorizationServer();
        http
                .cors(Customizer.withDefaults())
                .authorizeHttpRequests(authz -> authz
                        .requestMatchers(ALLOW_LIST).permitAll()
                        .requestMatchers("/**", "/oauth2/jwks/").hasAuthority("SCOPE_keys.write")
                        .anyRequest()
                        .authenticated())
                .securityMatchers(matchers ->
                        matchers.requestMatchers(antMatcher("/oauth2/**"), authorizationServerConfigurer.getEndpointsMatcher()))
                .with(authorizationServerConfigurer, (authorizationServer) ->
                        authorizationServer
                        .oidc(Customizer.withDefaults()))    // Enable OpenID Connect 1.0

                // Redirect to the login page when not authenticated from the
                // authorization endpoint
                .exceptionHandling((exceptions) -> exceptions
                        .defaultAuthenticationEntryPointFor(
                                new LoginUrlAuthenticationEntryPoint("/login"),
                                new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
                        ))
                // Accept access tokens for User Info and/or Client Registration
                .oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults()));
        return http.build();
    }

    // This configuration is set up for general application security, handling standard web security features like form login for paths not specifically managed by the OAuth2 configuration.
    @Bean (2)
    @Order(2)
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
            throws Exception {
        http
                .authorizeHttpRequests((authorize) -> authorize
                        .requestMatchers("/login", "/error", "/main.css")
                        .permitAll()
                        .anyRequest()
                        .authenticated()
                )
                // Form login handles the redirect to the login page from the
                // authorization server filter chain
                .formLogin((login) -> login.loginPage("/login"));

        return http.build();
    }

    @Bean (3)
    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 (4)
    public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
        return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
    }

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

}
로그인 후 복사
로그인 후 복사

저장소

@Configuration
public class CorsConfig {

    @Bean
    public UrlBasedCorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.addAllowedOrigin("http://localhost:3000/"); // Change to specific domains in production
        configuration.addAllowedMethod("*");
        configuration.addAllowedHeader("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}
로그인 후 복사
로그인 후 복사

엔티티

@Configuration
public class Clients {
    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("stomble")
                .clientAuthenticationMethod(ClientAuthenticationMethod.NONE)
                .authorizationGrantTypes(types -> {
                    types.add(AuthorizationGrantType.AUTHORIZATION_CODE);
                    types.add(AuthorizationGrantType.REFRESH_TOKEN);
                })
                .redirectUris(redirectUri -> {
                    redirectUri.add("http://localhost:3000");
                    redirectUri.add("https://oauth.pstmn.io/v1/callback");
                    redirectUri.add("http://localhost:3000/signin-callback");
                })
                .postLogoutRedirectUri("http://localhost:3000")
                .scopes(score -> {
                    score.add(OidcScopes.OPENID);
                    score.add(OidcScopes.PROFILE);
                    score.add(OidcScopes.EMAIL);
                })
                .clientSettings(ClientSettings.builder()
                        .requireAuthorizationConsent(false)
                        .requireProofKey(true)
                        .build())
                .build();
        return new InMemoryRegisteredClientRepository(oidcClient);
    }
}
로그인 후 복사

보안 필터에서 우리는 Spring Security에게 이 서비스를 사용하도록 지시해야 합니다

.clientAuthentication(clientAuth -> clientAuth.authenticationProvider(authenticationProvider))

@Configuration
public class UserConfig {

    @Bean
    public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
        UserDetails userDetailFirst = User.builder()
                .username("user1")
                .password(passwordEncoder.encode("password"))
                .roles("USER")
                .build();
        UserDetails userDetailSecond = User.builder()
                .username("user2")
                .password(passwordEncoder.encode("password"))
                .roles("USER")
                .build();
        return new InMemoryUserDetailsManager(List.of(userDetailFirst, userDetailSecond));
    }
}

@Bean
public PasswordEncoder passwordEncoder() {
   return new BCryptPasswordEncoder();
}
로그인 후 복사

결론

여기에는 인증 처리를 위한 두 가지 강력한 선택이 있습니다.

  • JdbcUserDetailsManager: 애플리케이션이 Spring의 기본 스키마와 일치하는 경우 간단한 옵션입니다.
  • Custom UserDetailsService: 특수 필드 및 역할을 관리할 수 있는 유연성을 제공합니다.

JdbcUserDetailsManager를 선택하거나 사용자 정의 UserDetailsService를 구현하기로 결정하더라도 둘 다 애플리케이션에 확장 가능한 데이터베이스 지원 인증 시스템을 제공합니다.

위 내용은 유연한 데이터 기반 인증을 위한 사용자 정의 사용자 세부 정보 서비스를 갖춘 Spring 인증 서버 스프링 보안의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
저자별 최신 기사
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿