728x90
1. package org.scoula.security.account.domain;

AuthVO
@Data
public class AuthVO implements GrantedAuthority {
private String username;
private String auth;
@Override
public String getAuthority() {
return auth;
}
}
- GrantedAuthority 인터페이스 구현
- getAuthority() 로 권한 정보 추출
CustomUser
@Getter @Setter
public class CustomUser extends User {
//실질적인 사용자 데이터
private MemberVO member;
public CustomUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
}
public CustomUser(MemberVO vo) {
super(vo.getUsername(), vo.getPassword(), vo.getAuthList());
this.member = vo;
}
}
- CustomUser(MemberVO vo)
- MemberVO 를 User로 변환
2. package org.scoula.security.account.dto;

LoginDTO
public class LoginDTO {
public String username;
public String password;
public static LoginDTO of(HttpServletRequest request) {
ObjectMapper om = new ObjectMapper();
try {
return om.readValue(request.getInputStream(), LoginDTO.class);
} catch (Exception e) {
e.printStackTrace();
throw new BadCredentialsException("username or password is incorrect");
}
}
}
- request body에서 username, password 역직렬화(JSON 문자열 → 자바 객체로 변환하는 것)
- username이나 password가 틀리면 여기서 Exception 발생
- JwtUsernamePasswordAuthenticationFilter의 attemptAuthentication에서 사용
3. package org.scoula.security.account.mapper;

UserDetailsMapper
public interface UserDetailsMapper {
public MemberVO get(String username);
}
- tbl_member 테이블과 tbl_member_auth 테이블을 Join 처리
- get()은 CustomUserDetailsService에서 username으로 MemberVo를 조회하는데 사용
MemberVO vo = mapper.get(username);
4. package org.scoula.security.filter;

JwtUsernamePasswordAuthenticationFilter
public class JwtUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public JwtUsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager, LoginSuccessHandler loginSuccessHandler, LoginFailureHandler loginFailureHandler) {
super(authenticationManager);
setFilterProcessesUrl("/api/auth/login");
setAuthenticationSuccessHandler(loginSuccessHandler);
setAuthenticationFailureHandler(loginFailureHandler);
}
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
LoginDTO login = LoginDTO.of(request);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(login.getUsername(), login.getPassword());
return getAuthenticationManager().authenticate(authenticationToken);
}
}
- 로그인 요청 경로 지정 : /api/auth/login
- 로그인 성공 처리기, 로그인 실패 처리기 등록
- attemptAuthentication() 메서드
- 등록한 인증 url로 요청이 오면 호출
- 요청에서 username, password 부분을 추출
- UsernamePasswordAuthenticationToken 구성
- AuthenticationManager에게 인증 요청
- 인증경과 Authentication을 리턴

5. package org.scoula.security.handler, service, util;

🎁 전체 코드 => 깃허브 업로드 예정
🎁 패키지 전체 요약

로그인 처리 전체 흐름
🐻❄️ 프론트엔드
stores/auth.js 에서 백엔드 서버 '/api/auth/login’ 경로로 POST 요청 전송
const login = async (member) => {
const { data } = await axios.post('/api/auth/login', member);
state.value = { ...data };
localStorage.setItem('auth', JSON.stringify(state.value));
};
🐻 백엔드
1️⃣ AuthenticationErrorFilter
필터 체인 초입에 위치 / 토큰이 만료되었거나 유효하지 않은 경우 예외 발생, 예외 응답 반환
2️⃣ JwtAuthenticationFilter
요청 헤더에 토큰이 존재하는 지 검사 / 토큰에서 username 추출
3️⃣ JwtUsernamePasswordAuthenticationFilter
약속된 login url 요청 절차 수행
- 요청이 JwtUsernamePasswordAuthenticationFilter.attemptAuthentication()로 전달됨.
- LoginDTO.of(request)를 통해 요청의 username, password를 꺼냄.
- 이 정보로 UsernamePasswordAuthenticationToken을 생성하여 AuthenticationManager에게 전달 → 로그인 인증 시도.
- 인증 성공 → LoginSuccessHandler 동작
- 인증 실패 → LoginFailureHandler 동작
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
// 요청 BODY의 JSON에서 username, password LoginDTO
LoginDTO login = LoginDTO.of(request);
// 인증 토큰(UsernamePasswordAuthenticationToken) 구성
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(login.getUsername(), login.getPassword());
// AuthenticationManager에게 인증 요청
return getAuthenticationManager().authenticate(authenticationToken);
}
4️⃣-1) LoginSuccessHandler
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
private final JwtProcessor jwtProcessor;
private AuthResultDTO makeAuthResult(CustomUser user) {
String username = user.getUsername();
// 토큰 생성
String token = jwtProcessor.generateToken(username);
// 토큰 + 사용자 기본 정보 (사용자명, ...)를 묶어서 AuthResultDTO 구성
return new AuthResultDTO(token, UserInfoDTO.of(user.getMember()));
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
// 인증 결과 Principal
CustomUser user = (CustomUser) authentication.getPrincipal();
// 인증 성공 결과를 JSON으로 직접 응답
AuthResultDTO result = makeAuthResult(user);
JsonResponse.send(response, result);
}
}
public static <T> void send(HttpServletResponse response, T result) throws IOException {
ObjectMapper om = new ObjectMapper();
response.setContentType("application/json;charset=UTF-8");
Writer out = response.getWriter();
out.write(om.writeValueAsString(result));
out.flush();
}
4️⃣-2) LoginFailureHandler
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
JsonResponse.sendError(response, HttpStatus.UNAUTHORIZED, "사용자 ID 또는 비밀번호가 일치하지 않습니다.");
}
public static void sendError(HttpServletResponse response, HttpStatus status, String message) throws IOException {
response.setStatus(status.value());
response.setContentType("application/json;charset=UTF-8");
Writer out = response.getWriter();
out.write(message);
out.flush();
}
728x90
'KB 국민은행 IT's your life 교육' 카테고리의 다른 글
| [KB IT's YOUR LIFE] 우수수료생의 6기 합격 후기(면접 질문 포함) (1) | 2026.01.03 |
|---|---|
| [KB IT's YOUR LIFE] 6기 우수수료생의 KB국민은행 부트캠프 후기 (5) | 2025.12.30 |
| Spring Security 인증 & JWT 개념 정리 (0) | 2025.07.01 |
| [DB] RDBMS vs NoSQL , 각각 활용 사례 정리 (0) | 2025.05.26 |
| [Java] 객체지향 설계 원칙 SOLID / 제어의 역전(IoC), 의존성 주입(DI) (0) | 2025.05.05 |