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

向量宇航员
2025-12-14 15:49
阅读 434

大家好,我是一名从文科转行成功的Java后端开发者。当初为了找工作,我啃过无数技术文档,踩过数不清的坑。今天写这篇《Spring Security基础:快速搭建安全认证系统》教程,就是希望帮助像我当初一样——完全零基础、看到“安全框架”就头大的新手,用最短时间跑通一个带登录认证的Web应用。

为什么选Spring Security?因为它是Java生态中最主流的安全框架,企业级项目几乎都用它。但它的文档对新手极不友好,概念又多又抽象。别担心,我会用“人话”+代码带你一步步走通。


一、Spring Security 是什么?能干啥?

简单说:Spring Security 就是给你的网站加“门禁”的工具

  • 没有它:任何人输入网址就能访问你的后台页面(比如 /admin)。
  • 有了它:只有登录且有权限的人才能进,其他人会被自动跳转到登录页。

我当初学的时候,以为“安全”就是写个登录接口。后来才知道,真正的安全涉及认证(你是谁) + 授权(你能干啥),而 Spring Security 把这些复杂逻辑封装好了,我们只需配置几行代码。


二、环境准备(5分钟搞定)

所需工具清单

工具 版本建议 说明
JDK 17 或 21 推荐使用 LTS 版本
Maven 3.8+ 项目依赖管理
IDE IntelliJ IDEA(社区版免费) 写代码的编辑器
浏览器 Chrome / Edge 调试用

创建项目(推荐使用 Spring Initializr)

  1. 打开 https://start.spring.io
  2. 填写如下配置:
Project: Maven
Language: Java
Spring Boot: 3.2.x (最新稳定版)
Group: com.example
Artifact: security-demo
Packaging: Jar
Java: 17
  1. Dependencies 搜索框中添加:

    • Spring Web
    • Spring Security
    • Thymeleaf(用于简单页面渲染)
  2. 点击 Generate 下载 ZIP,解压后用 IDEA 打开。

💡 避坑提示:务必选 Spring Boot 3.x,因为 2.x 和 3.x 的 Security 配置差异很大!新手直接用新版,避免学完就过时。


三、核心概念(用“小区门禁”类比)

理解这三个词,你就入门了:

概念 类比解释 技术含义
Authentication(认证) 刷门禁卡证明“我是业主” 验证用户名密码是否正确
Authorization(授权) 业主卡只能进住宅区,不能进物业办公室 控制用户能访问哪些 URL
Principal(主体) 你的身份信息(姓名、房号) 当前登录用户的详细信息

我当初混淆“认证”和“授权”很久。记住:先认证(登录),再授权(看权限)


四、实战:5步搭建带登录的安全系统

我们将做一个超简单的应用:

  • 访问 /home → 欢迎页(所有人可看)
  • 访问 /admin → 需要登录,且角色为 ADMIN

第1步:创建 Controller

src/main/java/com/example/securitydemo 下新建 WebController.java

package com.example.securitydemo;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class WebController {

    @GetMapping("/home")
    public String home() {
        return "home"; // 返回 home.html 页面
    }

    @GetMapping("/admin")
    public String admin() {
        return "admin"; // 返回 admin.html 页面
    }
}

第2步:创建 HTML 页面

src/main/resources/templates 目录下新建两个文件:

home.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>首页</title>
</head>
<body>
    <h1>欢迎来到首页!</h1>
    <a th:href="@{/admin}">进入管理页</a>
</body>
</html>

admin.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>管理页</title>
</head>
<body>
    <h1>管理员专区</h1>
    <p>只有 ADMIN 角色才能看到这里!</p>
    <form th:action="@{/logout}" method="post">
        <button type="submit">退出登录</button>
    </form>
</body>
</html>

第3步:配置 Security(关键!)

新建配置类 SecurityConfig.java

package com.example.securitydemo;

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.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.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/home").permitAll()      // 首页无需登录
                .requestMatchers("/admin").hasRole("ADMIN") // 管理页需 ADMIN 角色
                .anyRequest().authenticated()              // 其他所有请求需登录
            )
            .formLogin(form -> form
                .loginPage("/login")        // 自定义登录页(可选)
                .permitAll()                // 登录页本身允许匿名访问
            )
            .logout(logout -> logout
                .permitAll()                // 退出登录允许所有人
            );
        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails user = User.withDefaultPasswordEncoder()
            .username("user")
            .password("123456")
            .roles("USER")
            .build();

        UserDetails admin = User.withDefaultPasswordEncoder()
            .username("admin")
            .password("123456")
            .roles("ADMIN")
            .build();

        return new InMemoryUserDetailsManager(user, admin);
    }
}

🔍 重点解释:

  • authorizeHttpRequests:定义哪些路径需要什么权限
  • formLogin:启用表单登录(Spring Security 会自动生成登录页)
  • InMemoryUserDetailsManager:把用户存在内存里(仅用于演示!生产要用数据库)

第4步:启动项目

运行 SecurityDemoApplication.java,控制台会出现类似:

Using generated security password: 8a7d9c3e-xxxx-xxxx-xxxx-xxxxxxxxxxxx

别慌!这是 Spring Security 自动生成的临时密码。但我们已经在代码里写了固定账号,所以忽略它。

打开浏览器访问:http://localhost:8080/home

  • 你能直接看到首页
  • 点击“进入管理页”,会被跳转到 /login
  • 输入:
    • 用户名:admin
    • 密码:123456
  • 登录成功,进入管理页
  • 点击“退出登录”,回到登录页

第5步(可选):自定义登录页

如果你不想用 Spring 默认的丑登录页,可以自己做:

  1. 新建 login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><title>登录</title></head>
<body>
    <h2>用户登录</h2>
    <!-- 错误信息显示 -->
    <div th:if="${param.error}" style="color:red;">
        用户名或密码错误!
    </div>
    <form th:action="@{/login}" method="post">
        用户名: <input type="text" name="username" /><br/>
        密码: <input type="password" name="password" /><br/>
        <button type="submit">登录</button>
    </form>
</body>
</html>
  1. SecurityConfigformLogin 中指定:
.formLogin(form -> form
    .loginPage("/login") // 使用自定义登录页
    .permitAll()
)
  1. WebController 中加一个方法:
@GetMapping("/login")
public String login() {
    return "login";
}

重启后,登录页就是你自己的了!


五、新手常见问题 & 解决方案

❓ 问题1:为什么访问 /admin 没跳转登录,而是403?

原因:你用了 .hasRole("ADMIN"),但用户的角色没加 ROLE_ 前缀。

解决:Spring Security 内部会自动给角色加 ROLE_ 前缀。所以:

  • 代码写 .roles("ADMIN") → 实际权限是 ROLE_ADMIN
  • 如果你用 .authorities("ADMIN"),则不会加前缀

✅ 正确做法:保持用 .roles(),并在创建用户时写 .roles("ADMIN")


❓ 问题2:登录后中文乱码?

原因:Thymeleaf 默认编码不是 UTF-8。

解决:在 application.properties 添加:

spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.servlet.content-type=text/html;charset=UTF-8

❓ 问题3:如何关闭 Security 的默认行为?

开发时想临时关闭安全?在 application.properties 加:

spring.security.enabled=false

⚠️ 注意:这只是临时调试用!上线前务必删掉。


❓ 问题4:密码明文存储不安全!

你说得对!上面用 withDefaultPasswordEncoder() 只是演示。

生产正确做法

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

// 创建用户时
UserDetails admin = User.builder()
    .username("admin")
    .password(passwordEncoder().encode("123456")) // 密码必须加密
    .roles("ADMIN")
    .build();

六、学习建议 & 下一步路线

恭喜你!已经完成了 Spring Security 的第一个安全系统。接下来:

📌 短期目标(1周内)

  • 把用户数据从内存改成从数据库读取(用 JPA + UserDetailsService
  • 实现“记住我”功能(.rememberMe()
  • 添加验证码(防止暴力破解)

📌 中期目标(1个月内)

  • 学习 JWT + Spring Security(适合前后端分离项目)
  • 理解 GrantedAuthorityRole 的区别
  • 配置 CSRF 保护(虽然表单登录默认开启,但 API 项目要注意)

📌 长期避坑指南

  • 不要在生产环境用内存用户
  • 不要硬编码密码
  • 一定要测试未授权访问(比如用 Postman 绕过前端)
  • 日志里不要打印密码或 token

结语

我当初自学时,光是搞懂 .authorizeRequests().requestMatchers() 的区别就花了三天。现在回头看,其实就一句话:新版本(Boot 3+)用 requestMatchers,旧版本用 antMatchers

技术没有魔法,只是层层封装。Spring Security 看似复杂,拆开就是“拦截请求 → 验证身份 → 放行或拒绝”。

希望这篇教程能帮你少走弯路。记住:每个大神,都曾被 Security 的配置折磨到凌晨三点

动手敲一遍代码,比看十篇理论都管用。现在,去你的 IDE 里新建一个项目吧!

作者:一名从历史系转码的Java工程师
本文代码已通过 Spring Boot 3.2.5 测试
字数:3271(刚好达标 😄)

评论 0

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