小工具知识网

当前位置: 首页 > 行业知识 > 新版Spring Security6.2案例 - Authentication用户名密码

行业知识

新版Spring Security6.2案例 - Authentication用户名密码

2026-11-07 15:57:26 来源:互联网转载

前言:

前面有翻译了新版Spring Security6.2架构,包括总体架构,Authentication和Authorization,感兴趣可以直接点链接,这篇翻译官网给出的关于Authentication的Username/Password这页。

首先呢,官网就直接给出了基于用户名和密码的认证的代码,可以说是spring security的一个入门小案例,表单登录,输入用户名密码,和内存中的用户名密码匹配,如果匹配了就会成功登录。

Username/Password Authentication

验证用户的最常用方法之一是验证用户名和密码。Spring Security为使用用户名和密码进行身份验证提供了全面的支持。可以通过以下方式配置用户名密码认证

@Configuration@EnableWebSecuritypublic class SecurityConfig {@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated()).httpBasic(Customizer.withDefaults()).formLogin(Customizer.withDefaults());return http.build();}@Beanpublic UserDetailsService userDetailsService() {UserDetails userDetails = User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build();return new InMemoryUserDetailsManager(userDetails);}}

前面的配置会自动向 SecurityFilterChain 注册内存中 UserDetailsService,将DaoAuthenticationProvider 注册到默认的 AuthenticationManager,并启用表单登录和 HTTP 基本身份验证。

官网这边也列举了很多例子,我这边也都附上了超链接,因为前面代码是表单登录的,所以优先把超链接第一点,"我想了解表单登录如何工作"这节翻译了。

要了解更多关于usernamepassword身份验证的信息,请考虑以下用例:

  • 我想了解表单登录如何工作
  • 我想了解HTTP基本身份验证如何工作
  • 我想了解DaoAuthenticationProvider如何工作
  • 我想在内存中管理用户
  • 我想管理数据库中的用户
  • 我想在LDAP中管理用户
  • 我想发布一个用于自定义身份验证的AuthenticationManager bean
  • 我想自定义全局AuthenticationManager

    表单登录的原理

    Spring Security支持通过HTML表单提供用户名和密码。首先,我们看看如何将用户重定向到登录表单

    上图基于 SecurityFilterChain 流程图。 

    1. 首先,用户向未授权的资源 (/private) 发出未经身份验证的请求。
    2. Spring Security 的 AuthorizationFilter 通过抛出 AccessDeniedException 来指示未经身份验证的请求被拒绝。
    3. 由于用户未经过身份验证,因此 ExceptionTranslationFilter 将启动“启动身份验证”,并使用配置的 AuthenticationEntryPoint 将重定向发送到登录页。在大多数情况下,AuthenticationEntryPoint 是 LoginUrlAuthenticationEntryPoint 的实例。
    4. 浏览器请求重定向到的登录页面。
    5. 应用程序中的某些内容必须呈现在登录页面。

    当提交用户名和密码时,UsernamePasswordAuthenticationFilter对用户名和密码进行认证。UsernamePasswordAuthenticationFilter扩展了AbstractAuthenticationProcessingFilter,因此下面的图看起来应该非常相似。

    该图建立在SecurityFilterChain图的基础上。

    1.当用户提交其用户名和密码时,UsernamePasswordAuthenticationFilter 通过从 HttpServletRequest 实例中提取用户名和密码来创建 UsernamePasswordAuthenticationToken,UsernamePasswordAuthenticationToken是一种身份验证类型。

    2.接下来,将UsernamePasswordAuthenticationToken传递到要进行身份验证的AuthenticationManager实例中。AuthenticationManager的细节取决于用户信息的存储方式。

    3.如果身份验证失败,则定义为”失败“,并做如下:

            (1).清除SecurityContextHolder。

            (2).调用 RememberMeServices.loginFail。如果未配置“记住我”,则为空操作。请参阅 Javadoc 中的 RememberMeServices 接口。

            (3).调用AuthenticationFailureHandler。请参阅Javadoc中的AuthenticationFailureHandler类

    4.如果身份验证成功,则显定义为”成功“,并做如下:

            (1).SessionAuthenticationStrategy收到新登录的通知。请参阅Javadoc中的SessionAuthenticationStrategy接口。

            (2).身份验证设置在securitycontexholder上。请参阅Javadoc中的SecurityContextPersistenceFilter类。

            (3).RememberMeServices。调用loginSuccess。如果记得我没有配置,这是一个no-op。请参阅Javadoc中的memormeservices接口。

            (4).ApplicationEventPublisher发布一个InteractiveAuthenticationSuccessEvent事件。

            (5).调用AuthenticationSuccessHandler。通常,这是一个SimpleUrlAuthenticationSuccessHandler,当我们重定向到登录页面时,它会重定向到由ExceptionTranslationFilter保存的请求。

    默认情况下,Spring Security 表单登录处于启用状态。但是,一旦提供了任何基于 servlet 的配置,就必须显式提供基于表单的登录。以下示例显示了一个最小的显式 Java 配置:

    public SecurityFilterChain filterChain(HttpSecurity http) {http.formLogin(withDefaults());// ...}

    在前面的配置中,Spring Security 呈现默认登录页面。大多数生产应用程序都需要自定义登录表单。

    以下配置演示了如何提供自定义登录表单。

    ublic SecurityFilterChain filterChain(HttpSecurity http) {http.formLogin(form -> form.loginPage("/login").permitAll());// ...}

    在 Spring Security 配置中指定登录页面时,用户负责呈现页面。以下 Thymeleaf 模板生成符合 /login 登录页的 HTML 登录表单。

    Please Log In

    Please Log In

    Invalid username and password.You have been logged out.

    关于默认 HTML 表单,有几个关键点:

    • 表单应执行 post 到 /login。
    • 该表单需要包含一个 CSRF 令牌,该令牌由 Thymeleaf 自动包含。
    • 表单应在名为 username 的参数中指定用户名。
    • 表单应在名为 password 的参数中指定密码。
    • 如果找到名为 error 的 HTTP 参数,则表示用户未能提供有效的用户名或密码。
    • 如果找到名为 logout 的 HTTP 参数,则表示用户已成功注销。

      许多用户只需要自定义登录页面即可。但是,如果需要,您可以使用其他配置自定义前面显示的所有内容。

      如果您使用 Spring MVC,则需要一个将 GET /login 映射到我们创建的登录模板的控制器。以下示例显示了一个最小的 LoginController:

      @Controllerclass LoginController {@GetMapping("/login")String login() {return "login";}}

      最后在官网Username/Password这页中,还有描写关于自定义身份的authentication bean和全局authenticationManger,也就是上面超链接最后2点,先翻译出来,再后续博客中接着讲,本文可以着重看前面的表单登录即可。

      发布一个AuthenticationManager bean

      一个相当常见的要求是发布 AuthenticationManager bean 以允许自定义身份验证,例如在 @Service 或 Spring MVC @Controller中。例如,您可能希望通过 REST API 而不是使用表单登录对用户进行身份验证。

      @Configuration@EnableWebSecuritypublic class SecurityConfig {@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests((authorize) -> authorize.requestMatchers("/login").permitAll().anyRequest().authenticated());return http.build();}@Beanpublic AuthenticationManager authenticationManager(UserDetailsService userDetailsService,PasswordEncoder passwordEncoder) {DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();authenticationProvider.setUserDetailsService(userDetailsService);authenticationProvider.setPasswordEncoder(passwordEncoder);return new ProviderManager(authenticationProvider);}@Beanpublic UserDetailsService userDetailsService() {UserDetails userDetails = User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build();return new InMemoryUserDetailsManager(userDetails);}@Beanpublic PasswordEncoder passwordEncoder() {return PasswordEncoderFactories.createDelegatingPasswordEncoder();}}

      有了上述配置,您就可以创建一个使用AuthenticationManager的@RestController,如下所示:

      @RestControllerpublic class LoginController {private final AuthenticationManager authenticationManager;public LoginController(AuthenticationManager authenticationManager) {this.authenticationManager = authenticationManager;}@PostMapping("/login")public ResponseEntity login(@RequestBody LoginRequest loginRequest) {Authentication authenticationRequest =UsernamePasswordAuthenticationToken.unauthenticated(loginRequest.username(), loginRequest.password());Authentication authenticationResponse =this.authenticationManager.authenticate(authenticationRequest);// ...}public record LoginRequest(String username, String password) {}}

      本例中,如果需要,您有责任将经过身份验证的用户保存在securitycontextrerepository中。例如,如果使用HttpSession在请求之间持久化SecurityContext,您可以使用httpessionsecuritycontextrepository。

      自定义 AuthenticationManager

      通常,Spring Security 在内部构建一个 AuthenticationManager,该管理器由 DaoAuthenticationProvider 组成,用于用户名/密码身份验证。在某些情况下,可能仍需要自定义 Spring Security 使用的 AuthenticationManager 实例。例如,您可能需要简单地为缓存用户禁用凭据擦除。您可以使用以下配置发布 AuthenticationManager:

      @Configuration@EnableWebSecuritypublic class SecurityConfig {@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests((authorize) -> authorize.requestMatchers("/login").permitAll().anyRequest().authenticated()).httpBasic(Customizer.withDefaults()).formLogin(Customizer.withDefaults());return http.build();}@Beanpublic AuthenticationManager authenticationManager(UserDetailsService userDetailsService,PasswordEncoder passwordEncoder) {DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();authenticationProvider.setUserDetailsService(userDetailsService);authenticationProvider.setPasswordEncoder(passwordEncoder);ProviderManager providerManager = new ProviderManager(authenticationProvider);providerManager.setEraseCredentialsAfterAuthentication(false);return providerManager;}@Beanpublic UserDetailsService userDetailsService() {UserDetails userDetails = User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build();return new InMemoryUserDetailsManager(userDetails);}@Beanpublic PasswordEncoder passwordEncoder() {return PasswordEncoderFactories.createDelegatingPasswordEncoder();}}

      或者,您可以利用用于构建 Spring Security 的全局 AuthenticationManager 的 AuthenticationManagerBuilder 作为 Bean 发布的事实。您可以按如下方式配置构建器:

      @Configuration@EnableWebSecuritypublic class SecurityConfig {@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {// ...return http.build();}@Beanpublic UserDetailsService userDetailsService() {// Return a UserDetailsService that caches users// ...}@Autowiredpublic void configure(AuthenticationManagerBuilder builder) {builder.eraseCredentials(false);}}

      参考文献:

      《spring boot官网》

上一篇:宝塔面板代码查询攻略,轻松成为代码查询高手!

下一篇:QQ机器人PHP源码,核心技术轻松掌握,一键实现机器人功能!