Spring Security基础:快速搭建安全认证系统(零基础入门实战)

赵超
2025-12-16 06:24
阅读 230

大家好,我是B站的技术UP主小码哥。在大厂做后端开发三年,平时除了写业务代码,也喜欢在B站分享一些实用的开发技巧。最近很多粉丝私信问我:“Spring Security到底怎么入门?网上教程要么太深奥,要么直接甩一堆配置,根本看不懂。”

我当初学的时候也是这样——看到AuthenticationManagerUserDetailsService这些名词就头大,连最基本的“登录”都搞不定。所以今天这篇教程,我就用最简单的语言+完整可运行的代码,手把手带你从零搭建一个带用户名密码登录的安全系统。不讲理论堆砌,只讲你能立刻上手的实战内容

更重要的是,我会穿插面试题挑战环节,帮你提前应对求职中的高频考点。技术分享不是炫技,而是让你真正掌握、能用、能说清楚。


一、Spring Security 是什么?为什么需要它?

想象一下:你开发了一个后台管理系统,里面有用户数据、订单信息。如果随便谁都能访问 /admin/deleteAllUsers 这个接口,那公司第二天就得关门了。

Spring Security 就是给你的应用装上“门禁系统”

  • 谁可以进来?(认证 Authentication)
  • 进来后能去哪些房间?(授权 Authorization)

没有它,你的系统就像一栋没有保安的大楼——危险且不专业。

💡 面试题挑战 #1
Q:Spring Security 和 Shiro 有什么区别?
A:Shiro 更轻量、学习曲线平缓;Spring Security 功能更强大、与 Spring 生态无缝集成(比如 OAuth2、JWT),适合企业级复杂场景。大厂基本都用 Spring Security。


二、环境准备:5分钟搭好开发环境

我们用最主流的组合:Spring Boot + Spring Security + Maven

1. 创建项目

打开 start.spring.io,选择:

选项
Project Maven
Language Java
Spring Boot 3.2.x (最新稳定版)
Dependencies Spring Web, Spring Security

点击 “Generate”,下载 ZIP 并解压到本地。

2. 导入 IDE

用 IntelliJ IDEA 打开项目(Eclipse 也可以,但 IDEA 对 Spring 支持更好)。

3. 验证基础结构

确保 pom.xml 中包含以下依赖:

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

避坑指南:不要手动添加 Spring Security 的版本号!Spring Boot 的 parent POM 已经管理好了兼容版本。


三、核心概念:用生活例子理解专业术语

别被术语吓到,我用“小区门禁”来类比:

技术概念 小区门禁类比 作用
Authentication 你的身份证+人脸识别 证明“你是谁”
Authorization 你的房卡权限(只能进1号楼) 决定“你能做什么”
UserDetails 物业登记的业主信息(姓名、房号、权限) 存储用户数据
PasswordEncoder 身份证加密存储(不能明文存密码!) 安全地保存密码

关键点:

  • 认证(Authentication):登录过程,验证用户名密码是否正确。
  • 授权(Authorization):登录后,控制你能访问哪些 URL 或方法。

🌟 我当初学的时候:总混淆这两个词。记住口诀——“先认证(你是谁),再授权(你能干啥)”。


四、实战项目:从零实现用户名密码登录

我们将完成一个最简但完整的系统:

  • 访问 /hello → 需要登录
  • 登录页 → 输入用户名密码
  • 登录成功 → 显示欢迎信息

步骤 1:启动默认安全(验证环境)

直接运行 Application.java,然后浏览器访问 http://localhost:8080/hello

你会发现:

  • 自动跳转到 /login 页面
  • 控制台打印了一行密码(形如 Using generated security password: a1b2c3d4...

✅ 这说明 Spring Security 已生效!但它用的是内存临时用户,我们得改成自己的逻辑。

步骤 2:自定义用户(内存版)

创建配置类 SecurityConfig.java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

    @Bean
    public InMemoryUserDetailsManager userDetailsService() {
        UserDetails user = User.builder()
            .username("admin")
            .password("{noop}123456") // {noop} 表示不加密(仅演示用!)
            .roles("USER")
            .build();
        return new InMemoryUserDetailsManager(user);
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/hello").authenticated() // /hello 需要登录
                .anyRequest().permitAll()                   // 其他路径开放
            )
            .formLogin(form -> form
                .loginPage("/login")        // 自定义登录页(可选)
                .permitAll()
            );
        return http.build();
    }
}

同时创建一个简单 Controller:

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "Hello, authenticated user!";
    }
}

🔐 重要安全提示{noop} 表示密码明文存储!生产环境绝对禁止! 下一节我们会改用加密。

现在重启应用,访问 /hello → 跳转登录页 → 输入 admin / 123456 → 成功!

步骤 3:密码加密(必须做!)

修改 SecurityConfig.java,加入 PasswordEncoder

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder(); // Spring Security 推荐的强哈希算法
}

@Bean
public InMemoryUserDetailsManager userDetailsService() {
    UserDetails user = User.builder()
        .username("admin")
        .password(passwordEncoder().encode("123456")) // 加密密码
        .roles("USER")
        .build();
    return new InMemoryUserDetailsManager(user);
}

💡 为什么用 BCrypt?

  • 不可逆:无法从密文反推明文
  • 加盐:相同密码每次加密结果不同,防彩虹表攻击
  • 慢哈希:故意计算慢,防暴力破解

避坑指南:如果你看到 There is no PasswordEncoder mapped for the id "null" 错误,就是因为没配 PasswordEncoder

步骤 4:自定义登录页面(提升体验)

创建 src/main/resources/templates/login.html(需先加 Thymeleaf 依赖):

<!-- pom.xml 新增 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

login.html 内容:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><title>Login</title></head>
<body>
<h2>用户登录</h2>
<form th:action="@{/login}" method="post">
    用户名: <input type="text" name="username" /><br/>
    密码: <input type="password" name="password" /><br/>
    <input type="submit" value="登录" />
</form>
</body>
</html>

修改 SecurityConfig 中的 formLogin 配置:

.formLogin(form -> form
    .loginPage("/login")      // 使用自定义登录页
    .defaultSuccessUrl("/hello") // 登录成功跳转
    .permitAll()
)

再加一个返回登录页的 Controller:

@Controller
public class LoginController {
    @GetMapping("/login")
    public String login() {
        return "login"; // 返回 templates/login.html
    }
}

现在登录页就是你自己的页面了!


五、常见问题解答(新手必看)

❓ 问题1:为什么登录后总是跳回首页,而不是我指定的页面?

原因:Spring Security 默认行为是“重定向到之前被拦截的页面”。
解决方案:在 formLogin() 中明确指定:

.defaultSuccessUrl("/dashboard", true) // true = 无论从哪来,都去 /dashboard

❓ 问题2:如何放行静态资源(如 CSS/JS)?

authorizeHttpRequests 中添加:

.requestMatchers("/css/**", "/js/**", "/images/**").permitAll()

❓ 问题3:控制台没打印密码,登录不了怎么办?

原因:你已经配置了自定义用户,Spring Security 不会再生成临时密码。
解决方案:确保你的用户账号密码正确,并且密码已加密。

❓ 问题4:如何注销(退出登录)?

Spring Security 默认开启 /logout,访问即可退出。
自定义配置:

.logout(logout -> logout
    .logoutUrl("/custom-logout")
    .logoutSuccessUrl("/login?logout")
)

六、综合:一个完整的小案例

让我们整合所有知识点,做一个极简后台:

路径 权限要求 说明
/ 无需登录 首页
/admin 需要 ROLE_ADMIN 管理员页
/user 需要 ROLE_USER 普通用户页
// SecurityConfig.java 片段
.authorizeHttpRequests(auth -> auth
    .requestMatchers("/").permitAll()
    .requestMatchers("/admin").hasRole("ADMIN")
    .requestMatchers("/user").hasAnyRole("USER", "ADMIN")
    .anyRequest().authenticated()
)

创建两个用户:

UserDetails admin = User.builder()
    .username("admin")
    .password(passwordEncoder().encode("admin123"))
    .roles("ADMIN")
    .build();

UserDetails user = User.builder()
    .username("user")
    .password(passwordEncoder().encode("user123"))
    .roles("USER")
    .build();

return new InMemoryUserDetailsManager(admin, user);

💡 面试题挑战 #2
Q:hasRole("ADMIN")hasAuthority("ROLE_ADMIN") 有什么区别?
A:hasRole 会自动加上 ROLE_ 前缀,所以 .roles("ADMIN") 实际权限是 ROLE_ADMIN。两者等价,但建议统一用 hasRole


七、学习建议与下一步

恭喜你!你已经掌握了 Spring Security 的核心流程。但这只是冰山一角。

下一步推荐学习路径:

  1. 数据库集成:用 JdbcUserDetailsManager 或自定义 UserDetailsService 从 DB 读用户
  2. JWT 无状态认证:适用于前后端分离、APP 接口
  3. OAuth2 / 社交登录:微信、GitHub 第三方登录
  4. 方法级安全:用 @PreAuthorize 控制 Service 方法权限

我的学习建议:

  • 不要死记配置:理解 Filter Chain 的工作流程(画流程图!)
  • 善用调试:在 UserDetailsService.loadUserByUsername 打断点,看请求如何流转
  • 安全第一:永远不要明文存密码,永远不要信任前端传来的权限信息

🎯 最后的话
我在 B站【小码哥编程】更新了配套视频,包含本教程的完整代码演示和调试过程。搜索“Spring Security 零基础”就能找到。
技术分享的意义,就是让后来者少走弯路。如果你觉得有帮助,不妨点个赞,这对我很重要!

记住:每一个大神,都曾是连登录都配不明白的新手。你现在的困惑,正是成长的开始。

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝