docs(deployment): 更新部署文档并添加自动化部署脚本

- 更新了 DEPLOYMENT.md 文档,增加了更多部署细节和说明
- 添加了 Linux 和 Windows 平台的自动化部署脚本
- 更新了 README.md,增加了部署相关说明
- 调整了 .env 文件配置,以适应新的部署流程
- 移除了部分不必要的代码和配置
This commit is contained in:
2025-09-10 14:16:27 +08:00
parent 18fe719f94
commit b2d940e014
114 changed files with 6990 additions and 247 deletions

View File

@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.jiebanke</groupId>
<artifactId>backend-java</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>auth-service</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Eureka Client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- OpenFeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- MyBatis Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<scope>runtime</scope>
</dependency>
<!-- BCrypt -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
</dependency>
<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- RabbitMQ -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- 结伴客公共模块 -->
<dependency>
<groupId>com.jiebanke</groupId>
<artifactId>common</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,26 @@
package com.jiebanke.auth;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@MapperScan("com.jiebanke.auth.mapper")
@ComponentScan(basePackages = "com.jiebanke")
public class AuthApplication {
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args);
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@@ -0,0 +1,187 @@
package com.jiebanke.auth.controller;
import com.jiebanke.auth.service.AuthRedisService;
import com.jiebanke.common.vo.ApiResponse;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private AuthService authService;
@Value("${jwt.secret}")
private String jwtSecret;
@Autowired
private AuthRedisService authRedisService;
/**
* 用户注册
*/
@PostMapping("/register")
public ApiResponse<AuthResult> register(@RequestBody RegisterRequest request) {
User user = new User();
user.setUsername(request.getUsername());
user.setEmail(request.getEmail());
user.setPhone(request.getPhone());
user.setRealName(request.getNickname());
AuthResult result = authService.register(user, request.getPassword());
return ApiResponse.success(result);
}
/**
* 用户登录
*/
@PostMapping("/login")
public ApiResponse<AuthResult> login(@RequestBody LoginRequest request) {
AuthResult result = authService.login(request.getUsername(), request.getPassword());
return ApiResponse.success(result);
}
/**
* 获取当前用户信息
*/
@GetMapping("/me")
public ApiResponse<User> getCurrentUser(@RequestHeader("userId") Long userId) {
User user = authService.getCurrentUser(userId);
return ApiResponse.success(user);
}
/**
* 更新用户个人信息
*/
@PutMapping("/profile")
public ApiResponse<User> updateProfile(
@RequestHeader("userId") Long userId,
@RequestBody User user) {
User updatedUser = authService.updateProfile(userId, user);
return ApiResponse.success(updatedUser);
}
/**
* 修改密码
*/
@PutMapping("/password")
public ApiResponse<Map<String, String>> changePassword(
@RequestHeader("userId") Long userId,
@RequestBody ChangePasswordRequest request) {
boolean success = authService.changePassword(userId, request.getCurrentPassword(), request.getNewPassword());
Map<String, String> result = new HashMap<>();
result.put("message", success ? "密码修改成功" : "密码修改失败");
return ApiResponse.success(result);
}
/**
* 微信登录/注册
*/
@PostMapping("/wechat")
public ApiResponse<AuthResult> wechatLogin(@RequestBody WechatLoginRequest request) {
AuthResult result = authService.wechatLogin(request.getCode(), request.getUserInfo());
return ApiResponse.success(result);
}
/**
* 管理员登录
*/
@PostMapping("/admin/login")
public ApiResponse<AuthResult> adminLogin(@RequestBody LoginRequest request) {
AuthResult result = authService.adminLogin(request.getUsername(), request.getPassword());
return ApiResponse.success(result);
}
@GetMapping("/validate")
public ApiResponse<Boolean> validateToken(@RequestHeader("Authorization") String token) {
try {
// 移除 "Bearer " 前缀
if (token.startsWith("Bearer ")) {
token = token.substring(7);
}
// 解析JWT令牌
Claims claims = Jwts.parserBuilder()
.setSigningKey(jwtSecret.getBytes())
.build()
.parseClaimsJws(token)
.getBody();
// 从Redis中获取用户登录状态
Long userId = authRedisService.getUserLoginStatus(token);
if (userId != null && userId.equals(claims.get("userId", Long.class))) {
return ApiResponse.success(true);
}
return ApiResponse.success(false);
} catch (Exception e) {
return ApiResponse.success(false);
}
}
// 请求体类
static class RegisterRequest {
private String username;
private String password;
private String nickname;
private String email;
private String phone;
// Getters and setters
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getNickname() { return nickname; }
public void setNickname(String nickname) { this.nickname = nickname; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
}
static class LoginRequest {
private String username;
private String password;
// Getters and setters
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
static class ChangePasswordRequest {
private String currentPassword;
private String newPassword;
// Getters and setters
public String getCurrentPassword() { return currentPassword; }
public void setCurrentPassword(String currentPassword) { this.currentPassword = currentPassword; }
public String getNewPassword() { return newPassword; }
public void setNewPassword(String newPassword) { this.newPassword = newPassword; }
}
static class WechatLoginRequest {
private String code;
private Object userInfo;
// Getters and setters
public String getCode() { return code; }
public void setCode(String code) { this.code = code; }
public Object getUserInfo() { return userInfo; }
public void setUserInfo(Object userInfo) { this.userInfo = userInfo; }
}
}

View File

@@ -0,0 +1,23 @@
package com.jiebanke.auth.entity;
import com.jiebanke.common.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@EqualsAndHashCode(callSuper = true)
public class User extends BaseEntity {
private String username;
private String password;
private String email;
private String phone;
private String realName;
private String idCard;
private String status;
private BigDecimal balance;
private Integer creditScore;
private LocalDateTime lastLogin;
}

View File

@@ -0,0 +1,68 @@
package com.jiebanke.auth.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jiebanke.auth.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
@Mapper
public interface UserMapper extends BaseMapper<User> {
/**
* 根据用户名查找用户
* @param username 用户名
* @return 用户
*/
@Select("SELECT * FROM users WHERE username = #{username}")
User selectByUsername(@Param("username") String username);
/**
* 根据邮箱查找用户
* @param email 邮箱
* @return 用户
*/
@Select("SELECT * FROM users WHERE email = #{email}")
User selectByEmail(@Param("email") String email);
/**
* 根据手机号查找用户
* @param phone 手机号
* @return 用户
*/
@Select("SELECT * FROM users WHERE phone = #{phone}")
User selectByPhone(@Param("phone") String phone);
/**
* 检查用户名是否存在
* @param username 用户名
* @return 是否存在
*/
@Select("SELECT COUNT(*) FROM users WHERE username = #{username}")
int existsByUsername(@Param("username") String username);
/**
* 检查邮箱是否存在
* @param email 邮箱
* @return 是否存在
*/
@Select("SELECT COUNT(*) FROM users WHERE email = #{email}")
int existsByEmail(@Param("email") String email);
/**
* 检查手机号是否存在
* @param phone 手机号
* @return 是否存在
*/
@Select("SELECT COUNT(*) FROM users WHERE phone = #{phone}")
int existsByPhone(@Param("phone") String phone);
/**
* 更新最后登录时间
* @param id 用户ID
* @return 更新记录数
*/
@Update("UPDATE users SET last_login = NOW(), updated_at = NOW() WHERE id = #{id}")
int updateLastLogin(@Param("id") Long id);
}

View File

@@ -0,0 +1,46 @@
package com.jiebanke.auth.service;
import com.jiebanke.common.config.RabbitMQConfig;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class AuthRabbitMQService {
@Autowired
private RabbitTemplate rabbitTemplate;
// 发送登录成功消息
public void sendLoginSuccessMessage(Long userId, String username, String ip) {
Map<String, Object> message = new HashMap<>();
message.put("userId", userId);
message.put("username", username);
message.put("ip", ip);
message.put("eventType", "USER_LOGIN_SUCCESS");
rabbitTemplate.convertAndSend(
RabbitMQConfig.EXCHANGE_NAME,
RabbitMQConfig.USER_ROUTING_KEY,
message
);
}
// 发送登录失败消息
public void sendLoginFailureMessage(String username, String ip, String reason) {
Map<String, Object> message = new HashMap<>();
message.put("username", username);
message.put("ip", ip);
message.put("reason", reason);
message.put("eventType", "USER_LOGIN_FAILURE");
rabbitTemplate.convertAndSend(
RabbitMQConfig.EXCHANGE_NAME,
RabbitMQConfig.USER_ROUTING_KEY,
message
);
}
}

View File

@@ -0,0 +1,53 @@
package com.jiebanke.auth.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class AuthRedisService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 缓存验证码
public void cacheVerificationCode(String phone, String code) {
redisTemplate.opsForValue().set("verification:code:" + phone, code, 5, TimeUnit.MINUTES);
}
// 获取缓存的验证码
public String getCachedVerificationCode(String phone) {
return (String) redisTemplate.opsForValue().get("verification:code:" + phone);
}
// 删除缓存的验证码
public void removeCachedVerificationCode(String phone) {
redisTemplate.delete("verification:code:" + phone);
}
// 缓存登录失败次数
public void cacheLoginFailures(String identifier, Integer failures) {
redisTemplate.opsForValue().set("login:failures:" + identifier, failures, 1, TimeUnit.HOURS);
}
// 获取登录失败次数
public Integer getLoginFailures(String identifier) {
return (Integer) redisTemplate.opsForValue().get("login:failures:" + identifier);
}
// 增加登录失败次数
public void incrementLoginFailures(String identifier) {
Integer failures = getLoginFailures(identifier);
if (failures == null) {
failures = 0;
}
redisTemplate.opsForValue().set("login:failures:" + identifier, failures + 1, 1, TimeUnit.HOURS);
}
// 清除登录失败次数
public void clearLoginFailures(String identifier) {
redisTemplate.delete("login:failures:" + identifier);
}
}

View File

@@ -0,0 +1,11 @@
package com.jiebanke.auth.service;
import com.jiebanke.auth.entity.User;
import lombok.Data;
@Data
public class AuthResult {
private User user;
private String token;
private String message;
}

View File

@@ -0,0 +1,61 @@
package com.jiebanke.auth.service;
import com.jiebanke.auth.entity.User;
public interface AuthService {
/**
* 用户注册
* @param user 用户信息
* @return 注册后的用户信息和Token
*/
AuthResult register(User user, String password);
/**
* 用户登录
* @param username 用户名/邮箱/手机号
* @param password 密码
* @return 登录后的用户信息和Token
*/
AuthResult login(String username, String password);
/**
* 获取当前用户信息
* @param userId 用户ID
* @return 用户信息
*/
User getCurrentUser(Long userId);
/**
* 更新用户信息
* @param userId 用户ID
* @param user 更新的用户信息
* @return 更新后的用户信息
*/
User updateProfile(Long userId, User user);
/**
* 修改密码
* @param userId 用户ID
* @param currentPassword 当前密码
* @param newPassword 新密码
* @return 是否修改成功
*/
boolean changePassword(Long userId, String currentPassword, String newPassword);
/**
* 微信登录/注册
* @param code 微信授权码
* @param userInfo 微信用户信息
* @return 登录后的用户信息和Token
*/
AuthResult wechatLogin(String code, Object userInfo);
/**
* 管理员登录
* @param username 用户名/邮箱/手机号
* @param password 密码
* @return 登录后的管理员信息和Token
*/
AuthResult adminLogin(String username, String password);
}

View File

@@ -0,0 +1,255 @@
package com.jiebanke.auth.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jiebanke.auth.entity.User;
import com.jiebanke.auth.mapper.UserMapper;
import com.jiebanke.auth.service.AuthResult;
import com.jiebanke.auth.service.AuthService;
import com.jiebanke.auth.util.JwtUtil;
import com.jiebanke.common.exception.BusinessException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class AuthServiceImpl extends ServiceImpl<UserMapper, User> implements AuthService {
@Autowired
private UserMapper userMapper;
@Autowired
private JwtUtil jwtUtil;
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Override
public AuthResult register(User user, String password) {
// 检查用户名是否已存在
if (userMapper.existsByUsername(user.getUsername()) > 0) {
throw new BusinessException("用户名已存在");
}
// 检查邮箱是否已存在
if (user.getEmail() != null && userMapper.existsByEmail(user.getEmail()) > 0) {
throw new BusinessException("邮箱已存在");
}
// 检查手机号是否已存在
if (user.getPhone() != null && userMapper.existsByPhone(user.getPhone()) > 0) {
throw new BusinessException("手机号已存在");
}
// 加密密码
String hashedPassword = passwordEncoder.encode(password);
user.setPassword(hashedPassword);
// 设置默认值
if (user.getStatus() == null) {
user.setStatus("active");
}
// 创建新用户
this.save(user);
// 生成Token
String token = jwtUtil.generateToken(user.getId());
// 更新最后登录时间
userMapper.updateLastLogin(user.getId());
// 创建返回结果
AuthResult result = new AuthResult();
result.setUser(user);
result.setToken(token);
result.setMessage("注册成功");
return result;
}
@Override
public AuthResult login(String username, String password) {
if (username == null || username.isEmpty() || password == null || password.isEmpty()) {
throw new BusinessException("用户名和密码不能为空");
}
// 查找用户(支持用户名、邮箱、手机号登录)
User user = userMapper.selectByUsername(username);
if (user == null) {
user = userMapper.selectByEmail(username);
}
if (user == null) {
user = userMapper.selectByPhone(username);
}
if (user == null) {
throw new BusinessException("用户不存在");
}
// 检查用户状态
if (!"active".equals(user.getStatus())) {
throw new BusinessException("账户已被禁用");
}
// 验证密码
if (!passwordEncoder.matches(password, user.getPassword())) {
throw new BusinessException("密码错误");
}
// 生成Token
String token = jwtUtil.generateToken(user.getId());
// 更新最后登录时间
userMapper.updateLastLogin(user.getId());
// 创建返回结果
AuthResult result = new AuthResult();
result.setUser(user);
result.setToken(token);
result.setMessage("登录成功");
return result;
}
@Override
public User getCurrentUser(Long userId) {
User user = this.getById(userId);
if (user == null) {
throw new BusinessException("用户不存在");
}
return user;
}
@Override
public User updateProfile(Long userId, User user) {
User existingUser = this.getById(userId);
if (existingUser == null) {
throw new BusinessException("用户不存在");
}
// 更新字段
if (user.getRealName() != null) {
existingUser.setRealName(user.getRealName());
}
if (user.getEmail() != null) {
existingUser.setEmail(user.getEmail());
}
if (user.getPhone() != null) {
existingUser.setPhone(user.getPhone());
}
this.updateById(existingUser);
return existingUser;
}
@Override
public boolean changePassword(Long userId, String currentPassword, String newPassword) {
if (currentPassword == null || currentPassword.isEmpty() || newPassword == null || newPassword.isEmpty()) {
throw new BusinessException("当前密码和新密码不能为空");
}
if (newPassword.length() < 6) {
throw new BusinessException("新密码长度不能少于6位");
}
User user = this.getById(userId);
if (user == null) {
throw new BusinessException("用户不存在");
}
// 验证当前密码
if (!passwordEncoder.matches(currentPassword, user.getPassword())) {
throw new BusinessException("当前密码错误");
}
// 加密新密码
String hashedPassword = passwordEncoder.encode(newPassword);
user.setPassword(hashedPassword);
// 更新密码
return this.updateById(user);
}
@Override
public AuthResult wechatLogin(String code, Object userInfo) {
if (code == null || code.isEmpty()) {
throw new BusinessException("微信授权码不能为空");
}
// 这里应该调用微信API获取openid和unionid
// 模拟获取微信用户信息
String openid = "mock_openid_" + System.currentTimeMillis();
// 查找是否已存在微信用户
// 注意在实际实现中应该有一个专门的字段来存储微信openid
User user = null;
if (user == null) {
// 创建新用户(微信注册)
user = new User();
user.setUsername("wx_" + openid.substring(Math.max(0, openid.length() - 8)));
String randomPassword = String.valueOf(System.currentTimeMillis()).substring(0, 8);
String hashedPassword = passwordEncoder.encode(randomPassword);
user.setPassword(hashedPassword);
user.setRealName("微信用户");
user.setStatus("active");
this.save(user);
}
// 生成Token
String token = jwtUtil.generateToken(user.getId());
// 创建返回结果
AuthResult result = new AuthResult();
result.setUser(user);
result.setToken(token);
result.setMessage("微信登录成功");
return result;
}
@Override
public AuthResult adminLogin(String username, String password) {
if (username == null || username.isEmpty() || password == null || password.isEmpty()) {
throw new BusinessException("用户名和密码不能为空");
}
// 查找用户(支持用户名、邮箱、手机号登录)
User user = userMapper.selectByUsername(username);
if (user == null) {
user = userMapper.selectByEmail(username);
}
if (user == null) {
user = userMapper.selectByPhone(username);
}
if (user == null) {
throw new BusinessException("用户不存在");
}
// 检查用户状态
if (!"active".equals(user.getStatus())) {
throw new BusinessException("账户已被禁用");
}
// 验证密码
if (!passwordEncoder.matches(password, user.getPassword())) {
throw new BusinessException("密码错误");
}
// 生成Token
String token = jwtUtil.generateToken(user.getId());
// 更新最后登录时间
userMapper.updateLastLogin(user.getId());
// 创建返回结果
AuthResult result = new AuthResult();
result.setUser(user);
result.setToken(token);
result.setMessage("管理员登录成功");
return result;
}
}

View File

@@ -0,0 +1,107 @@
package com.jiebanke.auth.util;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
@Component
public class JwtUtil {
@Value("${jwt.secret:mySecretKey}")
private String secret;
@Value("${jwt.expiration:604800}")
private Long expiration;
/**
* 生成JWT Token
* @param userId 用户ID
* @return JWT Token
*/
public String generateToken(Long userId) {
Map<String, Object> claims = new HashMap<>();
return createToken(claims, userId.toString());
}
/**
* 从JWT Token中提取用户ID
* @param token JWT Token
* @return 用户ID
*/
public Long extractUserId(String token) {
return Long.valueOf(extractClaim(token, Claims::getSubject));
}
/**
* 验证JWT Token是否有效
* @param token JWT Token
* @param userId 用户ID
* @return 是否有效
*/
public Boolean validateToken(String token, Long userId) {
final Long extractedUserId = extractUserId(token);
return (extractedUserId.equals(userId) && !isTokenExpired(token));
}
/**
* 从JWT Token中提取声明
* @param token JWT Token
* @param claimsResolver 声明解析器
* @param <T> 声明类型
* @return 声明值
*/
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
/**
* 从JWT Token中提取所有声明
* @param token JWT Token
* @return 所有声明
*/
private Claims extractAllClaims(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}
/**
* 检查JWT Token是否过期
* @param token JWT Token
* @return 是否过期
*/
private Boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
/**
* 从JWT Token中提取过期时间
* @param token JWT Token
* @return 过期时间
*/
public Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
/**
* 创建JWT Token
* @param claims 声明
* @param subject 主题
* @return JWT Token
*/
private String createToken(Map<String, Object> claims, String subject) {
return Jwts.builder()
.setClaims(claims)
.setSubject(subject)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
}

View File

@@ -0,0 +1,36 @@
server:
port: 8081
spring:
application:
name: auth-service
datasource:
url: jdbc:mysql://localhost:3306/jiebanke?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
redis:
host: localhost
port: 6379
database: 0
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
jwt:
secret: mySecretKey
expiration: 604800
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: auto

View File

@@ -0,0 +1,55 @@
package com.jiebanke.auth.service;
import com.jiebanke.common.config.RabbitMQConfig;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import static org.mockito.Mockito.*;
class AuthRabbitMQServiceTest {
@Mock
private RabbitTemplate rabbitTemplate;
@InjectMocks
private AuthRabbitMQService authRabbitMQService;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
void testSendLoginSuccessMessage() {
Long userId = 1L;
String username = "testUser";
String ip = "127.0.0.1";
authRabbitMQService.sendLoginSuccessMessage(userId, username, ip);
verify(rabbitTemplate).convertAndSend(
RabbitMQConfig.EXCHANGE_NAME,
RabbitMQConfig.USER_ROUTING_KEY,
anyMap()
);
}
@Test
void testSendLoginFailureMessage() {
String username = "testUser";
String ip = "127.0.0.1";
String reason = "Invalid credentials";
authRabbitMQService.sendLoginFailureMessage(username, ip, reason);
verify(rabbitTemplate).convertAndSend(
RabbitMQConfig.EXCHANGE_NAME,
RabbitMQConfig.USER_ROUTING_KEY,
anyMap()
);
}
}

View File

@@ -0,0 +1,112 @@
package com.jiebanke.auth.service;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
class AuthRedisServiceTest {
@Mock
private RedisTemplate<String, Object> redisTemplate;
@Mock
private ValueOperations<String, Object> valueOperations;
@InjectMocks
private AuthRedisService authRedisService;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
when(redisTemplate.opsForValue()).thenReturn(valueOperations);
}
@Test
void testCacheVerificationCode() {
String phone = "13800138000";
String code = "123456";
authRedisService.cacheVerificationCode(phone, code);
verify(valueOperations).set(eq("verification:code:" + phone), eq(code), anyLong(), any());
}
@Test
void testGetCachedVerificationCode() {
String phone = "13800138000";
String code = "123456";
when(valueOperations.get("verification:code:" + phone)).thenReturn(code);
String result = authRedisService.getCachedVerificationCode(phone);
assertEquals(code, result);
verify(valueOperations).get("verification:code:" + phone);
}
@Test
void testRemoveCachedVerificationCode() {
String phone = "13800138000";
authRedisService.removeCachedVerificationCode(phone);
verify(redisTemplate).delete("verification:code:" + phone);
}
@Test
void testCacheLoginFailures() {
String identifier = "testUser";
Integer failures = 3;
authRedisService.cacheLoginFailures(identifier, failures);
verify(valueOperations).set(eq("login:failures:" + identifier), eq(failures), anyLong(), any());
}
@Test
void testGetLoginFailures() {
String identifier = "testUser";
Integer failures = 3;
when(valueOperations.get("login:failures:" + identifier)).thenReturn(failures);
Integer result = authRedisService.getLoginFailures(identifier);
assertEquals(failures, result);
verify(valueOperations).get("login:failures:" + identifier);
}
@Test
void testIncrementLoginFailures() {
String identifier = "testUser";
when(valueOperations.get("login:failures:" + identifier)).thenReturn(null);
authRedisService.incrementLoginFailures(identifier);
verify(valueOperations).set(eq("login:failures:" + identifier), eq(1), anyLong(), any());
}
@Test
void testIncrementLoginFailuresWithExistingValue() {
String identifier = "testUser";
when(valueOperations.get("login:failures:" + identifier)).thenReturn(2);
authRedisService.incrementLoginFailures(identifier);
verify(valueOperations).set(eq("login:failures:" + identifier), eq(3), anyLong(), any());
}
@Test
void testClearLoginFailures() {
String identifier = "testUser";
authRedisService.clearLoginFailures(identifier);
verify(redisTemplate).delete("login:failures:" + identifier);
}
}