首页 Java java教程 Spring 授权服务器 spring security 具有自定义用户详细信息服务,用于灵活的数据驱动身份验证

Spring 授权服务器 spring security 具有自定义用户详细信息服务,用于灵活的数据驱动身份验证

Jan 23, 2025 pm 12:07 PM

Spring授权服务器

Spring 授权服务器是一个旨在实现 OAuth 2.1 和 OpenID Connect 1.0 规范以及其他相关标准的框架。它基于 Spring Security 构建,为创建符合 OpenID Connect 1.0 和 OAuth2 授权服务器解决方案的身份提供者提供了安全、轻量级和可定制的基础。

功能列表

什么是 Spring Security 以及它是如何工作的?

简短回答
Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架。它是保护基于 Spring 的应用程序的事实标准。

本质上,Spring Security 本质上是 servlet 过滤器的集合,旨在通过强大的身份验证和授权功能来增强您的 Web 应用程序。

Spring Security 还与 Spring Web MVC 或 Spring Boot 等框架很好地配合,支持 OAuth2 和 SAML 等标准。它自动生成登录和注销接口,并保护您的应用程序免受 CSRF 等常见安全漏洞的影响。

嗯,这不是很有帮助,不是吗?

让我们深入研究网络安全,以掌握其安全工作流程的要点。

要成为Spring Security专家,首先要掌握这三个核心概念:

  • 身份验证
  • 授权
  • Servlet 过滤器

注意 - 不要绕过此部分;它为所有 Spring Security 功能奠定了基础。

验证

您需要在线访问您的银行账户以查看余额或进行交易。通常这是使用用户名和密码完成的

用户:“我是 John Doe。我的用户名是:johndoe1985。”
银行系统:“请验证您的身份。您的密码是多少?”
用户:“我的密码是:secureB@nk2023。”
银行系统:“欢迎,John Doe。这是您的帐户概览。”

授权

对于基本应用程序,仅进行身份验证就足够了:用户登录后,他们将被授予访问应用程序所有区域的权限。

但是,在大多数应用程序中,都有权限或角色在发挥作用。

用户:“让我来玩一下该交易......”
银行系统:“等一下,我需要先检查您的权限……是的,John Doe 先生,您的权限级别正确。尽情享受吧。”
用户:“我转1M哈哈哈……​开玩笑开玩笑”

小服务程序过滤器

现在,让我们探索 Servlet 过滤器。它们与身份验证和授权有何关系?

为什么使用 Servlet 过滤器?
每个 Spring Web 应用程序都围绕一个 servlet 展开:值得信赖的 DispatcherServlet。它的主要作用是将传入的 HTTP 请求(例如来自浏览器的请求)路由到适当的 @Controller 或 @RestController 进行处理。

事情是这样的:DispatcherServlet 本身没有任何内置的安全功能,并且您可能不想直接在 @Controller 中处理原始 HTTP Basic Auth 标头。理想情况下,在请求到达您的 @Controllers

之前就应该处理身份验证和授权

幸运的是,在 Java Web 环境中,您可以通过在 servlet 之前放置过滤器来实现这一点。这意味着您可以考虑创建一个 SecurityFilter 并将其设置在 Tomcat(servlet 容器/应用程序服务器)中,以在每个传入的 HTTP 请求到达您的 servlet 之前拦截和处理它。

Security context

SecurityFilter 大约有 4 个任务

  1. 首先,过滤器需要从请求中提取用户名/密码。它可以通过基本身份验证 HTTP 标头、表单字段或 cookie 等实现。
  2. 然后过滤器需要根据数据库等内容验证用户名/密码组合。
  3. 过滤器需要在身份验证成功后检查用户是否有权访问所请求的 URI。
  4. 如果请求通过了所有这些检查,那么过滤器就可以 让请求传递到您的 DispatcherServlet,即您的 @Controllers。

过滤器链

在实践中,我们会将一个过滤器分解为多个过滤器,然后您可以将它们链接在一起。

以下是传入 HTTP 请求的传输方式:

  1. 首先,它通过 LoginMethodFilter...
  2. 接下来,它会通过 AuthenticationFilter...
  3. 然后,它移动到 AuthorizationFilter...
  4. 最后,它到达您的 servlet。

此设置称为 FilterChain。

通过使用过滤器(或一系列过滤器),您可以有效地管理应用程序中的所有身份验证和授权挑战,而无需更改 @RestControllers 或 @Controllers 的核心实现。

Spring 的 DefaultSecurityFilterChain

假设您已经正确配置了 Spring Security 并启动了您的 Web 应用程序。您会注意到一条日志消息,如下所示:

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 请求到达时,它会依次通过这 15 个过滤器中的每一个,然后最终到达您的 @RestControllers。这些过滤器的顺序至关重要,因为请求是从链的顶部到底部处理的。

security chain

分析 Spring 的 FilterChain

深入研究链中每个过滤器的细节会让我们走得太远,但这里是一些关键过滤器的解释。为了更深入地了解其他内容,您可以探索 Spring Security 的源代码。

  1. BasicAuthenticationFilter:尝试在请求中查找基本身份验证 HTTP 标头,如果找到,则尝试使用标头的用户名和密码对用户进行身份验证。
  2. UsernamePasswordAuthenticationFilter:尝试查找用户名/密码请求参数/POST 正文,如果找到,则尝试使用这些值对用户进行身份验证。
  3. DefaultLoginPageGenerateFilter:如果您没有明确禁用该功能,则会为您生成登录页面。这个过滤器就是您在启用 Spring Security 时获得默认登录页面的原因。
  4. DefaultLogoutPageGenerateFilter:如果您没有明确禁用该功能,则会为您生成一个注销页面。
  5. FilterSecurityInterceptor:是否经过您的授权。

开玩笑

问题 - 为什么 HTTP 请求会被 Spring Security 过滤器破坏?
回答 - 因为每次它试图靠近时,过滤器都会说:“等一下!让我先检查一下你!” ?

是的,休息......哇,等等......这一次的安全讨论太多了!

设置 Spring 授权服务器

开始使用 Spring 授权服务器的最简单方法是创建基于 Spring Boot 的应用程序。您可以使用start.spring.io生成一个基本项目。

唯一需要的依赖是实现(“org.springframework.boot:spring-boot-starter-oauth2-authorization-server”)

我们将添加另外两个来执行更多操作

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 注解。

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):协议端点的 Spring Security 过滤器链。
(2) :用于身份验证的 Spring Security 过滤器链。
(3) : com.nimbusds.jose.jwk.source.JWKSource 的实例,用于签署访问令牌。
(4) : 用于解码签名访问令牌的 JwtDecoder 实例。
(5) : AuthorizationServerSettings 实例,用于配置 Spring Authorization Server。

让我们配置 CORS 以允许某些 URL 访问我们的应用程序

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("/", configuration):将定义的 CORS 规则(配置)应用于应用程序中的所有端点 (/)。

哇,配置太多了!但这就是 Spring 框架的魔力——它处理幕后的所有繁重工作。

是时候配置客户端了

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 授权代码

用户详情服务

UserDetailsS​​ervice 由 DaoAuthenticationProvider 用于检索 用户名密码以及其他用于使用用户名和密码进行身份验证的属性。 Spring Security 提供了 UserDetailsS​​ervice 的内存、JDBC 和缓存实现。

您可以通过将自定义 UserDetailsS​​ervice 公开为 bean 来定义自定义身份验证。

内存中用户详细信息管理器

@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();
    }

}
登录后复制
登录后复制

一旦我们启动应用程序,我们的 OIDC 和 OAuth2 设置与 Spring 授权服务器应该可以正常运行。但是,您会注意到我们使用了 InMemoryUserDetailsManager,它非常适合演示或原型设计。但对于生产环境,这是不可取的,因为应用程序重新启动后所有数据都会消失。

Spring Security 中的 JdbcUserDetailsManager

JdbcUserDetailsManager 是 Spring Security 中的一项功能,它使用 JDBC 通过连接到关系数据库来处理用户凭据和角色。当您的应用程序可以使用 Spring Security 期望的用户表的标准架构时,这是理想的选择。

可从 Spring security 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 标准表模式的应用程序有效。但是,如果您需要自定义(例如使用电子邮件而不是用户名进行登录),则实现自定义 UserDetailsS​​ervice 可提供必要的适应性。

带有客户实体的自定义 UserDetailsS​​ervice

让我们向提供程序添加自定义 CustomUserDetailsS​​ervice。在 AuthenticationProvider 中使用 setUserDetailsS​​ervice
设置自定义服务

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 的默认架构一致,这是一个简单的选项。
  • 自定义 UserDetailsS​​ervice:提供管理特殊字段和角色的灵活性。

无论您选择 JdbcUserDetailsManager 还是决定实现自定义 UserDetailsS​​ervice,两者都将为您的应用程序配备可扩展的、数据库支持的身份验证系统。

以上是Spring 授权服务器 spring security 具有自定义用户详细信息服务,用于灵活的数据驱动身份验证的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1654
14
CakePHP 教程
1413
52
Laravel 教程
1306
25
PHP教程
1252
29
C# 教程
1225
24
公司安全软件导致应用无法运行?如何排查和解决? 公司安全软件导致应用无法运行?如何排查和解决? Apr 19, 2025 pm 04:51 PM

公司安全软件导致部分应用无法正常运行的排查与解决方法许多公司为了保障内部网络安全,会部署安全软件。...

如何将姓名转换为数字以实现排序并保持群组中的一致性? 如何将姓名转换为数字以实现排序并保持群组中的一致性? Apr 19, 2025 pm 11:30 PM

将姓名转换为数字以实现排序的解决方案在许多应用场景中,用户可能需要在群组中进行排序,尤其是在一个用...

如何优雅地获取实体类变量名构建数据库查询条件? 如何优雅地获取实体类变量名构建数据库查询条件? Apr 19, 2025 pm 11:42 PM

在使用MyBatis-Plus或其他ORM框架进行数据库操作时,经常需要根据实体类的属性名构造查询条件。如果每次都手动...

如何使用MapStruct简化系统对接中的字段映射问题? 如何使用MapStruct简化系统对接中的字段映射问题? Apr 19, 2025 pm 06:21 PM

系统对接中的字段映射处理在进行系统对接时,常常会遇到一个棘手的问题:如何将A系统的接口字段有效地映�...

IntelliJ IDEA是如何在不输出日志的情况下识别Spring Boot项目的端口号的? IntelliJ IDEA是如何在不输出日志的情况下识别Spring Boot项目的端口号的? Apr 19, 2025 pm 11:45 PM

在使用IntelliJIDEAUltimate版本启动Spring...

Java对象如何安全地转换为数组? Java对象如何安全地转换为数组? Apr 19, 2025 pm 11:33 PM

Java对象与数组的转换:深入探讨强制类型转换的风险与正确方法很多Java初学者会遇到将一个对象转换成数组的�...

电商平台SKU和SPU数据库设计:如何兼顾用户自定义属性和无属性商品? 电商平台SKU和SPU数据库设计:如何兼顾用户自定义属性和无属性商品? Apr 19, 2025 pm 11:27 PM

电商平台SKU和SPU表设计详解本文将探讨电商平台中SKU和SPU的数据库设计问题,特别是如何处理用户自定义销售属...

如何利用Redis缓存方案高效实现产品排行榜列表的需求? 如何利用Redis缓存方案高效实现产品排行榜列表的需求? Apr 19, 2025 pm 11:36 PM

Redis缓存方案如何实现产品排行榜列表的需求?在开发过程中,我们常常需要处理排行榜的需求,例如展示一个�...

See all articles