Spring Security | 集认证、授权、防攻击于一体的安全框架

一、Spring Security简介

Spring Security是一个Java框架,用于保护应用程序的安全性。它提供了一套全面的安全解决方案,包括身份验证、授权、防止攻击等功能。Spring Security基于过滤器链的概念,可以轻松地集成到任何基于Spring的应用程序中。它支持多种身份验证选项和授权策略,开发人员可以根据需要选择适合的方式。此外,Spring Security还提供了一些附加功能,如集成第三方身份验证提供商和单点登录,以及会话管理和密码编码等。总之,Spring Security是一个强大且易于使用的框架,可以帮助开发人员提高应用程序的安全性和可靠性。

1.核心定位

Spring 生态的安全框架,基于AOP 和 Servlet 过滤器链 实现,提供 Web 请求和方法调用级的认证(是否能访问系统)授权(是否有权限操作),自包含部署无需额外配置文件。

2.技术选型

SSM 框架通常搭配 Shiro,Spring Boot/Spring Cloud 首选 Spring Security

3.基础使用

XML 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

引入spring-boot-starter-security依赖后,框架自动开启接口保护,默认生成用户名user,密码控制台随机生成,跳转默认登录页。

二、核心认证流程

Spring Security认证时序图:

1.自定义UserDetailService

java 复制代码
@Service
public class CustomUserDetailsService implements UserDetailsService {

    // 注入MyBatis的Mapper(替代之前的JPA Repository)
    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 1. 通过Mapper从数据库查询用户(含角色)
        SysUser sysUser = userMapper.selectUserByUsername(username);
        
        // 2. 用户不存在则抛出异常(Spring Security会捕获并处理)
        if (sysUser == null) {
            throw new UsernameNotFoundException("用户名不存在:" + username);
        }

        // 3. 转换为Spring Security的UserDetails对象
        // 处理角色:拼接ROLE_前缀(Spring Security的权限命名规范)
        List<GrantedAuthority> authorities = sysUser.getRoles().stream()
                .map(role -> new SimpleGrantedAuthority("ROLE_" + role.getRoleName()))
                .collect(Collectors.toList());

        // 构建UserDetails(核心:用户名、加密密码、权限列表)
        return User.withUsername(sysUser.getUsername())
                .password(sysUser.getPassword()) // 数据库中已加密的密码
                .authorities(authorities) // 用户权限
                .accountExpired(false) // 账号未过期
                .accountLocked(false) // 账号未锁定
                .credentialsExpired(false) // 凭证未过期
                .enabled(true) // 账号启用(已通过SQL的status=1过滤)
                .build();
    }
}
  • 对应图中DaoAuthenticationProvider调用loadUserByUsername的环节。

  • 你需要实现这个接口,告诉 Spring Security如何从你的数据源(数据库)加载用户信息,包括用户名、加密密码和权限列表。

2.重写配置类SecurityConfig

java 复制代码
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final UserDetailsService userDetailsService;
    private final PasswordEncoder passwordEncoder;
    
    // 构造注入
    public SecurityConfig(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) { 
        this.userDetailsService = userDetailsService;
        this.passwordEncoder = passwordEncoder;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(); // 配置密码编辑器
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/login", "/static/**", "/public/**").permitAll() // 公开接口
                .anyRequest().authenticated() // 其他请求需要认证
            )
            .formLogin(form -> form
                .loginPage("/login") // 自定义登录页面(可选)
                .loginProcessingUrl("/doLogin") // 处理登录请求的URL,对应图中的POST /login
                .usernameParameter("username") // 登录表单中用户名的参数名
                .passwordParameter("password") // 登录表单中密码的参数名
                .defaultSuccessUrl("/index", true) // 登录成功后的跳转页面
                .failureUrl("/login?error") // 登录失败后的跳转页面
            )
            .logout(logout -> logout
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login?logout")
            )
            .csrf(csrf -> csrf.disable()); // 前后端分离时通常关闭CSRF保护

        return http.build();
    }

    // 配置AuthenticationManager使用自定义的UserDetailsService和PasswordEncoder
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }
}
  • 这是整个认证流程的总控台 ,对应图中的DaoAuthenticationProvider调用passwordEncoder.matches()、``UsernamePasswordAuthenticationFilterAuthenticationManager
  1. Spring Security 要求密码必须加密存储,BCryptPasswordEncoder是推荐的实现,它会自动处理加盐和验证,防止彩虹表攻击。

  2. 定义哪些 URL 需要保护,哪些可以公开访问。

  3. 配置登录表单的参数和行为,让UsernamePasswordAuthenticationFilter知道从哪里获取用户名和密码。

  4. 配置登出逻辑。

  5. 构建AuthenticationManager,它会自动使用你提供的UserDetailsServicePasswordEncoder

三、核心授权流程

Spring Security授权时序图:

1.配置SecurityFilterChain(包含授权规则)

java 复制代码
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            // ========== 授权规则核心配置 ==========
            .authorizeHttpRequests(auth -> auth
                // 1. 公开接口:所有人可访问(无需授权)
                .requestMatchers("/", "/login", "/public/**").permitAll()
                
                // 2. 管理员接口:仅拥有ROLE_ADMIN权限的用户可访问
                .requestMatchers("/admin/**").hasRole("ADMIN")
                
                // 3. 普通用户接口:拥有ROLE_USER或ROLE_ADMIN均可访问
                .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
                
                // 4. 精确URL权限:需要特定权限(非角色,自定义权限)
                .requestMatchers("/order/create").hasAuthority("ORDER_CREATE")
                
                // 5. 剩余所有请求:必须认证(登录后)才能访问
                .anyRequest().authenticated()
            )
            // 认证相关配置(复用之前的登录/登出)
            .formLogin(form -> form
                .loginPage("/login")
                .loginProcessingUrl("/doLogin")
                .permitAll()
            )
            // 403异常处理(授权失败)
            .exceptionHandling(ex -> ex
                .accessDeniedPage("/403") // 授权失败跳转页面(前后端分离可换JSON处理器)
                // 前后端分离场景:自定义403返回JSON
                // .accessDeniedHandler(customAccessDeniedHandler())
            );

        return http.build();
    }

在原有的认证逻辑基础上添加授权规则逻辑,这是授权最核心的配置,对应时序图中SecurityMetadataSource获取权限规则的环节。

2.方法级授权

在配置类SecurityConfig上加上@EnableMethodSecurity(prePostEnabled = true)注解,即开启PreAuthorize/PostAuthorize注解了。

具体应用如下:

java 复制代码
@Service
public class AdminService {

    // 仅允许ADMIN角色调用该方法
    @PreAuthorize("hasRole('ADMIN')")
    public void deleteUser(Long userId) {
        // 业务逻辑:删除用户
    }

    // 允许USER或ADMIN角色,且用户ID匹配(自定义EL表达式)
    @PreAuthorize("hasAnyRole('USER','ADMIN') and authentication.name == #username")
    public UserInfo getUserInfo(String username) {
        // 业务逻辑:查询用户信息
    }

    // 方法执行后验证权限(很少用)
    @PostAuthorize("returnObject.username == authentication.name")
    public UserInfo getCurrentUserInfo() {
        // 业务逻辑:返回当前用户信息
    }
}
  • URL 级授权是 "粗粒度" 的(控制整个接口),方法级授权是 "细粒度" 的(控制单个业务方法);
  • @PreAuthorize在方法执行前验证权限,是最常用的方法级授权注解;
  • 支持 Spring EL 表达式,可直接引用方法参数、当前用户信息,实现灵活的权限判断。

注意:

  • 角色(Role) :是一种特殊的权限,Spring Security 默认会给角色加ROLE_前缀(如hasRole("ADMIN")实际匹配ROLE_ADMIN);

  • 权限(Authority) :更细粒度的权限(如ORDER_CREATEUSER_EDIT),无默认前缀,用hasAuthority()判断;

  • 建议:角色用于大分类(ADMIN/USER),权限用于具体操作(ORDER_CREATE/USER_EDIT),组合使用更灵活。

相关推荐
向哆哆2 小时前
厨房食品卫生安全检测数据集:智能餐饮与食品安全保障的视觉卫士
人工智能·安全·目标跟踪
苏天夏3 小时前
让 Typecho 拥抱 WebAuthn 无密码时代
安全·网络安全·php·开源软件
Rhystt5 小时前
furryCTF 题解|Web方向|保姆级详解|固若金汤、DeepSleep
git·python·安全·web安全·网络安全
m0_738120725 小时前
渗透测试——Momentum靶机渗透提取详细教程(XSS漏洞解密Cookie,SS获取信息,Redis服务利用)
前端·redis·安全·web安全·ssh·php·xss
一名优秀的码农5 小时前
symfonos系列-symfonos6v2(超详细)
安全·web安全·网络安全·网络攻击模型·安全威胁分析
米羊1215 小时前
shiro攻防利用
java·struts·安全
枷锁—sha15 小时前
【pwn系列】Pwndbg 汇编调试实操教程
网络·汇编·笔记·安全·网络安全
Bruce_Liuxiaowei15 小时前
Windows系统安全加固——从基础到进阶的实战配置
windows·安全·系统安全
A1b2rt16 小时前
CISCN-2025-FINAL-DarkHeap
安全·pwn·wp·ciscn