Bootstrap

重学SpringBoot3-集成Spring Security(二)

更多SpringBoot3内容请关注我的专栏:《SpringBoot3》
期待您的点赞👍收藏⭐评论✍


在上一节中,我们讨论了 Spring Security 的认证功能,通过实现用户身份验证来确保系统的安全性。而在本节中,我们将深入了解授权机制,如何控制用户在系统中可以访问的资源和操作。

1. 什么是授权?

授权(Authorization)是指控制用户是否有权访问某个资源或执行某个操作的过程。与认证不同,认证是确认用户的身份,而授权是基于用户的身份决定他们能做什么。例如,即使用户已经登录,如果他们没有足够的权限,他们也无法访问系统的某些资源。

在 Spring Security 中,授权主要基于角色和权限的概念进行控制。

  • 角色(Role):通常用来定义一组权限,比如 ADMIN 角色可能包含管理用户、查看日志等权限。
  • 权限(Authority):具体的操作或资源访问权,比如 READ_PRIVILEGESWRITE_PRIVILEGES 等。

Spring Security 提供了基于角色和权限的访问控制机制,使我们可以轻松管理系统中的授权逻辑。


2. 授权的基础配置

要实现授权,我们需要在 Spring Security 的配置类中定义用户的角色和访问策略。下面是一个简单的 Spring Boot 3 + Spring Security 授权示例。

2.1 配置 SecurityFilterChain

package com.coderjia.boot313security.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

/**
 * @author CoderJia
 * @create 2024/10/13 下午 01:57
 * @Description
 **/
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf(Customizer.withDefaults())
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/").permitAll()  // 公开访问
                        .requestMatchers("/admin/**").hasRole("ADMIN")   // 只有 ADMIN 角色可以访问 /admin 目录下的资源
                        .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")  // USER 和 ADMIN 角色都可以访问 /user 目录下的资源
                        .anyRequest().authenticated()  // 其他接口需认证
                )
                .formLogin(form -> form
                        .loginPage("/login")
                        .permitAll()
                )
                .logout((logout) -> logout
                        .logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET"))
                )
                .httpBasic(Customizer.withDefaults());  // 使用 HTTP Basic 认证
        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
        InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager();

        // 创建用户
        UserDetails user = User.builder()
                .username("coderjia")
                .password(passwordEncoder.encode("cj123456"))
                .roles("USER")
                .build();

        UserDetails admin = User.builder()
                .username("admin")
                .password(passwordEncoder.encode("admin123"))
                .roles("ADMIN")
                .build();
        userDetailsService.createUser(user);
        userDetailsService.createUser(admin);
        return userDetailsService;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();  // 使用 BCrypt 进行密码加密
    }
}

2.2 代码说明

  • authorizeHttpRequests():用于定义 URL 路径的访问权限。
  • requestMatchers("/admin/**").hasRole("ADMIN"):指定 /admin/** 下的所有路径都只有 ADMIN 角色的用户可以访问。
  • requestMatchers("/user/**").hasAnyRole("USER", "ADMIN"):允许 USERADMIN 角色访问 /user/** 下的资源。
  • anyRequest().authenticated():表示系统中的其他请求都需要用户登录后才可以访问。
  • formLogin()httpBasic():分别启用了表单登录和基本 HTTP 身份验证。

3. 角色与权限的区别

在 Spring Security 中,角色是权限的一种特殊形式。实际上,hasRole() 是基于 hasAuthority() 实现的。当我们定义角色时,Spring Security 会自动为角色加上前缀 ROLE_,所以 hasRole("ADMIN") 实际上是 hasAuthority("ROLE_ADMIN")

  • 角色:通常用于定义用户身份的层级。
  • 权限:则更细粒度地控制用户具体能做什么操作。

4. 基于注解的授权控制

除了在配置类中定义访问策略,Spring Security 还支持使用注解来控制方法的访问权限。常见的注解包括 @PreAuthorize@Secured

4.1 使用 @PreAuthorize 注解

@PreAuthorize 注解可以用于方法级别的权限控制。它可以在方法执行之前检查用户的权限。

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AdminController {

    @GetMapping("/admin/dashboard")
    @PreAuthorize("hasRole('ADMIN')")  // 只有 ADMIN 角色才能访问
    public String adminDashboard() {
        return "Welcome to Admin Dashboard!";
    }
}

使用用户 CoderJia 访问 /admin/dashboard 时提示无权限,使用 admin 访问则成功。

CoderJia访问

admin访问

4.2 使用 @Secured 注解

@Secured 注解也可以实现类似的功能,限制方法访问的权限。

import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @GetMapping("/user/profile")
    @Secured("ROLE_USER")  // 只有 USER 角色才能访问
    public String userProfile() {
        return "User Profile Information";
    }
}

同样的,admin 用户也无法访问只有 USER 角色才能访问的方法。

admin无法访问


5. 自定义权限决策

在某些场景中,我们可能需要更加灵活的权限控制。Spring Security 允许我们通过自定义 AccessDecisionManager 或使用 @PreAuthorize 表达式进行复杂的逻辑判断。

5.1 自定义权限表达式

例如,我们可以基于用户属性或者动态的业务数据来控制访问权限。

@PreAuthorize("hasRole('ADMIN') or #username == authentication.name")
public String getUserInfo(String username) {
    // 当前登录用户是 ADMIN,或者请求的 username 与登录用户一致时允许访问
    return "User Info: " + username;
}

登录用户和请求用户一致


6. 总结

在本篇中,我们介绍了 Spring Security 中的授权功能,包括如何使用基于角色和权限的访问控制,如何在方法级别进行授权,以及自定义复杂的权限决策逻辑。

授权是确保系统安全的重要组成部分,它能帮助我们在系统中根据用户的身份和角色对资源访问进行精细化控制。通过 Spring Security 提供的简单配置和注解支持,我们可以非常灵活地实现授权控制。

下一节中,我们将深入探讨更多 Spring Security 的高级功能。

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;