mirror of
https://github.com/cna-bootcamp/phonebill.git
synced 2025-12-06 08:06:24 +00:00
564 lines
16 KiB
Plaintext
564 lines
16 KiB
Plaintext
@startuml
|
|
!theme mono
|
|
|
|
title Auth Service - Detailed Class Design
|
|
|
|
package "com.unicorn.phonebill.auth" {
|
|
|
|
package "controller" {
|
|
class AuthController {
|
|
-authService: AuthService
|
|
-tokenService: TokenService
|
|
|
|
+login(request: LoginRequest): ApiResponse<LoginResponse>
|
|
+logout(): ApiResponse<SuccessResponse>
|
|
+verifyToken(): ApiResponse<TokenVerifyResponse>
|
|
+refreshToken(request: RefreshTokenRequest): ApiResponse<RefreshTokenResponse>
|
|
+getUserPermissions(): ApiResponse<PermissionsResponse>
|
|
+checkPermission(request: PermissionCheckRequest): ApiResponse<PermissionCheckResponse>
|
|
+getUserInfo(): ApiResponse<UserInfoResponse>
|
|
}
|
|
}
|
|
|
|
package "dto" {
|
|
class LoginRequest {
|
|
-userId: String
|
|
-password: String
|
|
-autoLogin: boolean
|
|
|
|
+getUserId(): String
|
|
+getPassword(): String
|
|
+isAutoLogin(): boolean
|
|
+validate(): void
|
|
}
|
|
|
|
class LoginResponse {
|
|
-accessToken: String
|
|
-refreshToken: String
|
|
-expiresIn: int
|
|
-user: UserInfo
|
|
|
|
+getAccessToken(): String
|
|
+getRefreshToken(): String
|
|
+getExpiresIn(): int
|
|
+getUser(): UserInfo
|
|
}
|
|
|
|
class RefreshTokenRequest {
|
|
-refreshToken: String
|
|
|
|
+getRefreshToken(): String
|
|
+validate(): void
|
|
}
|
|
|
|
class RefreshTokenResponse {
|
|
-accessToken: String
|
|
-expiresIn: int
|
|
|
|
+getAccessToken(): String
|
|
+getExpiresIn(): int
|
|
}
|
|
|
|
class TokenVerifyResponse {
|
|
-valid: boolean
|
|
-user: UserInfo
|
|
-expiresIn: int
|
|
|
|
+isValid(): boolean
|
|
+getUser(): UserInfo
|
|
+getExpiresIn(): int
|
|
}
|
|
|
|
class PermissionCheckRequest {
|
|
-serviceType: String
|
|
|
|
+getServiceType(): String
|
|
+validate(): void
|
|
}
|
|
|
|
class PermissionCheckResponse {
|
|
-serviceType: String
|
|
-hasPermission: boolean
|
|
-permissionDetails: Permission
|
|
|
|
+getServiceType(): String
|
|
+isHasPermission(): boolean
|
|
+getPermissionDetails(): Permission
|
|
}
|
|
|
|
class PermissionsResponse {
|
|
-userId: String
|
|
-permissions: List<Permission>
|
|
|
|
+getUserId(): String
|
|
+getPermissions(): List<Permission>
|
|
}
|
|
|
|
class UserInfoResponse {
|
|
-userId: String
|
|
-userName: String
|
|
-phoneNumber: String
|
|
-email: String
|
|
-status: String
|
|
-lastLoginAt: LocalDateTime
|
|
-permissions: List<String>
|
|
|
|
+getUserId(): String
|
|
+getUserName(): String
|
|
+getPhoneNumber(): String
|
|
+getEmail(): String
|
|
+getStatus(): String
|
|
+getLastLoginAt(): LocalDateTime
|
|
+getPermissions(): List<String>
|
|
}
|
|
|
|
class UserInfo {
|
|
-userId: String
|
|
-userName: String
|
|
-phoneNumber: String
|
|
-permissions: List<String>
|
|
|
|
+getUserId(): String
|
|
+getUserName(): String
|
|
+getPhoneNumber(): String
|
|
+getPermissions(): List<String>
|
|
}
|
|
|
|
class Permission {
|
|
-permission: String
|
|
-description: String
|
|
-granted: boolean
|
|
|
|
+getPermission(): String
|
|
+getDescription(): String
|
|
+isGranted(): boolean
|
|
}
|
|
|
|
class SuccessResponse {
|
|
-message: String
|
|
|
|
+getMessage(): String
|
|
}
|
|
}
|
|
|
|
package "service" {
|
|
interface AuthService {
|
|
+authenticateUser(userId: String, password: String): AuthenticationResult
|
|
+getUserInfo(userId: String): UserInfoDetail
|
|
+refreshUserToken(userId: String): TokenRefreshResult
|
|
+checkServicePermission(userId: String, serviceType: String): PermissionResult
|
|
+invalidateUserPermissions(userId: String): void
|
|
}
|
|
|
|
class AuthServiceImpl {
|
|
-userRepository: UserRepository
|
|
-tokenService: TokenService
|
|
-permissionService: PermissionService
|
|
-redisTemplate: RedisTemplate
|
|
-passwordEncoder: PasswordEncoder
|
|
-loginHistoryRepository: LoginHistoryRepository
|
|
|
|
+authenticateUser(userId: String, password: String): AuthenticationResult
|
|
+getUserInfo(userId: String): UserInfoDetail
|
|
+refreshUserToken(userId: String): TokenRefreshResult
|
|
+checkServicePermission(userId: String, serviceType: String): PermissionResult
|
|
+invalidateUserPermissions(userId: String): void
|
|
-validateLoginAttempts(user: User): void
|
|
-handleFailedLogin(userId: String): void
|
|
-handleSuccessfulLogin(user: User): void
|
|
-createUserSession(user: User, autoLogin: boolean): void
|
|
-saveLoginHistory(userId: String, ipAddress: String): void
|
|
}
|
|
|
|
interface TokenService {
|
|
+generateAccessToken(userInfo: UserInfoDetail): String
|
|
+generateRefreshToken(userId: String): String
|
|
+validateAccessToken(token: String): DecodedToken
|
|
+validateRefreshToken(token: String): boolean
|
|
+extractUserId(token: String): String
|
|
+getTokenExpiration(token: String): LocalDateTime
|
|
}
|
|
|
|
class TokenServiceImpl {
|
|
-jwtSecret: String
|
|
-accessTokenExpiry: int
|
|
-refreshTokenExpiry: int
|
|
|
|
+generateAccessToken(userInfo: UserInfoDetail): String
|
|
+generateRefreshToken(userId: String): String
|
|
+validateAccessToken(token: String): DecodedToken
|
|
+validateRefreshToken(token: String): boolean
|
|
+extractUserId(token: String): String
|
|
+getTokenExpiration(token: String): LocalDateTime
|
|
-createJwtToken(subject: String, claims: Map<String, Object>, expiry: int): String
|
|
-parseJwtToken(token: String): Claims
|
|
}
|
|
|
|
interface PermissionService {
|
|
+validateServiceAccess(permissions: List<String>, serviceType: String): PermissionResult
|
|
+getUserPermissions(userId: String): List<Permission>
|
|
+cacheUserPermissions(userId: String, permissions: List<Permission>): void
|
|
+invalidateUserPermissions(userId: String): void
|
|
}
|
|
|
|
class PermissionServiceImpl {
|
|
-userPermissionRepository: UserPermissionRepository
|
|
-redisTemplate: RedisTemplate
|
|
|
|
+validateServiceAccess(permissions: List<String>, serviceType: String): PermissionResult
|
|
+getUserPermissions(userId: String): List<Permission>
|
|
+cacheUserPermissions(userId: String, permissions: List<Permission>): void
|
|
+invalidateUserPermissions(userId: String): void
|
|
-mapServiceTypeToPermission(serviceType: String): String
|
|
-checkPermissionGranted(permissions: List<String>, requiredPermission: String): boolean
|
|
}
|
|
}
|
|
|
|
package "domain" {
|
|
class User {
|
|
-userId: String
|
|
-userName: String
|
|
-phoneNumber: String
|
|
-email: String
|
|
-passwordHash: String
|
|
-salt: String
|
|
-status: UserStatus
|
|
-loginAttemptCount: int
|
|
-lockedUntil: LocalDateTime
|
|
-lastLoginAt: LocalDateTime
|
|
-createdAt: LocalDateTime
|
|
-updatedAt: LocalDateTime
|
|
|
|
+getUserId(): String
|
|
+getUserName(): String
|
|
+getPhoneNumber(): String
|
|
+getEmail(): String
|
|
+getPasswordHash(): String
|
|
+getSalt(): String
|
|
+getStatus(): UserStatus
|
|
+getLoginAttemptCount(): int
|
|
+getLockedUntil(): LocalDateTime
|
|
+getLastLoginAt(): LocalDateTime
|
|
+isAccountLocked(): boolean
|
|
+canAttemptLogin(): boolean
|
|
+incrementLoginAttempt(): void
|
|
+resetLoginAttempt(): void
|
|
+lockAccount(duration: Duration): void
|
|
+updateLastLoginAt(loginTime: LocalDateTime): void
|
|
}
|
|
|
|
enum UserStatus {
|
|
ACTIVE
|
|
INACTIVE
|
|
LOCKED
|
|
|
|
+getValue(): String
|
|
}
|
|
|
|
class UserSession {
|
|
-userId: String
|
|
-sessionId: String
|
|
-userInfo: UserInfoDetail
|
|
-permissions: List<String>
|
|
-lastAccessTime: LocalDateTime
|
|
-createdAt: LocalDateTime
|
|
-ttl: Duration
|
|
|
|
+getUserId(): String
|
|
+getSessionId(): String
|
|
+getUserInfo(): UserInfoDetail
|
|
+getPermissions(): List<String>
|
|
+getLastAccessTime(): LocalDateTime
|
|
+getCreatedAt(): LocalDateTime
|
|
+getTtl(): Duration
|
|
+updateLastAccessTime(): void
|
|
+isExpired(): boolean
|
|
}
|
|
|
|
class AuthenticationResult {
|
|
-success: boolean
|
|
-accessToken: String
|
|
-refreshToken: String
|
|
-userInfo: UserInfoDetail
|
|
-errorMessage: String
|
|
|
|
+isSuccess(): boolean
|
|
+getAccessToken(): String
|
|
+getRefreshToken(): String
|
|
+getUserInfo(): UserInfoDetail
|
|
+getErrorMessage(): String
|
|
}
|
|
|
|
class DecodedToken {
|
|
-userId: String
|
|
-permissions: List<String>
|
|
-expiresAt: LocalDateTime
|
|
-issuedAt: LocalDateTime
|
|
|
|
+getUserId(): String
|
|
+getPermissions(): List<String>
|
|
+getExpiresAt(): LocalDateTime
|
|
+getIssuedAt(): LocalDateTime
|
|
+isExpired(): boolean
|
|
}
|
|
|
|
class PermissionResult {
|
|
-granted: boolean
|
|
-serviceType: String
|
|
-reason: String
|
|
-permissionDetails: Permission
|
|
|
|
+isGranted(): boolean
|
|
+getServiceType(): String
|
|
+getReason(): String
|
|
+getPermissionDetails(): Permission
|
|
}
|
|
|
|
class TokenRefreshResult {
|
|
-newAccessToken: String
|
|
-expiresIn: int
|
|
|
|
+getNewAccessToken(): String
|
|
+getExpiresIn(): int
|
|
}
|
|
|
|
class UserInfoDetail {
|
|
-userId: String
|
|
-userName: String
|
|
-phoneNumber: String
|
|
-email: String
|
|
-status: UserStatus
|
|
-lastLoginAt: LocalDateTime
|
|
-permissions: List<String>
|
|
|
|
+getUserId(): String
|
|
+getUserName(): String
|
|
+getPhoneNumber(): String
|
|
+getEmail(): String
|
|
+getStatus(): UserStatus
|
|
+getLastLoginAt(): LocalDateTime
|
|
+getPermissions(): List<String>
|
|
}
|
|
}
|
|
|
|
package "repository" {
|
|
interface UserRepository {
|
|
+findUserById(userId: String): Optional<User>
|
|
+save(user: User): User
|
|
+incrementLoginAttempt(userId: String): void
|
|
+resetLoginAttempt(userId: String): void
|
|
+lockAccount(userId: String, duration: Duration): void
|
|
+updateLastLoginAt(userId: String, loginTime: LocalDateTime): void
|
|
}
|
|
|
|
interface UserPermissionRepository {
|
|
+findPermissionsByUserId(userId: String): List<UserPermission>
|
|
+save(userPermission: UserPermission): UserPermission
|
|
+deleteByUserId(userId: String): void
|
|
}
|
|
|
|
interface LoginHistoryRepository {
|
|
+save(loginHistory: LoginHistory): LoginHistory
|
|
+findByUserIdOrderByLoginTimeDesc(userId: String, pageable: Pageable): List<LoginHistory>
|
|
}
|
|
|
|
package "entity" {
|
|
class UserEntity {
|
|
-id: Long
|
|
-userId: String
|
|
-userName: String
|
|
-phoneNumber: String
|
|
-email: String
|
|
-passwordHash: String
|
|
-salt: String
|
|
-status: String
|
|
-loginAttemptCount: int
|
|
-lockedUntil: LocalDateTime
|
|
-lastLoginAt: LocalDateTime
|
|
-createdAt: LocalDateTime
|
|
-updatedAt: LocalDateTime
|
|
|
|
+getId(): Long
|
|
+getUserId(): String
|
|
+getUserName(): String
|
|
+getPhoneNumber(): String
|
|
+getEmail(): String
|
|
+getPasswordHash(): String
|
|
+getSalt(): String
|
|
+getStatus(): String
|
|
+getLoginAttemptCount(): int
|
|
+getLockedUntil(): LocalDateTime
|
|
+getLastLoginAt(): LocalDateTime
|
|
+getCreatedAt(): LocalDateTime
|
|
+getUpdatedAt(): LocalDateTime
|
|
+toDomain(): User
|
|
}
|
|
|
|
class UserPermissionEntity {
|
|
-id: Long
|
|
-userId: String
|
|
-permissionCode: String
|
|
-status: String
|
|
-createdAt: LocalDateTime
|
|
-updatedAt: LocalDateTime
|
|
|
|
+getId(): Long
|
|
+getUserId(): String
|
|
+getPermissionCode(): String
|
|
+getStatus(): String
|
|
+getCreatedAt(): LocalDateTime
|
|
+getUpdatedAt(): LocalDateTime
|
|
+toDomain(): UserPermission
|
|
}
|
|
|
|
class LoginHistoryEntity {
|
|
-id: Long
|
|
-userId: String
|
|
-loginTime: LocalDateTime
|
|
-ipAddress: String
|
|
-userAgent: String
|
|
-success: boolean
|
|
-failureReason: String
|
|
-createdAt: LocalDateTime
|
|
|
|
+getId(): Long
|
|
+getUserId(): String
|
|
+getLoginTime(): LocalDateTime
|
|
+getIpAddress(): String
|
|
+getUserAgent(): String
|
|
+isSuccess(): boolean
|
|
+getFailureReason(): String
|
|
+getCreatedAt(): LocalDateTime
|
|
+toDomain(): LoginHistory
|
|
}
|
|
}
|
|
|
|
package "jpa" {
|
|
interface UserJpaRepository {
|
|
+findByUserId(userId: String): Optional<UserEntity>
|
|
+save(userEntity: UserEntity): UserEntity
|
|
+existsByUserId(userId: String): boolean
|
|
}
|
|
|
|
interface UserPermissionJpaRepository {
|
|
+findByUserIdAndStatus(userId: String, status: String): List<UserPermissionEntity>
|
|
+save(userPermissionEntity: UserPermissionEntity): UserPermissionEntity
|
|
+deleteByUserId(userId: String): void
|
|
}
|
|
|
|
interface LoginHistoryJpaRepository {
|
|
+save(loginHistoryEntity: LoginHistoryEntity): LoginHistoryEntity
|
|
+findByUserIdOrderByLoginTimeDesc(userId: String, pageable: Pageable): List<LoginHistoryEntity>
|
|
}
|
|
}
|
|
}
|
|
|
|
package "config" {
|
|
class SecurityConfig {
|
|
-jwtSecret: String
|
|
-accessTokenExpiry: int
|
|
-refreshTokenExpiry: int
|
|
|
|
+passwordEncoder(): PasswordEncoder
|
|
+corsConfigurationSource(): CorsConfigurationSource
|
|
+filterChain(http: HttpSecurity): SecurityFilterChain
|
|
+authenticationManager(): AuthenticationManager
|
|
}
|
|
|
|
class JwtConfig {
|
|
-secret: String
|
|
-accessTokenExpiry: int
|
|
-refreshTokenExpiry: int
|
|
|
|
+getSecret(): String
|
|
+getAccessTokenExpiry(): int
|
|
+getRefreshTokenExpiry(): int
|
|
+jwtEncoder(): JwtEncoder
|
|
+jwtDecoder(): JwtDecoder
|
|
}
|
|
|
|
class RedisConfig {
|
|
-host: String
|
|
-port: int
|
|
-password: String
|
|
-database: int
|
|
|
|
+redisConnectionFactory(): RedisConnectionFactory
|
|
+redisTemplate(): RedisTemplate<String, Object>
|
|
+cacheManager(): RedisCacheManager
|
|
+sessionRedisTemplate(): RedisTemplate<String, UserSession>
|
|
}
|
|
}
|
|
}
|
|
|
|
' Common Base Classes 사용
|
|
package "Common Module" <<External>> {
|
|
class ApiResponse<T>
|
|
class ErrorResponse
|
|
abstract class BaseTimeEntity
|
|
enum ErrorCode
|
|
class BusinessException
|
|
}
|
|
|
|
' 관계 정의
|
|
AuthController --> AuthService : uses
|
|
AuthController --> TokenService : uses
|
|
AuthController ..> LoginRequest : uses
|
|
AuthController ..> LoginResponse : creates
|
|
AuthController ..> UserInfoResponse : creates
|
|
AuthController ..> PermissionCheckResponse : creates
|
|
|
|
AuthServiceImpl --> UserRepository : uses
|
|
AuthServiceImpl --> TokenService : uses
|
|
AuthServiceImpl --> PermissionService : uses
|
|
AuthServiceImpl --> LoginHistoryRepository : uses
|
|
|
|
TokenServiceImpl ..> DecodedToken : creates
|
|
TokenServiceImpl ..> AuthenticationResult : creates
|
|
|
|
PermissionServiceImpl --> UserPermissionRepository : uses
|
|
|
|
UserRepository --> UserEntity : works with
|
|
UserPermissionRepository --> UserPermissionEntity : works with
|
|
LoginHistoryRepository --> LoginHistoryEntity : works with
|
|
|
|
UserRepository --> User : returns
|
|
UserPermissionRepository --> UserPermission : returns
|
|
LoginHistoryRepository --> LoginHistory : returns
|
|
|
|
UserEntity ..> User : converts to
|
|
UserPermissionEntity ..> UserPermission : converts to
|
|
LoginHistoryEntity ..> LoginHistory : converts to
|
|
|
|
UserJpaRepository --> UserEntity : manages
|
|
UserPermissionJpaRepository --> UserPermissionEntity : manages
|
|
LoginHistoryJpaRepository --> LoginHistoryEntity : manages
|
|
|
|
User --> UserStatus : has
|
|
UserSession --> UserInfoDetail : contains
|
|
|
|
AuthServiceImpl ..> AuthenticationResult : creates
|
|
AuthServiceImpl ..> UserInfoDetail : creates
|
|
PermissionServiceImpl ..> PermissionResult : creates
|
|
|
|
' Inheritance
|
|
UserEntity --|> BaseTimeEntity
|
|
UserPermissionEntity --|> BaseTimeEntity
|
|
LoginHistoryEntity --|> BaseTimeEntity
|
|
|
|
AuthService <|-- AuthServiceImpl : implements
|
|
TokenService <|-- TokenServiceImpl : implements
|
|
PermissionService <|-- PermissionServiceImpl : implements
|
|
|
|
UserRepository <|-- UserJpaRepository : implements
|
|
UserPermissionRepository <|-- UserPermissionJpaRepository : implements
|
|
LoginHistoryRepository <|-- LoginHistoryJpaRepository : implements
|
|
|
|
' Notes
|
|
note top of AuthController : "REST API 엔드포인트 제공\n- 로그인/로그아웃\n- 토큰 검증/갱신\n- 권한 확인"
|
|
note top of AuthServiceImpl : "인증/인가 비즈니스 로직\n- 사용자 인증 처리\n- 세션 관리\n- 권한 검증"
|
|
note top of TokenServiceImpl : "JWT 토큰 관리\n- 토큰 생성/검증\n- 페이로드 추출"
|
|
note top of UserEntity : "사용자 정보 저장\n- 로그인 시도 횟수 관리\n- 계정 잠금 처리"
|
|
note top of RedisConfig : "Redis 캐시 설정\n- 세션 캐싱\n- 권한 캐싱"
|
|
|
|
@enduml |