跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

Spring Boot 安全认证与授权实战

Spring Boot 安全认证与授权涉及 Spring Security 框架的集成与应用。通过配置依赖、编写安全策略类及用户服务,可实现基于内存或数据库的身份验证,并结合角色控制访问权限。演示了从基础集成到数据库认证的全流程,包含登录注册、权限拦截及测试验证,帮助开发者构建安全的 Web 应用。重点在于理解 AuthenticationManagerBuilder 的配置方式以及 UserDetailsService 的实现逻辑,确保不同角色对资源的访问受到严格控制。

追风少年发布于 2026/3/22更新于 2026/10/576 浏览
Spring Boot 安全认证与授权实战

Spring Boot 安全认证与授权实战

架构图

在 Web 开发中,安全是绕不开的话题。Spring Security 作为 Spring 生态的事实标准,提供了强大的认证与授权能力。本文将带你从零开始,掌握如何在 Spring Boot 中集成 Spring Security,实现基于内存和数据库的认证,以及基于角色的权限控制。

一、快速集成

要在项目中使用 Spring Security,首先需要在 pom.xml 中添加依赖。除了基础的 Web 支持,我们还需要引入 spring-boot-starter-security。

<dependencies>
    <!-- Web 依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Spring Security 依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <!-- 测试依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

添加依赖后,Spring Boot 会自动启用安全配置。默认情况下,所有请求都需要登录,且密码会随机生成并打印到控制台。为了自定义行为,我们需要创建一个配置类。

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 配置内存用户:admin/admin123 (ADMIN), user/user123 (USER)
        auth.inMemoryAuthentication()
            .passwordEncoder(NoOpPasswordEncoder.getInstance())
            .withUser("admin")
            .password("admin123")
            .roles("ADMIN")
            .and()
            .withUser("user")
            .password("user123")
            .roles("USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/admin/**").hasRole("ADMIN")
            .antMatchers("/user/**").hasRole("USER")
            .antMatchers("/").permitAll()
            .and()
            .formLogin()
            .loginPage("/login")
            .permitAll()
            .and()
            .logout()
            .permitAll();
    }
}

这里我们定义了两个角色:ADMIN 和 USER。/admin/** 路径仅允许管理员访问,而首页对所有用户开放。formLogin() 启用了默认的表单登录功能。

二、认证方式详解

1. 基于内存的认证

上面的示例就是典型的基于内存的认证。它适合开发环境或原型验证,因为用户数据硬编码在代码里。优点是简单直接,缺点是重启服务后数据丢失,不适合生产环境。

2. 基于数据库的认证

实际项目中,用户信息通常存储在数据库中。这需要引入 JPA 和数据库驱动(如 H2 用于演示)。

依赖补充:

<!-- Data JPA 依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- H2 数据库依赖 -->
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

实体类设计:

import javax.persistence.*;

@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String password;
    private String role;

    // Getter 和 Setter 方法省略...
}

Repository 接口:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    User findByUsername(String username);
}

核心服务层: Spring Security 通过 UserDetailsService 加载用户详情。我们需要实现这个接口来从数据库查询用户。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("用户不存在:" + username);
        }
        List<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority("ROLE_" + user.getRole()));
        return org.springframework.security.core.userdetails.User.builder()
                .username(user.getUsername())
                .password(user.getPassword())
                .authorities(authorities)
                .build();
    }
}

配置更新: 在 SecurityConfig 中注入这个服务,替换掉内存配置。

@Autowired
private UserDetailsServiceImpl userDetailsService;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService);
}

此时,系统会从数据库读取用户名和密码进行校验。记得在 application.properties 中配置好数据库连接信息。

三、授权策略

认证通过后,接下来要解决'能做什么'的问题。Spring Security 主要提供两种授权方式:基于角色和基于权限。

1. 基于角色的授权

这是最常用的方式,通过 hasRole("ROLE_NAME") 来控制 URL 访问。注意,Spring Security 会自动在角色前加 ROLE_ 前缀。

http.authorizeRequests()
    .antMatchers("/admin/**").hasRole("ADMIN")
    .antMatchers("/user/**").hasRole("USER");

2. 基于权限的授权

如果需要更细粒度的控制(例如某个按钮可见),可以使用 hasAuthority("PERMISSION_NAME")。这通常需要结合方法级注解或更复杂的表达式来实现。

四、前端视图与测试

为了让体验更完整,我们可以准备简单的 HTML 模板。使用 Thymeleaf 引擎可以方便地嵌入 Spring Security 标签。

登录页 (login.html)

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>登录</title></head>
<body>
<div class="container">
    <h1>登录</h1>
    <form th:action="@{/login}" method="post">
        <label for="username">用户名:</label>
        <input type="text" id="username" name="username" required>
        <label for="password">密码:</label>
        <input type="password" id="password" name="password" required>
        <button type="submit">登录</button>
    </form>
    <a href="/">返回首页</a>
</div>
</body>
</html>

注册页 (register.html)

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>注册</title></head>
<body>
<div class="container">
    <h1>注册</h1>
    <form th:action="@{/register}" method="post">
        <label for="username">用户名:</label>
        <input type="text" id="username" name="username" required>
        <label for="password">密码:</label>
        <input type="password" id="password" name="password" required>
        <label for="role">角色:</label>
        <select id="role" name="role" required>
            <option value="USER">用户</option>
            <option value="ADMIN">管理员</option>
        </select>
        <button type="submit">注册</button>
    </form>
    <a href="/">返回首页</a>
</div>
</body>
</html>

控制器逻辑:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class SecurityController {
    @Autowired
    private UserRepository userRepository;

    @GetMapping("/")
    public String index() { return "index"; }

    @GetMapping("/login")
    public String login() { return "login"; }

    @PostMapping("/register")
    public String registerUser(String username, String password, String role) {
        User user = new User(username, password, role);
        userRepository.save(user);
        return "redirect:/login";
    }
}

自动化测试: 最后,别忘了写单元测试来验证安全逻辑是否生效。下面是一个整合测试类的示例,模拟了未登录和已登录状态下的访问情况。

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import java.util.Base64;
import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class SecurityApplicationTests {
    @LocalServerPort
    private int port;

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    void testIndexPage() {
        String response = restTemplate.getForObject("http://localhost:" + port + "/", String.class);
        assertThat(response).contains("首页");
    }

    @Test
    void testUserPageWithUserAuthentication() {
        String credentials = "user:user123";
        String base64Credentials = Base64.getEncoder().encodeToString(credentials.getBytes());
        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", "Basic " + base64Credentials);
        HttpEntity<String> entity = new HttpEntity<>(headers);
        ResponseEntity<String> response = restTemplate.exchange(
            "http://localhost:" + port + "/user", HttpMethod.GET, entity, String.class
        );
        assertThat(response.getStatusCodeValue()).isEqualTo(200);
    }
}

五、总结

通过本文,我们完成了 Spring Boot 与 Spring Security 的全流程集成。从依赖引入到内存/数据库认证切换,再到基于角色的权限拦截,每一步都是构建安全应用的基础。记住,安全不是一蹴而就的,随着业务复杂度的增加,你可能还需要引入 OAuth2、JWT 等更高级的方案。但在起步阶段,掌握这些核心概念足以应对大多数场景。

在实际开发中,建议始终开启 HTTPS,并对密码进行加密存储(如 BCrypt),避免使用 NoOpPasswordEncoder 这样的明文编码器。希望这些经验能帮助你在项目中少走弯路。

目录

  1. Spring Boot 安全认证与授权实战
  2. 一、快速集成
  3. 二、认证方式详解
  4. 1. 基于内存的认证
  5. 2. 基于数据库的认证
  6. 三、授权策略
  7. 1. 基于角色的授权
  8. 2. 基于权限的授权
  9. 四、前端视图与测试
  10. 五、总结

更多推荐文章

查看全部
  • uv 虚拟环境管理:venv 创建、激活与版本指定
  • 昇腾 NPU 部署 Llama 2 模型:性能测试与优化实践
  • 渐进式AIGC系统:支持Nano-Banana-2绘画、VEO3视频生成及Agent智能体
  • 三级倒立摆 LQR 控制:Webots 仿真与 C 语言实现
  • JDK 17 官方下载与跨平台安装指南
  • MySQL 8.0 Windows 安装配置教程
  • 宇树 G1 人形机器人强化学习训练实战指南
  • 命令行工具 MCPHost:实现大模型与外部工具的 MCP 交互
  • GTC 2026 开幕:NemoClaw 入局 Agent 生态,具身智能端侧推理突破
  • POJ 2748 全排列算法:递归与回溯详解
  • 高精度算法详解:大整数加减乘除实现与原理
  • 降低论文 AI 检测率的三步实战策略:从 60% 到 20% 以下
  • DeepSeek-R1 大模型基于 MS-Swift 框架的部署、推理与微调实践
  • 国产数据库的新机遇:电科金仓以融合技术同步全球竞争
  • AR 滤镜触发机制:基于特定图案识别激活特效
  • FPGA小白学习日志二:利用LED实现2选1多路选择器
  • Trae 集成图片素描 MCP 工具实现多风格图像转换
  • PentAGI AI 自动化渗透工具 Docker 部署指南
  • 算法实战:位运算解决整数求和与缺失数字问题
  • AI 数据准备:EasyLink 多模态非结构化数据处理方案

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online