Spring Security 框架最佳实践

 更新时间:2025年11月21日 10:34:44   作者:海马  
Spring Security 是一个功能强大、高度可定制的身份验证(Authentication)和访问控制(Authorization)框架,是保护基于 Spring 的应用的事实标准,本文给大家介绍Spring Security框架最佳实践,感兴趣的朋友跟随小编一起看看吧

一、什么是 Spring Security?

Spring Security 是一个功能强大、高度可定制的身份验证(Authentication)和访问控制(Authorization)框架,是保护基于 Spring 的应用的事实标准。

✅ 核心功能:

  • 用户登录认证(Authentication)
  • 权限控制、角色控制(Authorization)
  • 防止 CSRF、会话固定、点击劫持等攻击
  • 支持多种登录方式:表单登录、HTTP Basic、OAuth2、JWT、LDAP 等
  • 与 Spring Boot 无缝集成

二、Spring Security 核心概念

1.Authentication(认证)

  • 代表“当前用户是谁”,包含用户名、密码、权限等。
  • 由 AuthenticationManager 管理认证过程。

2.UserDetails&UserDetailsService

  • UserDetails:封装用户信息(用户名、密码、权限、是否锁定等)
  • UserDetailsService:根据用户名加载 UserDetails
@Override
public UserDetails loadUserByUsername(String username) {
    // 从数据库查用户 → 返回 UserDetails(含加密密码 + 权限)
}

3.PasswordEncoder

  • 用于加密密码和比对密码。
  • 永远不要存储明文密码!
  • 常用实现:BCryptPasswordEncoderSCryptPasswordEncoderPbkdf2PasswordEncoder
// 注册时:
user.setPassword(passwordEncoder.encode(rawPassword));
// 登录时(框架自动调用):
passwordEncoder.matches(rawPassword, encodedPassword); // true/false

4.GrantedAuthority

  • 代表一个权限,如 "ROLE_ADMIN""user:delete"
  • 通常以 ROLE_ 开头表示角色,其它表示具体权限。

5.SecurityContext&SecurityContextHolder

  • 存储当前登录用户的信息(Authentication 对象)。
  • 可在任何地方获取当前用户:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth.getName();

三、Spring Security 工作流程(认证过程)

sequenceDiagram
    participant User
    participant Filter as FilterChain (Spring Security)
    participant Provider as AuthenticationProvider
    participant Service as UserDetailsService
    participant Encoder as PasswordEncoder
    User->>Filter: 提交用户名/密码
    Filter->>Provider: 调用 authenticate()
    Provider->>Service: loadUserByUsername(username)
    Service-->>Provider: 返回 UserDetails(含加密密码)
    Provider->>Encoder: matches(输入密码, 数据库加密密码)
    alt 匹配成功
        Encoder-->>Provider: true
        Provider-->>Filter: 返回认证成功 Authentication
        Filter->>User: 登录成功,跳转
    else 匹配失败
        Encoder-->>Provider: false
        Provider-->>Filter: 抛出异常
        Filter->>User: 登录失败
    end

四、常用配置方式(Spring Boot)

1. 引入依赖(Maven)

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

2. 基础配置类(启用 Web 安全)

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authz -> authz
                .requestMatchers("/login", "/register", "/css/**").permitAll()
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login")           // 自定义登录页
                .defaultSuccessUrl("/home")    // 登录成功跳转
                .permitAll()
            )
            .logout(logout -> logout
                .logoutSuccessUrl("/login?logout")
                .permitAll()
            );
        return http.build();
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(); // 推荐使用 BCrypt
    }
}

3. 自定义 UserDetailsService

@Component
public class MyUserServiceImpl implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userMapper.findUserByName(username);
        if (user == null) throw new UsernameNotFoundException("用户不存在");
        // ✅ 直接返回数据库中的加密密码(注册时已加密存储!)
        return User.builder()
            .username(user.getUsername())
            .password(user.getSupwd()) // ← 不要再 encode!
            .roles("USER") // 或者 .authorities(getAuthorities())
            .build();
    }
}

五、权限控制(授权 Authorization)

1. 方法级别权限(推荐)

@Service
public class ArticleService {
    @PreAuthorize("hasRole('ADMIN')") // 需要 ADMIN 角色
    public void deleteArticle(Long id) {
        // ...
    }
    @PreAuthorize("hasAuthority('article:edit')") // 需要具体权限
    public void editArticle(Article article) {
        // ...
    }
    @PostAuthorize("returnObject.owner == authentication.name")
    public Article getArticle(Long id) {
        // 返回后检查:只有文章主人才能看
    }
}

⚠️ 要启用方法级权限,需在配置类加:@EnableMethodSecurity

@Configuration
@EnableWebSecurity
@EnableMethodSecurity // ← 启用 @PreAuthorize 等注解
public class SecurityConfig { ... }

2. 页面级别权限(Thymeleaf)

<!-- 只有 ADMIN 能看到 -->
<div sec:authorize="hasRole('ADMIN')">
    <a href="/admin" rel="external nofollow" >管理后台</a>
</div>
<!-- 显示当前用户名 -->
<span sec:authentication="name"></span>

需引入:

<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity6</artifactId>
</dependency>

六、常见自定义扩展

1. 自定义登录成功/失败处理器

@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, 
                                        HttpServletResponse response, 
                                        Authentication authentication) throws IOException {
        response.sendRedirect("/home?loginSuccess");
    }
}

在配置中使用:

.formLogin(form -> form
    .successHandler(mySuccessHandler)
    .failureHandler(myFailureHandler)
)

2. 自定义权限异常处理

@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
        response.sendRedirect("/403");
    }
}

配置:

.exceptionHandling(ex -> ex
    .accessDeniedHandler(myAccessDeniedHandler)
)

七、与 JWT / 前后端分离集成(简要)

如果你是前后端分离项目(如 Vue + Spring Boot),通常不用 Session,改用 JWT:

  1. 用户 POST /login 提交用户名密码
  2. 后端验证成功 → 生成 JWT Token 返回
  3. 前端后续请求在 Header 中携带 Authorization: Bearer <token>
  4. 后端用 Filter 解析 Token → 设置 SecurityContext

⚠️ 此时 UserDetailsService 依然有用 —— 用于从 Token 中的用户名加载用户权限!

八、最佳实践 & 注意事项

项目建议
密码存储必须加密(BCrypt 最常用)
密码比对交给 Spring Security,不要手动比对
权限设计角色(ROLE_) + 细粒度权限(user:delete)结合
登录页可自定义,但路径要 permitAll
CSRF表单登录默认开启,JWT 项目可关闭
调试可临时 .authorizeHttpRequests(authz -> authz.anyRequest().permitAll()) 放行所有

✅ 总结一句话:

Spring Security = 认证(你是谁)+ 授权(你能干什么)+ 安全防护,你只需要提供“用户数据”和“权限规则”,框架自动完成验证和拦截。

📚 学习路径建议:

  1. 先跑通表单登录 + 自定义 UserDetailsService
  2. 学会配置 URL 权限控制
  3. 掌握方法级权限 @PreAuthorize
  4. 学习自定义处理器(登录成功/失败、无权限)
  5. 进阶:JWT、OAuth2、方法权限表达式、动态权限

💡 送你一个完整可运行的最小示例结构:

src/
├── controller/
│   └── LoginController.java       // 登录页、首页
├── service/
│   └── MyUserServiceImpl.java     // loadUserByUsername
├── config/
│   └── SecurityConfig.java        // 权限配置 + PasswordEncoder
└── entity/
    └── User.java                  // 用户实体(含加密密码字段)

到此这篇关于Spring Security 框架的文章就介绍到这了,更多相关spring security 框架内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 用Java打印九九除法表代码分析

    用Java打印九九除法表代码分析

    这篇文章主要介绍了如何用Java语言打印九九除法表,包括其使用的源代码,需要的朋友可以参考下。
    2017-08-08
  • java身份证验证代码实现

    java身份证验证代码实现

    java身份证验证代码实现,需要的朋友可以参考一下
    2013-02-02
  • 使用mtrace追踪JVM堆外内存泄露的方法

    使用mtrace追踪JVM堆外内存泄露的方法

    这篇文章主要给大家介绍了如何使用mtrace追踪JVM堆外内存泄露,文章通过代码示例介绍的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2023-09-09
  • idea项目结构中不显示out文件夹的解决

    idea项目结构中不显示out文件夹的解决

    本文通过图片的方式详细解释操作步骤,使读者能够更直观更方便地理解和执行操作,同时,文章末尾祝福读者步步高升,一帆风顺,展现了作者的人情味和亲和力,整体来说,这是一篇简单易懂、实用性强的操作指南
    2024-10-10
  • Java 中的注解详解及示例代码

    Java 中的注解详解及示例代码

    本文主要介绍Java 中的注解,这里提供了详细的相关资料,及示例代码,帮助大家学习理解,有兴趣的小伙伴可以参考下
    2016-08-08
  • Java 流的高级使用之收集数据解析

    Java 流的高级使用之收集数据解析

    这篇文章主要介绍了Java 流的高级使用之收集数据解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • Java模拟实现机场过安检处理流程

    Java模拟实现机场过安检处理流程

    这篇文章主要为大家详细介绍了用Java模拟实现机场安全检查流程的案例,涉及线程的相关知识,由子线程不断的检查通过旅客,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • Java连接MYSQL数据库的实现步骤

    Java连接MYSQL数据库的实现步骤

    以下的文章主要描述的是java连接MYSQL数据库的正确操作步骤,在此篇文章里我们主要是以实例列举的方式来引出其具体介绍
    2013-06-06
  • 微服务和分布式的区别详解

    微服务和分布式的区别详解

    在本篇文章里小编给各位整理了关于微服务和分布式的区别以及相关知识点总结,有兴趣的朋友们学习下。
    2019-07-07
  • Java多线程下的单例模式参考

    Java多线程下的单例模式参考

    这篇文章主要演示多线程下的单例模式,分别演示了lock和synchronized两种方案,希望能给大家做一个参考。
    2016-06-06

最新评论