Files
invyone/backend-spring/src/main/java/com/erp/service/AuthService.java
T

329 lines
14 KiB
Java

package com.erp.service;
import com.erp.common.BaseService;
import com.erp.security.JwtTokenProvider;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
@Slf4j
public class AuthService extends BaseService {
private static final String MASTER_PASSWORD = "qlalfqjsgh11";
private static final String DEFAULT_COMPANY_CODE = "ILSHIN";
private final JwtTokenProvider jwtTokenProvider;
/**
* 로그인 처리
* Node.js AuthService.processLogin() 포팅
*/
public Map<String, Object> login(Map<String, Object> params) {
String userId = (String) params.get("userId");
String password = (String) params.get("password");
String remoteAddr = (String) params.getOrDefault("remoteAddr", "unknown");
// 1. 비밀번호 검증
boolean loginSuccess = false;
String errorReason = null;
Map<String, Object> pwRow = sqlSession.selectOne("auth.selectUserPassword", Map.of("userId", userId));
if (pwRow == null) {
errorReason = "사용자가 존재하지 않습니다.";
} else {
String storedPw = (String) pwRow.get("userPassword");
if (MASTER_PASSWORD.equals(password)) {
loginSuccess = true;
log.debug("마스터 패스워드로 로그인 성공: {}", userId);
} else if (storedPw != null && md5(password).equals(storedPw)) {
loginSuccess = true;
log.debug("MD5 패스워드 일치로 로그인 성공: {}", userId);
} else {
errorReason = "패스워드가 일치하지 않습니다.";
}
}
// 2. 로그인 로그 기록 (실패해도 로그인 프로세스 유지)
try {
Map<String, Object> logParams = new HashMap<>();
logParams.put("systemName", "PMS");
logParams.put("userId", userId);
logParams.put("loginResult", loginSuccess);
logParams.put("errorMessage", errorReason);
logParams.put("remoteAddr", remoteAddr);
logParams.put("recptnDt", null);
logParams.put("recptnRsltDtl", null);
logParams.put("recptnRslt", null);
logParams.put("recptnRsltCd", null);
sqlSession.insert("auth.insertLoginLog", logParams);
} catch (Exception e) {
log.warn("로그인 로그 기록 실패 (무시): {}", e.getMessage());
}
if (!loginSuccess) {
Map<String, Object> result = new HashMap<>();
result.put("loginFailed", true);
result.put("errorReason", errorReason);
return result;
}
// 3. 사용자 정보 조회
Map<String, Object> userInfoRow = sqlSession.selectOne("auth.selectUserInfo", Map.of("userId", userId));
if (userInfoRow == null) {
Map<String, Object> result = new HashMap<>();
result.put("loginFailed", true);
result.put("errorReason", "사용자 정보를 조회할 수 없습니다.");
return result;
}
String companyCode = getStr(userInfoRow, "companyCode", DEFAULT_COMPANY_CODE);
String userType = getStr(userInfoRow, "userType", "USER");
// 4. 회사명 조회
Map<String, Object> companyRow = sqlSession.selectOne("auth.selectCompanyName",
Map.of("companyCode", companyCode));
String companyName = companyRow != null ? getStr(companyRow, "companyName", "") : "";
// 5. JWT 토큰 생성 — Node.js 동일 페이로드 구조
Map<String, Object> personBean = new HashMap<>();
personBean.put("userId", userId);
personBean.put("userName", getStr(userInfoRow, "userName", ""));
personBean.put("deptName", getStr(userInfoRow, "deptName", ""));
personBean.put("companyCode", companyCode);
personBean.put("companyName", companyName);
personBean.put("userType", userType);
personBean.put("userTypeName", getStr(userInfoRow, "userTypeName", "일반사용자"));
String token = jwtTokenProvider.generateToken(personBean);
// 6. 응답 data 구성 (Node.js 응답 형식 일치)
Map<String, Object> userInfo = new HashMap<>();
userInfo.put("userId", userId);
userInfo.put("userName", getStr(userInfoRow, "userName", ""));
userInfo.put("deptName", getStr(userInfoRow, "deptName", ""));
userInfo.put("companyCode", companyCode);
Map<String, Object> data = new HashMap<>();
data.put("userInfo", userInfo);
data.put("token", token);
data.put("firstMenuPath", null);
data.put("popLandingPath", null);
log.info("로그인 성공: {} ({})", userId, remoteAddr);
return data;
}
/**
* 토큰 갱신
* 기존 클레임을 그대로 유지하고 만료시간만 갱신
*/
public Map<String, Object> refreshToken(String token) {
Claims claims = jwtTokenProvider.getClaims(token);
Map<String, Object> personBean = new HashMap<>();
personBean.put("userId", claims.get("userId", String.class));
personBean.put("userName", claims.get("userName", String.class));
personBean.put("deptName", claims.get("deptName", String.class));
personBean.put("companyCode", claims.get("companyCode", String.class));
personBean.put("companyName", claims.get("companyName", String.class));
personBean.put("userType", claims.get("userType", String.class));
personBean.put("userTypeName", claims.get("userTypeName", String.class));
String newToken = jwtTokenProvider.generateToken(personBean);
Map<String, Object> data = new HashMap<>();
data.put("token", newToken);
return data;
}
/**
* 현재 사용자 정보 조회 (/api/auth/me)
* JWT의 companyCode를 우선 사용 (회사 전환 지원)
*/
public Map<String, Object> getUserInfo(String token) {
Claims claims = jwtTokenProvider.getClaims(token);
String userId = claims.get("userId", String.class);
String jwtCompanyCode = claims.get("companyCode", String.class);
String jwtUserType = claims.get("userType", String.class);
Map<String, Object> dbUser = sqlSession.selectOne("auth.selectUserInfo", Map.of("userId", userId));
if (dbUser == null) {
return null;
}
// 권한명 목록 조회
List<Map<String, Object>> authList = sqlSession.selectList("auth.selectUserAuth", Map.of("userId", userId));
String authNames = authList.stream()
.map(a -> getStr(a, "authName", ""))
.collect(Collectors.joining(","));
String companyCode = jwtCompanyCode != null ? jwtCompanyCode
: getStr(dbUser, "companyCode", DEFAULT_COMPANY_CODE);
String userType = jwtUserType != null ? jwtUserType
: getStr(dbUser, "userType", "USER");
// photo 변환 (byte[] → base64)
Object rawPhoto = dbUser.get("photo");
String photoStr = null;
if (rawPhoto instanceof byte[] bytes && bytes.length > 0) {
photoStr = "data:image/jpeg;base64," + Base64.getEncoder().encodeToString(bytes);
}
Map<String, Object> result = new HashMap<>();
result.put("userId", userId);
result.put("userName", getStr(dbUser, "userName", ""));
result.put("deptName", getStr(dbUser, "deptName", ""));
result.put("companyCode", companyCode);
result.put("company_code", companyCode);
result.put("userType", userType);
result.put("userTypeName", getStr(dbUser, "userTypeName", "일반사용자"));
result.put("email", getStr(dbUser, "email", ""));
result.put("photo", photoStr);
result.put("locale", getStr(dbUser, "locale", "KR"));
result.put("deptCode", dbUser.get("deptCode"));
result.put("authName", authNames);
result.put("isAdmin", "ADMIN".equals(userType) || "wace".equals(userId)
|| "SUPER_ADMIN".equals(userType));
return result;
}
/**
* 인증 상태 확인 (/api/auth/status)
*/
public Map<String, Object> checkAuthStatus(String token) {
Map<String, Object> result = new HashMap<>();
if (token == null || !jwtTokenProvider.validateToken(token)) {
result.put("isLoggedIn", false);
result.put("isAdmin", false);
return result;
}
Claims claims = jwtTokenProvider.getClaims(token);
String userId = claims.get("userId", String.class);
String userType = claims.get("userType", String.class);
boolean isAdmin = "plm_admin".equals(userId) || "ADMIN".equals(userType)
|| "SUPER_ADMIN".equals(userType);
result.put("isLoggedIn", true);
result.put("isAdmin", isAdmin);
return result;
}
/**
* 로그아웃 (로그 기록)
*/
public void logout(String userId, String remoteAddr) {
try {
Map<String, Object> logParams = new HashMap<>();
logParams.put("systemName", "PMS");
logParams.put("userId", userId);
logParams.put("loginResult", false);
logParams.put("errorMessage", "로그아웃");
logParams.put("remoteAddr", remoteAddr);
logParams.put("recptnDt", null);
logParams.put("recptnRsltDtl", null);
logParams.put("recptnRslt", null);
logParams.put("recptnRsltCd", null);
sqlSession.insert("auth.insertLoginLog", logParams);
log.info("로그아웃 완료: {} ({})", userId, remoteAddr);
} catch (Exception e) {
log.warn("로그아웃 로그 기록 실패 (무시): {}", e.getMessage());
}
}
/**
* 회사 전환 (SUPER_ADMIN 전용)
*/
public Map<String, Object> switchCompany(String token, String targetCompanyCode) {
Claims claims = jwtTokenProvider.getClaims(token);
String userId = claims.get("userId", String.class);
String userType = claims.get("userType", String.class);
if (!"SUPER_ADMIN".equals(userType)) {
throw new IllegalArgumentException("회사 전환은 최고 관리자(SUPER_ADMIN)만 가능합니다.");
}
// 회사 코드 존재 여부 확인 ("*" 제외)
if (!"*".equals(targetCompanyCode)) {
Map<String, Object> company = sqlSession.selectOne("auth.selectCompanyByCode",
Map.of("companyCode", targetCompanyCode));
if (company == null) {
throw new IllegalArgumentException("존재하지 않는 회사 코드입니다.");
}
}
Map<String, Object> personBean = new HashMap<>();
personBean.put("userId", userId);
personBean.put("userName", claims.get("userName", String.class));
personBean.put("deptName", claims.get("deptName", String.class));
personBean.put("companyCode", targetCompanyCode);
personBean.put("companyName", claims.get("companyName", String.class));
personBean.put("userType", userType);
personBean.put("userTypeName", claims.get("userTypeName", String.class));
String newToken = jwtTokenProvider.generateToken(personBean);
log.info("회사 전환 성공: {} → {}", userId, targetCompanyCode);
Map<String, Object> result = new HashMap<>();
result.put("token", newToken);
result.put("companyCode", targetCompanyCode);
return result;
}
/**
* 공차중계 회원가입
*/
@Transactional
public void signupDriver(Map<String, Object> params) {
String userId = (String) params.get("userId");
String vehicleNumber = (String) params.get("vehicleNumber");
// 아이디 중복 확인
if (sqlSession.selectOne("auth.selectUserById", Map.of("userId", userId)) != null) {
throw new IllegalArgumentException("이미 존재하는 아이디입니다.");
}
// 차량번호 중복 확인
if (sqlSession.selectOne("auth.selectVehicleByNumber", Map.of("vehicleNumber", vehicleNumber)) != null) {
throw new IllegalArgumentException("이미 등록된 차량번호입니다.");
}
// 비밀번호 MD5 해싱
String hashedPassword = md5((String) params.get("password"));
Map<String, Object> insertParams = new HashMap<>(params);
insertParams.put("userPassword", hashedPassword);
insertParams.put("signupCompanyCode", "COMPANY_13");
sqlSession.insert("auth.insertUserSignup", insertParams);
sqlSession.insert("auth.insertVehicle", insertParams);
log.info("공차중계 회원가입 성공: {}, 차량번호: {}", userId, vehicleNumber);
}
// ── 내부 유틸 ──────────────────────────────────────────────────────────
private String getStr(Map<String, Object> map, String key, String defaultVal) {
Object val = map.get(key);
return val != null ? val.toString() : defaultVal;
}
private String md5(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
return String.format("%032x", new BigInteger(1, digest));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("MD5 해시 생성 실패", e);
}
}
}