AuthService.java
Springboot/bookstore-api/src/main/java/com/example/bookstore/service/AuthService.java
package com.example.bookstore.service;
import com.example.bookstore.dto.AuthResponse;
import com.example.bookstore.dto.LoginRequest;
import com.example.bookstore.dto.RegisterRequest;
import com.example.bookstore.dto.UserResponse;
import com.example.bookstore.entity.Role;
import com.example.bookstore.entity.User;
import com.example.bookstore.exception.DuplicateResourceException;
import com.example.bookstore.repository.UserRepository;
import com.example.bookstore.security.JwtService;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 認證 Service:處理「註冊」與「登入」的商業邏輯。
*/
@Service
public class AuthService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final AuthenticationManager authenticationManager;
private final JwtService jwtService;
public AuthService(UserRepository userRepository,
PasswordEncoder passwordEncoder,
AuthenticationManager authenticationManager,
JwtService jwtService) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.authenticationManager = authenticationManager;
this.jwtService = jwtService;
}
/**
* 註冊新使用者。
* 流程:檢查重複 → 密碼加密 → 存入資料庫 → 回傳使用者資料(不含密碼)
*/
@Transactional
public UserResponse register(RegisterRequest request) {
// 1. 檢查帳號與 Email 是否已被使用(重複則回 409 Conflict)
if (userRepository.existsByUsername(request.username())) {
throw new DuplicateResourceException("帳號已被使用:" + request.username());
}
if (userRepository.existsByEmail(request.email())) {
throw new DuplicateResourceException("Email 已被使用:" + request.email());
}
// 2. 建立使用者:密碼務必先用 BCrypt 加密再儲存!
// passwordEncoder.encode() 每次都會產生不同的雜湊值(自動加鹽)
User user = new User(
request.username(),
passwordEncoder.encode(request.password()),
request.email(),
Role.ROLE_USER // 新註冊者一律是一般使用者(安全預設)
);
// 3. 存入資料庫並回傳(UserResponse 不包含密碼欄位)
User saved = userRepository.save(user);
return UserResponse.from(saved);
}
/**
* 登入。
* 流程:驗證帳號密碼 → 產生 JWT → 回傳 token 給客戶端
*/
public AuthResponse login(LoginRequest request) {
// 1. 把帳號密碼交給 AuthenticationManager 驗證。
// 它會透過 CustomUserDetailsService 載入使用者、
// 再用 BCrypt 比對密碼;驗證失敗會拋出 BadCredentialsException
// (由 GlobalExceptionHandler 統一轉成 401 回應)。
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
request.username(), request.password()));
// 2. 驗證通過 → 從資料庫取得完整使用者資料
User user = userRepository.findByUsername(request.username())
.orElseThrow(); // 前面已驗證成功,理論上必定存在
// 3. 產生 JWT 並包裝成回應
String token = jwtService.generateToken(user.getUsername(), user.getRole());
return new AuthResponse(
token,
"Bearer",
jwtService.getExpirationMs() / 1000, // 毫秒轉成秒
user.getUsername(),
user.getRole().name());
}
}
Related articles
BookstoreApiApplication.java
BookstoreApiApplication.java — java source code from the Springboot learning materials (Springboot/bookstore-api/src/main/java/com/example/bookstore/BookstoreApiApplication.java).
Read article →DataInitializer.java
DataInitializer.java — java source code from the Springboot learning materials (Springboot/bookstore-api/src/main/java/com/example/bookstore/config/DataInitializer.java).
Read article →SecurityConfig.java
SecurityConfig.java — java source code from the Springboot learning materials (Springboot/bookstore-api/src/main/java/com/example/bookstore/config/SecurityConfig.java).
Read article →AuthController.java
AuthController.java — java source code from the Springboot learning materials (Springboot/bookstore-api/src/main/java/com/example/bookstore/controller/AuthController.java).
Read article →BookController.java
BookController.java — java source code from the Springboot learning materials (Springboot/bookstore-api/src/main/java/com/example/bookstore/controller/BookController.java).
Read article →ApiErrorResponse.java
ApiErrorResponse.java — java source code from the Springboot learning materials (Springboot/bookstore-api/src/main/java/com/example/bookstore/dto/ApiErrorResponse.java).
Read article →