SpringBoot 多人协作平台实战(7):完善登录模块 —— Spring 注解体系与密码加密实践

SpringBoot 多人协作平台实战(7):完善登录模块 ------ Spring 注解体系与密码加密实践

本文是《Java SpringBoot 多人协作平台》系列实战教程的第七篇,在前几课完成基础项目搭建之后,本节重点梳理 Spring 注解体系的核心概念,并在此基础上完善用户登录模块,引入 BCrypt 密码加密机制。


一、Spring 注解体系:项目中的两类对象

在 Spring Boot 项目中,类大体分为两类:

  • 容器对象(Entity) :仅用于承载数据,例如 User,不提供任何业务功能,通常放在 entity 包下,不需要交给 Spring 管理。
  • 服务对象(Bean) :负责处理业务逻辑、持久化、接口等功能,由 Spring 容器统一管理和注入。

理解这一区别,是正确使用 Spring 注解的前提。


二、用菜市场理解 Spring 注解

Spring 容器可以理解为整个菜市场的管理处 ,Bean 就是摊位上被统一管理的员工、工具或设备 。把一个类交给 Spring 管理,相当于给摊位办理营业执照,让市场统一调配。

2.1 @Component ------ 普通摊位

kotlin 复制代码
@Component
public class Scale {  // 电子秤
}
  • 含义:这是一个普通摊位,由市场统一登记管理。
  • 适用场景:通用工具类、辅助组件等没有明确分层职责的类。

2.2 @Service ------ 业务服务摊位

kotlin 复制代码
@Service
public class VegetableService {
    // 卖菜、算账、称重
}
  • 含义:专门负责处理业务逻辑的摊位。
  • 本质 :是 @Component 的语义化扩展,功能完全相同,但命名更清晰。
  • 适用场景:Service 层,处理核心业务逻辑。

2.3 @Repository ------ 仓库摊位

kotlin 复制代码
@Repository
public class VegetableDao {
    // 查菜还有多少
}
  • 含义:专门负责与数据库交互的仓库摊位。
  • 本质 :同样是 @Component 的扩展,额外提供持久层异常转译功能。
  • 适用场景:DAO 层,负责数据的增删改查。

2.4 @Controller ------ 门口接待窗口

kotlin 复制代码
@Controller
public class BuyController {
    // 顾客说要买啥,我来安排
}
  • 含义:市场门口的接待窗口,负责接收外部请求并分发处理。
  • 本质@Component 的扩展,结合视图解析使用;若接口直接返回 JSON,通常使用 @RestController
  • 适用场景:Controller 层,处理 HTTP 请求。

2.5 @Bean ------ 外购设备,手动登记(重点)

以上四个注解都是在自己的类上标注 ,让 Spring 自动扫描注册。但如果要使用的是第三方库的类 (无法修改其源码),就需要用 @Bean 手动注册:

typescript 复制代码
@Configuration
public class MarketConfig {

    @Bean
    public CashierMachine cashierMachine() {
        // 从外部引入,手动放入 Spring 容器
        return new CashierMachine();
    }
}

一句话区分:

  • @Component 家族:自己盖的摊位,让市场自动收编
  • @Bean:外面买来的设备,搬进来手动登记

2.6 @Configuration ------ 市场规划办公室

ruby 复制代码
@Configuration
public class MarketConfig {
    // 在这里统一声明 @Bean
}
  • 含义 :专门存放 @Bean 定义的配置类,相当于市场的规划办公室。
  • 本质 :本身也是一个 @Component,只是语义上专用于配置。

注解总结对照表

注解 类比 本质 适用层
@Component 普通小摊位 基础注解 通用
@Service 卖菜做生意摊位 @Component 扩展 业务层
@Repository 仓库摊位 @Component 扩展 数据层
@Controller 门口接待窗口 @Component 扩展 控制层
@Bean 外购设备手动登记 方法级注解 配置类中
@Configuration 市场规划办公室 @Component 扩展 配置类

三、查看接口实现类的快捷键

在 IntelliJ IDEA 中,当你看到一个接口或抽象类,想快速跳转到其实现或子类时:

css 复制代码
Ctrl + Alt + B

这个快捷键在阅读 Spring 框架源码、理解 UserDetailsService 等接口时非常实用。


四、完善登录模块:引入密码加密

4.1 密码安全的三条铁律

在实现登录模块时,密码处理必须严格遵守以下原则:

  1. 绝对不能用明文存储密码 ------ 一旦数据库泄露,用户密码将全部暴露。
  2. 加密是不可逆的 ------ 只能比对,不能还原原始密码。
  3. 加密必须一致 ------ 使用成熟的标准算法(如 BCrypt),不要自己设计加密方式。

BCrypt 每次加密相同的字符串会产生不同的哈希值(因为内置了随机 salt),但 matches() 方法能正确验证,这是它相比 MD5 等算法的重要优势。


4.2 配置加密服务:WebSecurityConfig

首先引入 Spring Security,在配置类中注册 BCryptPasswordEncoder Bean:

scala 复制代码
package hello.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

这里使用的正是 @BeanBCryptPasswordEncoder 是 Spring Security 提供的第三方类,无法在其上添加 @Component,因此通过 @Bean 方法手动注册到容器中。


4.3 实现用户服务:UserService

UserService 实现了 Spring Security 的 UserDetailsService 接口,负责用户注册(密码加密存储)和登录验证(按用户名加载用户信息):

java 复制代码
package hello.service;

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.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import javax.inject.Inject;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class UserService implements UserDetailsService {

    private BCryptPasswordEncoder bCryptPasswordEncoder;

    // 使用线程安全的 Map 临时存储用户(暂不接数据库)
    private Map<String, String> userPasswords = new ConcurrentHashMap<>();

    @Inject
    public UserService(BCryptPasswordEncoder bCryptPasswordEncoder) {
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
        // 初始化一个默认用户用于测试
        save("admin", "admin");
    }

    /**
     * 注册用户,密码经 BCrypt 加密后存储
     */
    public void save(String username, String password) {
        userPasswords.put(username, bCryptPasswordEncoder.encode(password));
    }

    /**
     * 查询用户加密后的密码
     */
    public String getPassword(String username) {
        return userPasswords.get(username);
    }

    /**
     * Spring Security 调用此方法加载用户信息进行认证
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        if (!userPasswords.containsKey(username)) {
            throw new UsernameNotFoundException(username + " 不存在!");
        }
        String encodedPassword = userPasswords.get(username);
        return new User(username, encodedPassword, Collections.emptyList());
    }
}

关键点解析:

  • @Inject 等同于 @Autowired,通过构造器注入 BCryptPasswordEncoder,是推荐的依赖注入方式。
  • ConcurrentHashMap 是线程安全的,适合多线程环境下的临时数据存储。
  • loadUserByUsernameUserDetailsService 的核心方法,Spring Security 在登录时会自动调用它获取用户信息并完成密码校验。
  • 本节课暂不接入数据库,使用内存 Map 存储,后续章节会替换为真实持久层。

五、整体架构回顾

本节课完成之后,登录模块的基本流程如下:

scss 复制代码
用户发起登录请求
       ↓
Controller 接收请求
       ↓
Spring Security 调用 UserDetailsService.loadUserByUsername()
       ↓
UserService 从 Map 中取出加密密码
       ↓
BCryptPasswordEncoder.matches() 校验密码
       ↓
认证成功 / 失败

六、下一步:会话与登录状态持久化

完成基本的注册与登录后,下一个核心问题是:

登录成功之后,如何在后续请求中携带用户身份信息?

这涉及到 Session / Token(如 JWT)机制的选择与实现,将在后续章节中详细展开。


小结

知识点 要点
Spring 注解分类 @Component 家族用于自定义类;@Bean 用于第三方类
密码加密原则 不可逆、不明文、使用标准算法(BCrypt)
UserDetailsService Spring Security 认证的核心接口,必须实现 loadUserByUsername
构造器注入 优于字段注入,利于测试和依赖明确
暂不接数据库 ConcurrentHashMap 模拟持久层,降低学习复杂度

系列课程:Java SpringBoot 多人协作平台实战 · 第七章 · SpringBoot完善登录模块

相关推荐
Amazing53071 小时前
Redis 7配置的三个隐藏陷阱
后端
Ting-yu1 小时前
Spring AI Alibaba零基础速成(2) ---- Ollama安装与使用
java·后端·spring·ai
qq_5470261791 小时前
SpringBoot + Redis 电商秒杀完整方案
spring boot·redis·后端
会编程的吕洞宾1 小时前
Spring_Boot_3_3_的___Transactional__
java·后端·spring
阿聪谈架构1 小时前
第11章:结构化输出与数据提取 —— 让 AI 直接返回你想要的数据格式
人工智能·后端
神奇小汤圆1 小时前
Java面试八股文+场景题+答案,100万字精华版,全网仅此一份
后端
数据仓库搬砖人2 小时前
XGBoost 调参指南
后端
学以智用2 小时前
.NET Core 仓储模式(Repository Pattern)完整教程
后端·.net
叫我少年2 小时前
Quartz.NET 调度框架:从入门到封装实战
后端