Spring Security的Java配置简介

2025/04/03

1. 概述

本教程介绍了Spring Security的Java配置,使用户无需使用XML即可轻松配置Spring Security。

Java配置在Spring 3.1中被添加到Spring框架中,在Spring 3.2中被扩展到Spring Security,并在用@Configuration标注的类中定义。

2. Maven依赖

要将Spring Security集成到Spring Boot应用程序中,我们需要在pom.xml中添加spring-boot-starter-security依赖:

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

3. 使用Java配置的Web安全

让我们从Spring Security Java配置的基本示例开始:

@Configuration
public class SecurityConfig {

    @Bean
    public UserDetailsService inMemoryUserDetailsService(PasswordEncoder passwordEncoder) {
        UserDetails user = User.builder()
                .username("user")
                .password(passwordEncoder.encode("password"))
                .roles("USER")
                .build();
        return new InMemoryUserDetailsManager(user);
    }
}

此配置类使用Spring Security设置基本的内存身份验证机制。

它定义了一个用户名/密码为user/password的用户。此外,它还分配了USER角色。我们使用InMemoryUserDetailsManager,它将用户详细信息存储在内存中。此外,我们需要一个PasswordEncoder Bean来对用户的密码进行编码:

@Bean
public PasswordEncoder passwordEncoder() {
    return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

4. Web安全

为了配置我们的授权规则和身份验证机制,我们定义了一个SecurityFilterChain Bean:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests((authz) -> authz
                    .anyRequest().authenticated())
            .httpBasic(withDefaults());
    return http.build();
}

上述配置确保对应用程序的任何请求都通过HTTP基本身份验证进行验证。

现在,当我们启动应用程序并导航到http://localhost:8080/app/时,会出现一个基本身份验证登录提示。输入用户名“user”和密码“password”后,服务器将验证凭据。如果身份验证成功,我们就可以访问请求的资源。

5. 表单登录

我们可以通过在声明中添加formLogin DSL调用来添加基于浏览器的登录和默认登录页面:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests((authz) -> authz
                    .anyRequest().authenticated())
            .formLogin(withDefaults())
            .httpBasic(withDefaults());
    return http.build();
}

在启动时,我们可以看到为我们生成了一个默认页面:

6. 角色授权

现在让我们使用角色在每个URL上配置一些简单的授权:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
            .authorizeHttpRequests((authz) -> authz
                    .requestMatchers("/").hasRole("USER")
                    .requestMatchers("/admin/**").hasRole("ADMIN")
                    .anyRequest().authenticated()
            )
            .formLogin(withDefaults())
            .httpBasic(withDefaults());
    return http.build();
}

这些限制了具有USER角色的用户对根(/)的访问以及具有ADMIN角色的用户对/admin/**的访问,而任何经过身份验证的用户都可以访问网站的其余部分。

注意我们如何使用类型安全的API hasRole。

7. 注销

与Spring Security的许多其他方面一样,注销具有框架提供的一些很好的默认设置。

默认情况下,注销请求会使会话无效,清除所有身份验证缓存,清除SecurityContextHolder,并重定向到登录页面。

这是一个简单的注销配置

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
            // ...
            .logout(withDefaults());
    return http.build();
}

但是,如果我们想要对可用的处理程序进行更多的控制,那么更完整的实现将是这样的:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http,
                                       LogoutSuccessHandler webSecurityUserLogoutHandler) throws Exception {
    http
            // ...
            .logout((logout) -> logout
                    .logoutSuccessUrl("/")
                    .invalidateHttpSession(true)
                    .logoutSuccessHandler(webSecurityUserLogoutHandler)
                    .deleteCookies("JSESSIONID")
            );
    return http.build();
}

@Bean
public LogoutSuccessHandler webSecurityUserLogoutHandler() {
    return (request, response, authentication) -> {
        System.out.println("User logged out successfully!");
        response.sendRedirect("/app");
    };
}

此设置提供了一个干净、安全的注销过程,同时提供了处理注销后操作的灵活性。

8. 身份验证

让我们看一下使用Spring Security允许身份验证的另一种方法。

8.1 内存身份验证

回想一下,一开始,我们使用内存配置:

@Bean
public UserDetailsService inMemoryUserDetailsService(
        PasswordEncoder passwordEncoder) {
    UserDetails user = User.builder()
            .username("user")
            .password(passwordEncoder.encode("password"))
            .roles("USER")
            .build();
    UserDetails admin = User.builder()
            .username("admin")
            .password(passwordEncoder.encode("password"))
            .roles("ADMIN")
            .build();
    return new InMemoryUserDetailsManager(user, admin);
}

8.2 JDBC身份验证

要将其迁移到JDBC,首先,我们将H2依赖添加到我们的pom.xml中:

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

另外,我们需要在application.properties中配置H2数据库:

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver
spring.sql.init.schema-locations=classpath:org/springframework/security/core/userdetails/jdbc/users.ddl

这定义了我们在下一部分中可以依赖的数据源:

@Bean
public UserDetailsManager jdbcUserDetailsManager(DataSource dataSource,
                                                 PasswordEncoder passwordEncoder) {
    JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager(dataSource);
    UserDetails user = User.builder()
            .username("user")
            .password(passwordEncoder.encode("password"))
            .roles("USER")
            .build();
    UserDetails admin = User.builder()
            .username("admin")
            .password(passwordEncoder.encode("password"))
            .roles("ADMIN")
            .build();
    jdbcUserDetailsManager.createUser(user);
    jdbcUserDetailsManager.createUser(admin);
    return jdbcUserDetailsManager;
}

另外,我们需要设置一个使用默认用户模式初始化的嵌入式数据源。

当然,对于上述两个示例,我们还需要按照概述定义PasswordEncoder Bean。Spring Security在org/springframework/security/core/userdetails/jdbc/users.ddl中提供了一个。

我们使用@Profile(inmemory或jdbc)来选择应该激活哪些UserDetailsManager Bean:

spring.profiles.active=inmemory

9. 总结

在本快速教程中,我们介绍了Spring Security的Java配置基础知识,并重点介绍了说明最简单配置场景的代码示例。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章