1. 项目概述
最近在开发一个AI智慧社区项目时,我实现了动态路由、修改密码和退出登录三个核心功能。在完成这些功能后,我发现其中涉及的技术点非常密集,几乎每个环节都是Java后端面试中的高频考点。于是,我决定将这些知识点整理成10个常见的面试题,并附上详细的解析和可能的追问。
1.1 核心功能与技术栈
这个项目主要使用了以下技术栈:
- 后端框架:Spring Boot 2.7 + Spring Security
- 持久层:MyBatis + MySQL
- 认证方案:JWT
- 缓存:Redis
- 前端:Vue.js
三个核心功能的技术实现要点:
- 动态路由:基于用户角色从数据库加载路由配置
- 修改密码:使用BCrypt加密存储新密码
- 退出登录:通过Redis黑名单机制使JWT失效
2. HTTP方法与RESTful设计
2.1 HTTP方法详解
在RESTful API设计中,HTTP方法的选择至关重要:
| 方法 | 语义 | 幂等性 | 参数位置 | 典型场景 |
|---|---|---|---|---|
| GET | 获取资源 | 是 | URL | 查询用户信息 |
| POST | 创建资源 | 否 | 请求体 | 新增用户 |
| PUT | 更新资源 | 是 | 请求体 | 修改用户信息 |
| DELETE | 删除资源 | 是 | URL | 删除用户 |
注意:PUT和PATCH的区别在于,PUT是全量更新,PATCH是部分更新。实际开发中,如果资源字段较多,建议使用PATCH避免不必要的网络传输。
2.2 修改密码的接口设计
在项目中,修改密码接口的设计引发了一些思考:
java复制@PutMapping("/sys/user/password")
public Result updatePassword(@RequestBody UpdatePasswordDTO dto) {
// 实现逻辑
}
这种设计比传统的/sys/user/updatePassword更符合RESTful规范,因为:
- URL中只包含名词(资源)
- 使用PUT方法明确表示更新操作
- 密码作为用户资源的属性进行更新
3. Spring参数绑定机制
3.1 @RequestBody vs @RequestParam
这两个注解的区别经常在面试中被问到:
| 特性 | @RequestBody | @RequestParam |
|---|---|---|
| 数据来源 | 请求体(通常是JSON) | URL查询参数 |
| 适用方法 | POST/PUT/PATCH | GET/DELETE |
| 参数类型 | 复杂对象 | 基本类型或简单对象 |
| 内容类型 | application/json | application/x-www-form-urlencoded |
3.2 常见问题排查
当POST请求接收不到参数时,需要检查:
- 请求头是否包含
Content-Type: application/json - 是否遗漏了
@RequestBody注解 - 请求体JSON格式是否正确
- DTO字段名称是否与JSON键名匹配
4. 认证机制深度解析
4.1 JWT与Session对比
| 维度 | Session机制 | JWT机制 |
|---|---|---|
| 状态管理 | 服务端存储 | 无状态 |
| 扩展性 | 需要共享存储 | 天然支持分布式 |
| 性能 | 每次需要查询 | 只需验证签名 |
| 失效控制 | 服务端可立即失效 | 需要额外机制(如黑名单) |
| 存储内容 | 只存session ID | 可存储自定义claims |
4.2 JWT黑名单实现
退出登录时使JWT失效的典型实现:
java复制// 登录时生成token并存入Redis
String token = Jwts.builder().setSubject(username).compact();
redisTemplate.opsForValue().set("token:"+token, "valid", 30, TimeUnit.MINUTES);
// 退出时加入黑名单
redisTemplate.opsForValue().set("blacklist:"+token, "invalid", getRemainingExpiration(token));
// 校验时检查黑名单
if (redisTemplate.hasKey("blacklist:"+token)) {
throw new AuthenticationException("Token已失效");
}
5. 密码安全实践
5.1 密码存储方案演进
- 明文存储:绝对禁止
- 简单哈希(MD5/SHA):易受彩虹表攻击
- 加盐哈希:每个用户使用不同盐值
- 自适应哈希(BCrypt):最佳实践
5.2 BCrypt使用示例
Spring Security提供的BCrypt实现:
java复制@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// 加密密码
String encodedPassword = passwordEncoder.encode(rawPassword);
// 校验密码
boolean matches = passwordEncoder.matches(rawPassword, encodedPassword);
BCrypt的特点:
- 自动生成随机盐(salt)
- 包含工作因子(work factor)控制计算成本
- 相同密码每次加密结果不同
- 内置防时序攻击保护
6. MyBatis高级用法
6.1 参数绑定安全
#{}和${}的区别示例:
xml复制<!-- 安全写法 -->
<select id="findById" resultType="User">
SELECT * FROM user WHERE id = #{id}
</select>
<!-- 危险写法(可能SQL注入) -->
<select id="findByColumn" resultType="User">
SELECT * FROM user ORDER BY ${columnName}
</select>
动态SQL的安全实践:
- 尽量使用
#{} - 必须使用
${}时,要在Java层做严格校验 - 考虑使用MyBatis的
@Param注解明确参数名
6.2 多参数处理
当Mapper方法有多个参数时的正确写法:
java复制// 接口定义
void updateUser(@Param("id") Long id, @Param("user") User user);
// XML配置
<update id="updateUser">
UPDATE user
SET name = #{user.name}, age = #{user.age}
WHERE id = #{id}
</update>
7. 分层架构设计
7.1 三层架构职责
| 层级 | 职责 | 典型注解 |
|---|---|---|
| Controller | 请求处理/响应返回 | @RestController |
| Service | 业务逻辑/事务管理 | @Service |
| Mapper | 数据访问 | @Mapper |
7.2 分层开发建议
-
Controller层:
- 保持精简
- 只做参数基本校验
- 统一异常处理
-
Service层:
- 业务逻辑的核心位置
- 方法粒度要适中
- 注意事务边界
-
Mapper层:
- 只关注SQL执行
- 避免业务逻辑
- 复杂查询使用XML配置
8. 拦截器与过滤器
8.1 执行流程对比
code复制HTTP请求 → 过滤器链 → DispatcherServlet → 拦截器链 → Controller
8.2 典型应用场景
过滤器适合:
- 请求日志记录
- 跨域处理
- 全局字符编码设置
拦截器适合:
- 权限校验(如JWT验证)
- 接口耗时统计
- 业务参数预处理
9. Redis实战应用
9.1 验证码实现
java复制// 生成验证码
String captcha = generateRandomCode();
String key = "captcha:" + UUID.randomUUID();
redisTemplate.opsForValue().set(key, captcha, 5, TimeUnit.MINUTES);
// 校验验证码
String storedCaptcha = redisTemplate.opsForValue().get(key);
if (!captcha.equals(storedCaptcha)) {
throw new BusinessException("验证码错误");
}
redisTemplate.delete(key); // 一次性使用
9.2 Redis选型考量
相比其他存储方案的优势:
- 性能:内存读写,微秒级响应
- 功能:丰富的数据结构(String/Hash/Set等)
- 可靠性:持久化机制(RDB/AOF)
- 扩展性:集群模式支持
10. 面试准备建议
10.1 技术深度挖掘
对于每个技术点,建议从以下几个维度准备:
- 基本原理:是什么、为什么需要
- 实现细节:怎么用、有哪些关键配置
- 对比分析:与其他方案的优劣比较
- 实践经验:实际项目中如何应用、遇到过什么问题
10.2 项目经验提炼
在面试中介绍项目时,可以采用STAR法则:
- Situation:项目背景
- Task:你的职责
- Action:关键技术决策
- Result:项目成果
比如介绍动态路由实现:
"在AI智慧社区项目(S)中,我负责权限模块开发(T)。基于RBAC模型设计了动态路由方案,通过JWT+Redis实现了无状态认证(A),最终支持了500+路由配置的灵活管理(R)。"
在实际开发过程中,我发现很多看似简单的功能背后都隐藏着丰富的技术细节。比如修改密码功能,就涉及RESTful设计、密码加密、事务管理等多个知识点。建议大家在日常开发中养成记录和总结的习惯,这样在面试时就能从容应对各种技术问题了。