Spring Security基础知识

3

Spring Security的基础是什么?也就是Spring如何在内部设置安全性。为了让Spring Security开箱即用,需要提供哪些涉及到的bean呢?

1个回答

12

首先,我将解释如何将Spring Security引入您的应用程序中。

只需将以下依赖项添加到您的应用程序中。现在,当您运行应用程序时,默认情况下已实现spring security。(截至2021年4月,版本可能会在未来更改)

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-core</artifactId>
    <version>5.4.5</version>
</dependency>

仔细查看控制台,您会看到为默认用户生成的密码:user。该密码是一个需要使用的哈希值。

现在,当您访问应用程序中的任何URL时,您将受到Postman的限制。从您的浏览器中,您将看到一个登录页面,在该页面中您需要输入此用户名和密码,然后就可以访问您的URL。这是内置的Spring安全设置。

但是,在幕后发生了什么?

我将通过提醒您Servlets、Filters和Spring中的DispatcherServlet来回答这个问题。

DispatcherServlet是Spring MVC的基础,它将请求转发到您的控制器。基本上,DispatcherServlet也是一个servlet。

我可以在DispatcherServlet之前创建一系列过滤器,并在将请求转发到我的DispatcherServlet和控制器之前检查我的请求的身份验证和授权。这样,我就可以为我的应用程序引入安全性。这正是Spring Security所做的。

以下链接非常精细地突出显示了DispatcherServlet之前存在的所有过滤器以及这些过滤器的重要性。请参考以下链接:

Spring Security过滤器链的工作原理

现在,我们需要了解身份验证和授权是什么:

  1. 身份验证-使用您的应用程序的任何人都需要一些信息,并且您需要验证该用户的用户名、密码以允许他访问您的应用程序。如果他的用户名或密码错误,那就意味着他没有通过身份验证。
  2. 授权-一旦用户经过认证,您的应用程序可能会有一些URL仅允许管理员用户访问,而不允许普通用户访问。这就是基于用户角色授权用户访问应用程序某些部分的做法。

让我们来看看Spring Filter Chain中一些重要的Filter:

• BasicAuthenticationFilter:尝试查找请求中的Basic Auth HTTP标头,如果找到,则尝试使用标头的用户名和密码对用户进行身份验证。

• UsernamePasswordAuthenticationFilter:尝试查找用户名/密码请求参数/POST正文,如果找到,则尝试使用这些值对用户进行身份验证。

• DefaultLoginPageGeneratingFilter:为您生成一个登录页,如果您没有明确禁用该功能。这个过滤器是为什么在启用Spring Security后会获得默认登录页面的原因。

• DefaultLogoutPageGeneratingFilter:为您生成一个注销页面,如果您没有明确禁用该功能。

• FilterSecurityInterceptor:执行您的授权。

这些过滤器默认情况下为您提供了一个登录页面(您在浏览器中看到的)。此外,它们提供了一个注销页面、使用基本身份验证或表单登录的登录功能,以及防止CSRF攻击。

请记住,在将Spring Security添加到您的pom.xml之后,在开始时的登录页面就会出现。这是由以下类发生的:

public abstract class WebSecurityConfigurerAdapter implements
                WebSecurityConfigurer<WebSecurity> {

    protected void configure(HttpSecurity http) throws Exception {
            http
                .authorizeRequests()
                    .anyRequest().authenticated()
                    .and()
                .formLogin().and()
                .httpBasic();
        }
}

这个WebSecurityConfigurerAdapter类是我们所扩展的,我们重写了它的configure方法。按照上述说明,所有请求都需要通过表单登录方式进行基本认证。这个登录页面是Spring提供的默认页面,我们在访问URL时看到了它。

现在,下一个问题出现了,如果我们想要自己配置怎么办?下面的主题正好讨论了这个问题:

如何配置Spring Security?

为了配置Spring Security,我们需要有一个@Configuration、@EnableWebSecurity类,该类继承WebSecurityConfigurerAdapter类。

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity http) throws Exception {
      http
        .authorizeRequests()
          .antMatchers("/", "/home").permitAll()
          .anyRequest().authenticated()
          .and()
       .formLogin()
         .loginPage("/login")
         .permitAll()
         .and()
      .logout()
        .permitAll()
        .and()
      .httpBasic();
  }
}

您需要完成以上提到的配置。现在,您可以进行特定的安全配置,例如允许哪些URL,哪些需要进行身份验证,应用程序将执行哪些类型的身份验证以及特定URL允许的角色是什么。

因此,基本上您的所有身份验证和授权信息都在这里配置。还有关于CORS、CSRF和其他漏洞的其他配置也在这里完成,但这超出了基础知识的范围。

在上面的示例中,所有发送到//home的请求都允许任何用户访问并获取响应,但其他请求需要进行身份验证。此外,我们允许表单登录,即当访问除//home之外的任何请求时,用户将被呈现登录页面,在该页面中输入用户名和密码,并且该用户名/密码将使用基本身份验证进行验证,即发送HTTP基本身份验证标头进行验证。

到目前为止,我们已经添加了Spring Security,保护了我们的URL,并配置了Spring Security。但是,如何检查用户名和密码是否已通过身份验证?以下内容讨论了这一点:

您需要指定一些@Bean才能使Spring Security正常工作。为什么需要一些bean? 因为Spring容器需要这些bean在幕后实现安全性。

您需要提供这两个bean——UserDetailsService和PasswordEncoder。

UserDetailsService 负责向Spring容器提供您的用户。该用户可以存在于您的DB、内存或任何地方。例如,它可以存储在带有用户名、密码、角色和其他列的用户表中。

@Bean
public UserDetailsService userDetailsService() {
    return new MyUserDetailsService();
}

以上,我们提供了我们定制的MyUserDetailsService,它必须是Spring容器的UserDetailsService子类以便识别其目的。以下是示例实现:

public class MyDatabaseUserDetailsService implements UserDetailsService {

        UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 
         //  Load the user from the users table by username. If not found, throw UsernameNotFoundException.
         // Convert/wrap the user to a UserDetails object and return it.
        return someUserDetails;
    }
}
public interface UserDetails extends Serializable {

    String getUsername();

    String getPassword();

    // isAccountNonExpired,isAccountNonLocked,
    // isCredentialsNonExpired,isEnabled
}

您看,UserDetailsService提供了包含UserDetails对象的容器。

默认情况下,Spring提供了以下这些UserDetailsService的实现:

1. JdbcUserDetailsManager- 这是一个基于JDBC的UserDetailsService。您可以配置它以匹配您的用户表/列结构。

2. InMemoryUserDetailsManager- 将所有用户详细信息保存在内存中。这通常用于测试目的。

3. org.springframework.security.core.userdetail.User– 这是大多数自定义应用程序中使用的东西。您可以在自己的用户对象的自定义实现上扩展此User类。

现在,根据上面的内容,如果有任何请求需要进行身份验证,则由于我们已经准备好了UserDetailsService,我们将从由UserDetailsService返回的UserDetails对象中获取发送请求的用户并使用来自我们的UserDetailsService的用户名/密码验证其发送的用户名/密码。

通过这种方式,用户得到了认证。

注意: 从用户接收到的密码会自动哈希处理。因此,如果我们没有来自UserDetailsService的密码哈希表示形式,即使密码正确,也会失败。

为了防止这种情况,我们向容器提供PasswordEncoder bean,它将对UserDetails对象中的密码应用PasswordEncoder指定的哈希算法,并对其进行哈希。然后,它检查两个散列密码并验证或拒绝用户。

PasswordEncoder- 这提供了您密码的哈希用于安全目的。 为什么? 您不能/不应处理明文密码。那就跟Spring Security的主要目的背道而驰了。最好使用任何算法将其哈希。

@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
    return new BCryptPasswordEncoder();
}

现在,您可以在应用程序的任何位置自动装配此PasswordEncoder。

AuthenticationProvider-

在某些情况下,我们没有访问用户密码,但某些第三方以某种花哨的方式存储我们用户的信息。

在这些情况下,我们需要向我们的Spring容器提供AuthenticationProvider bean。一旦容器拥有了这个对象,它将尝试使用我们提供的实现进行身份验证,以与该第三方进行身份验证,该第三方将向我们提供一个UserDetails对象或任何其他对象,我们可以从中获得我们的UserDetails对象。

一旦获得了这个对象,那么就意味着我们已经通过身份验证,并且我们将带着我们的用户名、密码和权限/角色发送回一个UsernamePasswordAuthenticationToken。如果未获得,则可以抛出异常。

    @Bean
    public AuthenticationProvider authenticationProvider() {
        return new MyAuthenticationProvider();
    }

身份验证提供程序主要由一个方法组成,一个基本的实现可能如下所示:

public class MyAuthenticationProvider implements AuthenticationProvider {

        Authentication authenticate(Authentication authentication)  
                throws AuthenticationException {
            String username = authentication.getPrincipal().toString(); 
            String password = authentication.getCredentials().toString();

            User user = callThirdPartyService(username, password); 
            if (user == null) {                                     
                throw new AuthenticationException("Incorrect username/password");
            }
            return new UserNamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), user.getAuthorities());
        }
}

这就是Spring Security基础知识以及其内部功能,我们可以利用这些来自定义我们的安全实现。你可以在任何地方找到示例。更高级的主题,如JWT、Oauth2实现、CSRF预防、CORS允许等超出了范围。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接