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

Spring Boot 安全认证与授权实战

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

追风少年发布于 2026/3/22更新于 2026/5/2113 浏览
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. 五、总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Stable Diffusion XL 本地部署与艺术创作终端搭建
  • AI Agent 新范式:FastGPT 集成 MCP 协议构建工具增强智能体
  • 动态规划算法详解(C/C++)
  • Visual C++ 6.0 中文版在 Windows 11 下的安装与配置指南
  • 五大 AI 工具一站式提效指南:豆包、即梦、剪映、飞书与扣子
  • 汽车雷达多径幽灵目标检测:GLRT 与稀疏压缩感知解析
  • MySQL 表基础语法:增删查改核心技巧
  • Python 3.8+ 海象运算符详解
  • OpenClaw 多 Agent 与多飞书机器人配置指南
  • Whisper-large-v3 语音识别实测:准确率对比与工程优化
  • 码云新增 PR 显示权限,助力计算机教学
  • Cppcheck C/C++ 代码静态分析工具使用指南
  • Ubuntu 22.04 配置 VMware 共享文件夹实战指南
  • MCP 插件实战:Browser Tools 集成指南
  • 无人机低空智能巡飞巡检平台:全域感知与智能决策
  • DataX 二进制与源码部署及 DataX-Web 可视化平台搭建
  • JDK 25 已发布,为何多数企业仍停留在 Java 8
  • C++ 继承中同名成员的隐藏与重载规则解析
  • 企业电子招标采购管理系统功能与技术架构
  • 18 大小龙虾 AI Agent 框架技术选型与架构对比

相关免费在线工具

  • 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