hmmmmmmmmmmmm 2024. 8. 13. 21:05

 

 

AuthController.java

package net.javaguides.ems.controller;

import lombok.AllArgsConstructor;
import net.javaguides.ems.dto.JwtAuthResponse;
import net.javaguides.ems.dto.LoginDto;
import net.javaguides.ems.service.AuthService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@CrossOrigin("*")
@AllArgsConstructor
@RestController
@RequestMapping("/api/auth")
public class AuthController {

    private AuthService authService;

    // 로그인 API
    @PostMapping("/login")
    public ResponseEntity<JwtAuthResponse> login(@RequestBody LoginDto loginDto) {

        JwtAuthResponse jwtAuthResponse = authService.login(loginDto);

        return new ResponseEntity<>(jwtAuthResponse, HttpStatus.OK);
    }
}

 

로그인 인증 처리

 

 

AuthService.java

package net.javaguides.ems.service;

import net.javaguides.ems.dto.JwtAuthResponse;
import net.javaguides.ems.dto.LoginDto;

public interface AuthService {
    JwtAuthResponse login(LoginDto loginDto);
}

 

로그인 메소드 정의

 

 

AuthServiceImpl.java

package net.javaguides.ems.service.impl;

import lombok.AllArgsConstructor;
import net.javaguides.ems.dto.JwtAuthResponse;
import net.javaguides.ems.dto.LoginDto;
import net.javaguides.ems.entity.User;
import net.javaguides.ems.repository.UserRepository;
import net.javaguides.ems.security.JwtTokenProvider;
import net.javaguides.ems.service.AuthService;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
@AllArgsConstructor
@Override
public JwtAuthResponse login(LoginDto loginDto) {

    // 1. 사용자 인증
    // authenticationManager로 사용자 인증 처리 (사용자이름이나 이메일, 비밀번호로 인증 시도)
    // 인증 성공 시 authentication 변수에 사용자 정보 할당
    Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
            loginDto.getUsernameOrEmail(),
            loginDto.getPassword()
    ));

    // 2. 인증된 사용자 정보를 세션에 저장
    // 인증된 사용자 정보를 SecurityContextHolder에 할당해 세션 유지
    SecurityContextHolder.getContext().setAuthentication(authentication);

    // 3. jwt 토큰 생성
    // 인증된 사용자 정보로 jwt 토큰 생성
    String token = jwtTokenProvider.generateToken(authentication);

    // 4. 사용자 정보 조회/역할 확인
    // Optional<User>: 존재하지 않을 수도 있는 데이터 안전 처리
    // userOptional: id, name, username, email, password, role
    // 같은 파라미터(loginDto.getUsernameOrEmail()) 2번 전달하는 이유: 메소드가 username이든 email이든 상관없이 사용자 정보를 찾도록
    // 사용자 이름이나 이메일로 사용자 조회 (role 추가 목적)
    Optional<User> userOptional = userRepository.findByUsernameOrEmail(loginDto.getUsernameOrEmail(), loginDto.getUsernameOrEmail());

    String role = null;

    // 사용자가 있으면
    if (userOptional.isPresent()) {
        User loggedInUser = userOptional.get();
        // 역할 중에 첫 번째 역할 가져오기
        Optional<Role> optionalRole = loggedInUser.getRoles().stream().findFirst();

        if (optionalRole.isPresent()) {
            Role userRole = optionalRole.get();
            // role 이름(ex: ROLE_USER, ROLE_ADMIN) 가져오기
            role = userRole.getName();
        }
    }

    // 5. JwtAuthResponse에 정보(tokentype, role) 설정
    // jwtAuthResponse: 인증 후 클라이언트에게 전달하는 jwt와 토큰 정보 객체 (accessToken, tokenType, role)
    JwtAuthResponse jwtAuthResponse = new JwtAuthResponse();

    // accessToken은 이미 할당되어서 tokenType, role만 생성해서 할당
    jwtAuthResponse.setRole(role);
    jwtAuthResponse.setAccessToken(token);

    // 6. jwt + 역할 정보 할당된 응답 반환
    return jwtAuthResponse;
}

 

로그인 요청 처리

jwt 생성, 반환

 

 

 

LoginDto.java

package net.javaguides.ems.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class LoginDto {
    private String usernameOrEmail;
    private String password;
}

 

로그인 시 사용자가 입력하는 사용자 정보

 

 

 

JwtAuthResponse.java

package net.javaguides.ems.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class JwtAuthResponse {

    private String accessToken;
    private String tokenType = "Bearer";
    private String role;
}

 

로그인 후 서버가 클라이언트에게 반환하는 jwt 토큰 관련 정보

사용자가 인증되었고 세션이 유지되고 있다는 확인증 같은 것

 

 

 

JwtTokenProvider.java

package net.javaguides.ems.security;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

import java.security.Key;
import java.util.Date;

@Component
public class JwtTokenProvider {

    // jwt 비밀키
    @Value("${app.jwt-secret}")
    private String jwtSecret;

    // jwt 유효기간
    @Value("${app.jwt-expiration-milliseconds}")
    private Long jwtExpirationMillis;

    // 1. jwt 생성
    public String generateToken(Authentication authentication) {
        String username = authentication.getName();
        Date currentDate = new Date();
        Date expireDate = new Date(currentDate.getTime() + jwtExpirationMillis);

        // jwt 생성 (username, 발행일, 만료일, 서명 포함)
        String token = Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(expireDate)
                .signWith(key())
                .compact();
        return token;
    }

    // 2. 서명에 사용할 비밀 키 생성
    private Key key() {
        return Keys.hmacShaKeyFor(
                Decoders.BASE64.decode(jwtSecret)
        );
    }


    // 3. jwt에서 username 가져오기 (인증된 사용자가 누구인지 확인)
    public String getUsername(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(key())
                .build()
                .parseClaimsJws(token)
                .getBody();

        String username = claims.getSubject();

        return username;
    }


    // 4. jwt 유효성 검증 (토큰이 변조되거나, 만료되지 않았는지 확인)
    public boolean validateToken(String token) {
        Jwts.parser()
                .setSigningKey(key())
                .build()
                .parse(token);

        // 예외가 발생하지 않으면 토큰이 유효함을 반환
        return true;
    }

}

 

jwt 토큰 생성

검증

토큰에서 사용자 정보 추출

 

 

 

UserRepository.java

package net.javaguides.ems.repository;

import net.javaguides.ems.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByUsernameOrEmail(String username, String email);
}

 

데이터베이스 User 스키마와 상호작용