package com.goi.erp.auth; import com.goi.erp.common.exception.InvalidPasswordException; import com.goi.erp.common.exception.UserNotFoundException; import com.goi.erp.config.JwtService; import com.goi.erp.config.SecuritySystemClientsProperties; import com.goi.erp.employee.Employee; import com.goi.erp.employee.EmployeeRepository; import com.goi.erp.employee.EmployeeRole; import com.goi.erp.employee.EmployeeRoleRepository; import com.goi.erp.role.RolePermissionRepository; import com.goi.erp.token.Token; import com.goi.erp.token.TokenRepository; import com.goi.erp.token.TokenType; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.stream.Collectors; @Service @Transactional @RequiredArgsConstructor public class AuthenticationService { private final EmployeeRepository employeeRepository; private final EmployeeRoleRepository employeeRoleRepository; private final RolePermissionRepository rolePermissionRepository; private final TokenRepository tokenRepository; private final PasswordEncoder passwordEncoder; private final JwtService jwtService; private final SecuritySystemClientsProperties systemClientsProperties; private final AuthCookieService authCookieService; private final UserDetailsService userDetailsService; // ===================== 로그인 ===================== public AuthenticationResponse authenticate( AuthenticationRequest request, HttpServletResponse response ) { Employee employee = employeeRepository.findByEmpLoginId(request.getEmpLoginId()) .orElseThrow(() -> new UserNotFoundException("Employee not found")); if (!passwordEncoder.matches(request.getEmpLoginPassword(), employee.getEmpLoginPassword())) { throw new InvalidPasswordException("Invalid password"); } List activeRoles = employeeRoleRepository.findActiveRolesByEmployeeId(employee.getEmpId()); List roles = extractRoles(activeRoles); List permissions = extractPermissions(activeRoles); String accessToken = jwtService.generateToken(employee, roles, permissions); String refreshToken = jwtService.generateRefreshToken(employee, roles, permissions); revokeAllEmployeeTokens(employee); // refresh 도 저장 saveEmployeeToken(employee, accessToken, TokenType.ACCESS); saveEmployeeToken(employee, refreshToken, TokenType.REFRESH); // Cookie로 내려줌 authCookieService.addAuthCookie(response, accessToken); authCookieService.addRefreshCookie(response, refreshToken); return AuthenticationResponse.builder() .authenticated(true) .loginId(employee.getEmpLoginId()) .firstName(employee.getEmpFirstName()) .lastName(employee.getEmpLastName()) .roles(roles) .build(); } // ===================== Refresh Token ===================== public void refreshToken(HttpServletRequest request, HttpServletResponse response) { String refreshToken = authCookieService .extractRefreshToken(request) .orElse(null); if (refreshToken == null) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } String loginId = jwtService.extractUsername(refreshToken); if (loginId == null) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } UserDetails userDetails = userDetailsService.loadUserByUsername(loginId); if (!jwtService.isTokenValid(refreshToken, userDetails)) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } Employee employee = employeeRepository.findByEmpLoginId(loginId) .orElseThrow(() -> new UserNotFoundException("Employee not found")); List activeRoles = employeeRoleRepository.findActiveRolesByEmployeeId(employee.getEmpId()); List roles = extractRoles(activeRoles); List permissions = extractPermissions(activeRoles); String newAccessToken = jwtService.generateToken(employee, roles, permissions); String newRefreshToken = jwtService.generateRefreshToken(employee, roles, permissions); // 기존 토큰 전부 revoke revokeAllEmployeeTokens(employee); // 새 토큰 저장 saveEmployeeToken(employee, newAccessToken, TokenType.ACCESS); saveEmployeeToken(employee, newRefreshToken, TokenType.REFRESH); // 쿠키 갱신 authCookieService.addAuthCookie(response, newAccessToken); authCookieService.addRefreshCookie(response, newRefreshToken); // body 없음 response.setStatus(HttpServletResponse.SC_NO_CONTENT); } // ===================== System Token ===================== // OPR / CRM 등 서버간 호출용 (Header 기반) public SystemAuthenticationResponseDto authenticateSystem(SystemAuthenticationRequestDto request) { if (request.getClientId() == null || request.getClientSecret() == null) { throw new InvalidPasswordException("Missing client credentials"); } var clientConfig = systemClientsProperties.getClients().get(request.getClientId()); if (clientConfig == null) { throw new InvalidPasswordException("Invalid system client"); } if (!clientConfig.getSecret().equals(request.getClientSecret())) { throw new InvalidPasswordException("Invalid system secret"); } String jwtToken = jwtService.generateSystemToken( request.getClientId(), clientConfig.getPermissions() ); return SystemAuthenticationResponseDto.builder() .accessToken(jwtToken) .refreshToken(null) .build(); } // ===================== 내부 헬퍼 ===================== private List extractRoles(List roles) { return roles.stream() .map(er -> er.getRoleInfo().getRoleName()) .collect(Collectors.toList()); } private List extractPermissions(List roles) { return roles.stream() .flatMap(er -> rolePermissionRepository .findByRoleId(er.getRoleInfo().getRoleId()).stream()) .map(rp -> rp.getPermissionInfo().getPermModule() + ":" + rp.getPermissionInfo().getPermAction() + ":" + rp.getPermissionInfo().getPermScope()) .distinct() .collect(Collectors.toList()); } private void saveEmployeeToken(Employee employee, String jwtToken, TokenType tokenType) { Token token = Token.builder() .employee(employee) .token(jwtToken) .tokenType(tokenType) .expired(false) .revoked(false) .build(); tokenRepository.save(token); } private void revokeAllEmployeeTokens(Employee employee) { tokenRepository.revokeAllValidTokensByEmployee(employee.getEmpId()); } }