329 lines
14 KiB
Java
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);
|
|
}
|
|
}
|
|
}
|