
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
package com.takensoft.common.util;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.takensoft.cms.loginPolicy.service.LoginModeService;
import com.takensoft.cms.loginPolicy.service.LoginPolicyService;
import com.takensoft.cms.mber.service.LgnHstryService;
import com.takensoft.cms.mber.vo.LgnHstryVO;
import com.takensoft.cms.mber.vo.MberVO;
import com.takensoft.cms.token.service.RefreshTokenService;
import com.takensoft.cms.token.vo.RefreshTknVO;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @author takensoft
* @since 2025.03.21
* @modification
* since | author | description
* 2025.03.21 | takensoft | 최초 등록
* 2025.05.28 | takensoft | 통합 로그인 적용, 문제 해결
* 2025.05.29 | takensoft | Redis 통합 중복로그인 관리
* 2025.06.04 | takensoft | Redis 트랜잭션 및 타이밍 이슈 해결
*
* 통합 로그인 유틸리티
*/
@Component
@RequiredArgsConstructor
public class LoginUtil {
private final LgnHstryService lgnHstryService;
private final HttpRequestUtil httpRequestUtil;
private final LoginModeService loginModeService;
private final RefreshTokenService refreshTokenService;
private final LoginPolicyService loginPolicyService;
private final JWTUtil jwtUtil;
private final SessionUtil sessionUtil;
private final RedisTemplate<String, String> redisTemplate;
@Value("${jwt.accessTime}")
private long JWT_ACCESSTIME;
@Value("${jwt.refreshTime}")
private long JWT_REFRESHTIME;
@Value("${cookie.time}")
private int COOKIE_TIME;
/**
* 통합 로그인 성공 처리
*/
public void successLogin(MberVO mber, HttpServletRequest req, HttpServletResponse res) {
// 로그인 방식 확인
String loginMode = loginModeService.getLoginMode();
res.setHeader("loginMode", loginMode);
try {
// 로그인 이력 등록
String loginType = (String) req.getAttribute("loginType");
if (!"OAUTH2".equals(loginType)) {
saveLoginHistory(mber, req);
}
if ("S".equals(loginMode)) {
// Redis 기반 중복로그인 관리 적용
handleSessionLogin(mber, req, res);
} else {
// 기존 Redis 기반 관리 유지
handleJwtLogin(mber, req, res);
}
}
catch (IOException ioe) {
throw new RuntimeException(ioe);
}
catch (Exception e) {
throw e;
}
}
/**
* 세션 모드 로그인 처리 - Redis 트랜잭션 개선
*/
private void handleSessionLogin(MberVO mber, HttpServletRequest req, HttpServletResponse res) throws IOException {
// JWT 토큰은 생성하되 세션에만 저장
String accessToken = jwtUtil.createJwt("Authorization",
mber.getMbrId(), mber.getLgnId(), mber.getMbrNm(),
(List) mber.getAuthorities(), JWT_ACCESSTIME);
// 세션 생성 및 정보 저장
HttpSession session = req.getSession(true);
session.setAttribute("JWT_TOKEN", accessToken);
session.setAttribute("mbrId", mber.getMbrId());
session.setAttribute("mbrNm", mber.getMbrNm());
session.setAttribute("lgnId", mber.getLgnId());
session.setAttribute("roles", mber.getAuthorList());
session.setAttribute("loginType", req.getAttribute("loginType") != null ?
req.getAttribute("loginType") : "S");
// 토큰만 저장
if (!loginPolicyService.getPolicy()) {
String tokenKey = "session_token:" + mber.getMbrId();
// 기존 토큰 삭제 후 새 토큰 저장
redisTemplate.delete(tokenKey);
redisTemplate.opsForValue().set(tokenKey, accessToken, Duration.ofSeconds(session.getMaxInactiveInterval()));
}
// OAuth2가 아닌 경우 JSON 응답 전송
String loginType = (String) req.getAttribute("loginType");
if (!"OAUTH2".equals(loginType)) {
try {
Map<String, Object> result = new HashMap<>();
result.put("mbrId", mber.getMbrId());
result.put("mbrNm", mber.getMbrNm());
result.put("roles", mber.getAuthorList());
res.setContentType("application/json;charset=UTF-8");
res.setCharacterEncoding("UTF-8");
res.setStatus(HttpStatus.OK.value());
String jsonResponse = new ObjectMapper().writeValueAsString(result);
res.getWriter().write(jsonResponse);
res.getWriter().flush();
} catch (Exception e) {
throw e;
}
}
}
/**
* JWT 모드 로그인 처리
*/
private void handleJwtLogin(MberVO mber, HttpServletRequest req, HttpServletResponse res) throws IOException {
// JWT 토큰 생성
String accessToken = jwtUtil.createJwt("Authorization", mber.getMbrId(), mber.getLgnId(), mber.getMbrNm(), (List) mber.getAuthorities(), JWT_ACCESSTIME);
String refreshToken = jwtUtil.createJwt("refresh", mber.getMbrId(), mber.getLgnId(), mber.getMbrNm(), (List) mber.getAuthorities(), JWT_REFRESHTIME);
// Refresh 토큰 처리
RefreshTknVO refresh = new RefreshTknVO();
refresh.setMbrId(mber.getMbrId());
// 기존 refresh 토큰 삭제
if (refreshTokenService.findByCheckRefresh(req, refresh)) {
refreshTokenService.delete(req, refresh);
}
refresh.setToken(refreshToken);
// 응답 헤더 및 쿠키 설정
res.setHeader("Authorization", accessToken);
res.addCookie(jwtUtil.createCookie("refresh", refreshToken, COOKIE_TIME));
// 중복 로그인 비허용일 때 Redis 저장
if (!loginPolicyService.getPolicy()) {
redisTemplate.delete("jwt:" + mber.getMbrId());
redisTemplate.opsForValue().set("jwt:" + mber.getMbrId(), accessToken, JWT_ACCESSTIME, TimeUnit.MILLISECONDS);
}
// Refresh 토큰 저장
refreshTokenService.saveRefreshToken(req, res, refresh, JWT_REFRESHTIME);
// OAuth2가 아닌 경우만 상태 코드 설정
String loginType = (String) req.getAttribute("loginType");
if (!"OAUTH2".equals(loginType)) {
res.setStatus(HttpStatus.OK.value());
}
}
/**
* 로그인 이력 저장
*/
private void saveLoginHistory(MberVO mber, HttpServletRequest req) {
try {
String userAgent = httpRequestUtil.getUserAgent(req);
LgnHstryVO lgnHstryVO = new LgnHstryVO();
lgnHstryVO.setLgnId(mber.getLgnId());
lgnHstryVO.setLgnType(mber.getAuthorities().stream()
.anyMatch(role -> role.getAuthority().equals("ROLE_ADMIN")) ? "0" : "1");
lgnHstryVO.setCntnIp(httpRequestUtil.getIp(req));
lgnHstryVO.setCntnOperSys(httpRequestUtil.getOS(userAgent));
lgnHstryVO.setDeviceNm(httpRequestUtil.getDevice(userAgent));
lgnHstryVO.setBrwsrNm(httpRequestUtil.getBrowser(userAgent));
lgnHstryService.LgnHstrySave(lgnHstryVO);
} catch (Exception e) {
}
}
}