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),组合使用更灵活。

相关推荐
黄焖鸡能干四碗4 小时前
业务数据中台技术方案(PPT)
大数据·数据库·人工智能·安全·需求分析
德迅云安全-小潘7 小时前
恶意爬虫对数字资产的系统性威胁
网络·人工智能·安全·web安全
数据知道8 小时前
MongoDB审计日志配置:详细讲述满足合规性要求的安全记录
数据库·安全·mongodb
流水迢迢lst8 小时前
靶场练习day14--任意文件读取
网络·安全
触想工业平板电脑一体机9 小时前
触想工控机化身电力智能网关,助力电力系统安全高效运行
安全·系统安全
HAPPY酷9 小时前
Linux `shutdown` 命令速查:安全关机与重启
linux·chrome·安全
globaldomain9 小时前
安全研究发现OpenClaw AI代理“极易受劫持”
人工智能·安全·openclaw·龙虾
数据知道10 小时前
MongoDB安全加固最佳实践:CIS基准符合性检查与实施步骤
数据库·安全·mongodb
cyforkk10 小时前
数据库里的隐形守卫:通俗易懂理解 RLS(行级安全)
数据库·安全
李子红了时10 小时前
Win10一键禁用自动更新和Windows Defender安全中心
windows·安全