【发布时间】:2021-07-23 13:07:54
【问题描述】:
什么是 Spring Security 的基础知识,即 Spring 如何在内部设置安全性。为 Spring Security 提供开箱即用的所有相关 bean?
【问题讨论】:
标签: java spring spring-boot spring-mvc spring-security
什么是 Spring Security 的基础知识,即 Spring 如何在内部设置安全性。为 Spring Security 提供开箱即用的所有相关 bean?
【问题讨论】:
标签: java spring spring-boot spring-mvc spring-security
我将首先解释如何将 Spring Security 引入您的应用程序。
只需将以下依赖项添加到您的应用程序。现在,当您运行应用程序时,默认情况下会实现 spring 安全性。 (截至 2021 年 4 月,版本可能会在未来发生变化)
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>5.4.5</version>
</dependency>
仔细查看控制台,您将看到为默认用户生成的密码:user。密码是您需要使用的哈希值。
当您现在从应用程序访问任何 URL 时,您将被 Postman 限制。在您的浏览器中,您将看到一个登录页面,您需要在其中输入此用户名和密码,然后您将进入您的 URL。这设置了内置的 Spring Security。
但是幕后发生了什么?
我将通过提醒您Spring中的Servlet和Filters和DispatcherServlet来回答这个问题。
DispatcherServlet 是 Spring MVC 的基础,它将请求转发到您的控制器。基本上,DispatcherServlet 也是一个 servlet。
我可以在 DispatcherServlet 之前创建一个过滤器链,并在转发请求以访问我的 DispatcherServlet 和我的控制器之前检查我的身份验证和授权请求。这样,我可以为我的应用程序引入安全性。这正是 Spring Security 所做的。
下面的链接非常巧妙地突出了 DispatcherServlet 之前的所有过滤器以及这些过滤器的重要性。请参考以下链接:
How Spring Security Filter Chain works
现在,我们需要了解什么是身份验证和授权:
让我们看一下过滤器链中一些重要的Spring过滤器:
• BasicAuthenticationFilter: 尝试在请求中查找 Basic Auth HTTP 标头,如果找到,则尝试使用标头的用户名和密码对用户进行身份验证。
• UsernamePasswordAuthenticationFilter: 尝试查找用户名/密码请求参数/POST 正文,如果找到,则尝试使用这些值对用户进行身份验证。
• DefaultLoginPageGeneratingFilter:如果您没有明确禁用该功能,则会为您生成登录页面。这个过滤器是你在启用 Spring Security 时获得默认登录页面的原因。
• DefaultLogoutPageGeneratingFilter:如果您没有明确禁用该功能,则会为您生成一个注销页面。
• FilterSecurityInterceptor:您的授权。
默认情况下,这些过滤器会为您提供您在浏览器上看到的登录页面。此外,它们还提供注销页面、使用基本身份验证或表单登录登录的能力,以及防止 CSRF 攻击。
请记住,在您的 pom.xml 中添加 Spring Security 之后开始的登录页面。这是因为下面的课程:
public abstract class WebSecurityConfigurerAdapter implements
WebSecurityConfigurer<WebSecurity> {
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.httpBasic();
}
}
这个 WebSecurityConfigurerAdapter 类是我们扩展的,我们覆盖了它的配置方法。如上所述,所有请求都需要通过表单登录方法进行基本身份验证。这个登录页面是我们访问 URL 时看到的 Spring 提供的默认页面。
现在,下一个问题出现了,如果我们想自己做这个配置怎么办?以下主题正是讨论了这一点:
如何配置 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 Basic Auth Header 进行身份验证。
到目前为止,我们已经添加了 Spring Security,保护了我们的 URL,配置了 Spring Security。但是,我们将如何检查用户名和密码以进行身份验证?下面讨论这个:
你需要指定一些@Beans 才能让 Spring Security 工作。为什么需要一些豆子? 因为 Spring Container 需要这些 bean 来实现底层的安全性。
您需要提供这两个 bean – UserDetailsService 和 PasswordEncoder。
UserDetailsService – 这负责将您的用户提供给 Spring 容器。用户可以出现在您的数据库、内存中,任何地方。例如:它可以与用户名、密码、角色和其他列一起存储在用户表中。
@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();
}
AuthenticationProvider 主要由一个方法组成,基本实现可能如下所示:
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 津贴超出了范围。
【讨论】: