205 lines
7.6 KiB
Java
205 lines
7.6 KiB
Java
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<EmployeeRole> activeRoles =
|
|
employeeRoleRepository.findActiveRolesByEmployeeId(employee.getEmpId());
|
|
|
|
List<String> roles = extractRoles(activeRoles);
|
|
List<String> 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<EmployeeRole> activeRoles =
|
|
employeeRoleRepository.findActiveRolesByEmployeeId(employee.getEmpId());
|
|
|
|
List<String> roles = extractRoles(activeRoles);
|
|
List<String> 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<String> extractRoles(List<EmployeeRole> roles) {
|
|
return roles.stream()
|
|
.map(er -> er.getRoleInfo().getRoleName())
|
|
.collect(Collectors.toList());
|
|
}
|
|
|
|
private List<String> extractPermissions(List<EmployeeRole> 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());
|
|
}
|
|
}
|