春季安全:身份验证和授权(第1部分)
#api #java #springboot #springsecurity

当我们创建用于提供服务或Web应用程序的API时,为项目提供身份验证和授权至关重要。在这篇文章中,我将讲述如何使用弹簧安全模块制作必要的安全配置。

开始,我将创建一个带有两个端点“/”/“,”/“欢迎”的RestController,并尝试运行该应用程序并查看发生的情况。

@RestController
public class HomeController {   
   @GetMapping("/")
   public String sayHello() {
      return "Welcome to Spring Security!";
   }
   @GetMapping("/welcome")
   public String sayHello() {
      return "Welcome to Spring Security(Protected)!";
   }

}

这是结果!

base endpoint without spring security module
让我们看看如果我们将Spring Security添加到项目依赖性
中会发生什么

<dependency>             
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
</dependency>

现在,如果我们运行应用程序并尝试击中和端点,您将被重定向到下面的登录页面。

Endpoint after adding spring security
好吧,我不记得添加/login端点或配置任何用户。但是如何渲染登录页面,为什么?
仅通过将依赖项添加到ClassPath来启用弹簧安全。默认情况下,所有路由/端点都需要身份验证才能访问。但是,哪些证书用于登录?我不记得配置任何凭据!这是因为如果我们不配置它,弹簧将在运行时间提供默认的随机凭据

auto generated password

安全配置

Spring MVC在通过一组过滤器后,将所有传入的HTTP请求发送到名为DispatcherServlet的单个Servlet。调度员负责将HTTP请求发送到相应的控制器。
当我们将Spring Security添加到类Path时,将一组安全过滤器添加到过滤器链中。这些过滤器拦截了每个HTTP请求,然后才能进入控制器。这是Spring Security验证HTTP请求并决定转发或拒绝请求的地方。
Spring Security Architecture

默认情况下,每个HTTP请求都是认证的。但是,我们可以通过制作自己的安全配置来覆盖这种行为

为了做到这一点,我们需要提供我们的自定义SecurityFilterChain bean

默认行为是由于bean

@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests().anyRequests().authenticated();
    http.formLogin();
    http.httpBasic();
    return http.build();
}

现在,我们可以在这样的全新配置类中拥有自己的豆子。

@Configuration
class ApiSecurityConfiguration {    
   @Bean
   SecurityFilterChain customSecurityFilterChain(HttpSecurity http) throws Exception {
      http.authorizeHttpRequests((requests)-> requests
           .requestMatchers("/welcome").authenticated()              
               .anyRequests().authenticated());
       .formLogin();
       .httpBasic(Customizer.withDefaults());
      return http.build();
   }
}

上面的配置使/welcome端点是身份验证的,并且除了公开访问的任何端点

以外的任何端点

,但我们仍然没有设置自己的用户集。
在使用用户设置前进之前,我们需要了解如何处理请求。

UserDetailsService

UserDetailsService(接口)-> loadUserByUserName(String username)
这是加载用户特定数据的核心接口

UserDetailsManager(接口扩展了UserDetailsservice)允许创建,更新,删除UserDetails(请参阅下面的方法签名)的USERDETAILSERVICE的扩展

  • createUser(UserDetails user)
  • updateUser(UserDetails user)
  • deleteUser(String username user)
  • changePassword(String oldPwd, String newPwd)
  • userExists(String username)

Spring Security提供的示例实现类(用于UserDetailsManager接口)为InMemoryUserDetailsManagerJdbcUserDetailsManagerLdapUserDetailsManager

上述所有接口和类都使用界面UserDetails,它提供核心用户信息

为了证明用户详细信息的身份,我们需要实现UserDetailsService接口并将其作为弹簧bean提供。我们可以使用Spring提供的默认实现,或者我们可以拥有自己的自定义实现。
如果我们要构建企业应用程序,建议您拥有自己的自定义实施。但是为简单起见,我们将使用提供的默认实现
在这篇文章中,我们将看到如何配置InMemoryUserDetailsManager

@Bean
UserDetailsService userDetailsService() {
   UserDetails admin = User.withDefaultPasswordEncoder()                         
                           .username("admin")                                
                           .password("12345")                                
                           .authorities("admin")                                 
                           .build();    
   UserDetails user = User.withDefaultPasswordEncoder()
               .username("user")
               .password("54321")
               .authorities("read")
               .build();
   return new InMemoryUserDetailsManager(user, admin);
    }

在这里,我们正在使用DefaultPasswordEncoder()。但是,总的来说,我们需要提供一个PasswordEncoder豆。春季为我们提供了许多密码编码器
例如,

@Bean
PasswordEncoder passwordEncoder() {
   return new BCryptyPasswordEncoder()
}

现在,使用这些配置,我们可以使用Inmemory中提供的用户访问受保护的路线。
我们还可以使用Spring Security提供的默认JdbcUserDetailsManager Bean从数据库中获取用户
为了使用它,我们需要有一个数据源
我们将使用MySQL进行此应用程序。 DataSource配置是在applications.properties文件中完成的,或者我们有application.yml

spring:
  datasource:
    platform: mysql
    url: jdbc:mysql://localhost:5432/mydb
    username: foo
    password: bar
    jpa:
       show-sql: true
       properties:
          hibernate:
        format_sql: true

默认情况下,JdbcUserDetailsManager默认实现,假设我们遵循具有users(id, username, password, enabled)authorities(id, username, authority)表的数据库架构
因此,如果我们使用的是JdbcUserDetailsManager实现,我们需要遵循相同的DDL模式
JdbcUserDetailsManager bean替换InMemoryUserDetailsManager bean(确保通过datasoucrce对象)

@Bean
UserDetailsService userDetailsService(DataSource dataSource) {
   return new JdbcUserDetailsManager(dataSource)
}

好吧,我们已经配置了我们的应用程序以使用inmemory userDetails,并配置为使用默认实现从数据库中获取用户详细信息。

,但这对于企业应用程序还不够,因为我们可能会根据客户建议在数据库中遵循同一命名约定。在这种情况下,我们不能使用JdbcUserDetailsManager

但这是另一天的话题。
我将在下一篇文章中写更多有关它的信息。