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 之前拦截和处理它。
SecurityFilter 大约有 4 个任务
- 首先,过滤器需要从请求中提取用户名/密码。它可以通过基本身份验证 HTTP 标头、表单字段或 cookie 等实现。
- 然后过滤器需要根据数据库等内容验证用户名/密码组合。
- 过滤器需要在身份验证成功后检查用户是否有权访问所请求的 URI。
- 如果请求通过了所有这些检查,那么过滤器就可以 让请求传递到您的 DispatcherServlet,即您的 @Controllers。
过滤器链
在实践中,我们会将一个过滤器分解为多个过滤器,然后您可以将它们链接在一起。
以下是传入 HTTP 请求的传输方式:
- 首先,它通过 LoginMethodFilter...
- 接下来,它会通过 AuthenticationFilter...
- 然后,它移动到 AuthorizationFilter...
- 最后,它到达您的 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。这些过滤器的顺序至关重要,因为请求是从链的顶部到底部处理的。
分析 Spring 的 FilterChain
深入研究链中每个过滤器的细节会让我们走得太远,但这里是一些关键过滤器的解释。为了更深入地了解其他内容,您可以探索 Spring Security 的源代码。
- BasicAuthenticationFilter:尝试在请求中查找基本身份验证 HTTP 标头,如果找到,则尝试使用标头的用户名和密码对用户进行身份验证。
- UsernamePasswordAuthenticationFilter:尝试查找用户名/密码请求参数/POST 正文,如果找到,则尝试使用这些值对用户进行身份验证。
- DefaultLoginPageGenerateFilter:如果您没有明确禁用该功能,则会为您生成登录页面。这个过滤器就是您在启用 Spring Security 时获得默认登录页面的原因。
- DefaultLogoutPageGenerateFilter:如果您没有明确禁用该功能,则会为您生成一个注销页面。
- 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") }
上面我们做了一些事情
- clientId:允许访问的唯一标识符
- clientAuthenticationMethod :定义身份验证方法
- redirectUris 仅允许定义的 URL
- authorizationGrantTypes 授权代码
用户详情服务
UserDetailsService 由 DaoAuthenticationProvider 用于检索 用户名、密码以及其他用于使用用户名和密码进行身份验证的属性。 Spring Security 提供了 UserDetailsService 的内存、JDBC 和缓存实现。
您可以通过将自定义 UserDetailsService 公开为 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(); } } </securitycontext></securitycontext>
一旦我们启动应用程序,我们的 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 标准表模式的应用程序有效。但是,如果您需要自定义(例如使用电子邮件而不是用户名进行登录),则实现自定义 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(); } } </securitycontext></securitycontext>
存储库
@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 的默认架构一致,这是一个简单的选项。
- 自定义 UserDetailsService:提供管理特殊字段和角色的灵活性。
无论您选择 JdbcUserDetailsManager 还是决定实现自定义 UserDetailsService,两者都将为您的应用程序配备可扩展的、数据库支持的身份验证系统。
以上是Spring 授权服务器 spring security 具有自定义用户详细信息服务,用于灵活的数据驱动身份验证的详细内容。更多信息请关注PHP中文网其他相关文章!

本文分析了2025年的前四个JavaScript框架(React,Angular,Vue,Susve),比较了它们的性能,可伸缩性和未来前景。 尽管由于强大的社区和生态系统,所有这些都保持占主导地位,但它们的相对人口

本文介绍了SnakeyAml中的CVE-2022-1471漏洞,这是一个允许远程代码执行的关键缺陷。 它详细介绍了如何升级春季启动应用程序到Snakeyaml 1.33或更高版本的降低风险,强调了依赖性更新

Node.js 20通过V8发动机改进可显着提高性能,特别是更快的垃圾收集和I/O。 新功能包括更好的WebSembly支持和精制的调试工具,提高开发人员的生产率和应用速度。

本文讨论了使用咖啡因和Guava缓存在Java中实施多层缓存以提高应用程序性能。它涵盖设置,集成和绩效优势,以及配置和驱逐政策管理最佳PRA

Java的类上载涉及使用带有引导,扩展程序和应用程序类负载器的分层系统加载,链接和初始化类。父代授权模型确保首先加载核心类别,从而影响自定义类LOA

本文探讨了在黄瓜步骤之间共享数据的方法,比较方案上下文,全局变量,参数传递和数据结构。 它强调可维护性的最佳实践,包括简洁的上下文使用,描述性

本文使用lambda表达式,流API,方法参考和可选探索将功能编程集成到Java中。 它突出显示了通过简洁性和不变性改善代码可读性和可维护性等好处


热AI工具

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

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

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

SublimeText3汉化版
中文版,非常好用

SublimeText3 英文版
推荐:为Win版本,支持代码提示!

螳螂BT
Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

Dreamweaver CS6
视觉化网页开发工具

WebStorm Mac版
好用的JavaScript开发工具