코드 그라데이션

JWT 예제 구현 (7) 회원가입, 권한 검증 로직 본문

Spring/Security

JWT 예제 구현 (7) 회원가입, 권한 검증 로직

완벽한 장면 2023. 12. 13. 22:54

UserController

package inflearn.freejwt.controller;

import inflearn.freejwt.dto.UserDto;
import inflearn.freejwt.entity.User;
import inflearn.freejwt.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

@RequiredArgsConstructor
@RestController
@RequestMapping("/api")
public class UserController {

    private final UserService userService;

    /**
     * 사용자 회원가입을 처리하는 엔드포인트.
     *
     * @param userdto 회원가입 요청에 필요한 사용자 정보를 담은 DTO 객체
     * @return 회원가입이 성공하면 사용자 정보를 담은 ResponseEntity를 반환
     */
    @PostMapping("/signup")
    public ResponseEntity<User> siginup(@Valid @RequestBody UserDto userdto) {
        return ResponseEntity.ok(userService.signup(userdto));
    }



    /**
     * 현재 사용자의 정보를 조회하는 엔드포인트.
     * 사용자 및 관리자 역할을 가진 사용자만 접근할 수 있다.
     *
     * @return 현재 사용자의 정보를 담은 ResponseEntity를 반환
     */
    @GetMapping("/user")
    @PreAuthorize("hasAnyRole('USER', 'ADMIN')")
    public ResponseEntity<User> getMyUserInfo() {
        // userService를 사용하여 현재 사용자의 정보를 조회하고, 그 결과를 ResponseEntity로 반환.
        // getMyUserWithAuthorities()는 현재 사용자의 정보와 권한 정보를 함께 조회하는 서비스 메서드.
        // 반환된 정보는 ResponseEntity.ok()를 사용하여 HTTP 200 OK 상태와 함께 반환됨.
        return ResponseEntity.ok(userService.getMyUserWithAuthorities().get());
    }



    /**
     * 특정 사용자의 정보를 조회하는 엔드포인트.
     * 관리자 역할을 가진 사용자만 접근할 수 있다.
     *
     * @param username 조회할 사용자의 이름
     * @return 특정 사용자의 정보를 담은 ResponseEntity를 반환
     */
    @GetMapping("/user/{username}")
    @PreAuthorize("hasAnyRole('ADMIN')")
    public ResponseEntity<User> getUserInfo(@PathVariable String username) {
        // userService를 사용하여 특정 사용자의 정보를 조회하고, 그 결과를 ResponseEntity로 반환함.
        // getUserWithAuthorities(username)는 특정 사용자 정보와 권한 정보를 함께 조회하는 서비스 메서드.
        // 반환된 정보는 ResponseEntity.ok()를 사용하여 HTTP 200 OK 상태와 함께 반환됨.
        return ResponseEntity.ok(userService.getUserWithAuthorities(username).get());
    }
}

 

 

UserService

package inflearn.freejwt.service;

import inflearn.freejwt.dto.UserDto;
import inflearn.freejwt.entity.Authority;
import inflearn.freejwt.entity.User;
import inflearn.freejwt.repository.UserRepository;
import inflearn.freejwt.util.SecurityUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.Optional;

@RequiredArgsConstructor
@Service
public class UserService {

    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;


    @Transactional
    public User signup(UserDto userDto) {
        if (userRepository.findOneWithAuthoritiesByUsername(userDto.getUsername()).orElse(null) != null) {
            throw new RuntimeException("이미 가입되어 있는 유저입니다.");
        }
        // 권한 정보 생성
        Authority authority = Authority.builder()
                .authorityName("ROLE_USER")
                .build();

        // 유저 정보 생성
        User user = User.builder()
                .username(userDto.getUsername())
                .password(passwordEncoder.encode(userDto.getPassword()))
                .nickname(userDto.getNickname())
                .authorities(Collections.singleton(authority))
                .activated(true)
                .build();

        // 저장
        return userRepository.save(user);
    }

    /**
     *
     * @param username
     * @return
     * username을 기준으로 정보를 가져오는 메서드
     */
    @Transactional(readOnly = true)
    public Optional<User> getUserWithAuthorities(String username) {
        return userRepository.findOneWithAuthoritiesByUsername(username);
    }

    /**
     * SecurityContext에 저장된 username의 정보만 가져온다.
     * @return
     */
    @Transactional(readOnly = true)
    public Optional<User> getMyUserWithAuthorities() {
        return SecurityUtil.getCurrentUsername().flatMap(userRepository::findOneWithAuthoritiesByUsername);
    }

}

 

 

util/SecurityUtil

package inflearn.freejwt.util;

import lombok.NoArgsConstructor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Optional;

@NoArgsConstructor
public class SecurityUtil {
    private static final Logger logger = LoggerFactory.getLogger(SecurityUtil.class);

    /**
     * 이 메서드의 역할은 Security Context의 Authentication 객체를 이용해 username을 리턴해주는 간단한 유틸성 메서드
     *
     * @return 현재 사용자의 username을 Optional<String> 형태로 반환. 인증 정보가 없으면 Optional.empty() 반환.
     */
    public static Optional<String> getCurrentUsername() {
        // 현재의 Authentication 객체를 가져온다.
        final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        // 인증 정보가 없는 경우
        if (authentication == null) {
            logger.debug("Security Context에 인증 정보가 없습니다.");
            return Optional.empty(); // 빈 Optional 반환
        }

        String username = null;

        // Authentication 객체의 주체(principal)를 확인
        if (authentication.getPrincipal() instanceof UserDetails) {
            UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
            username = springSecurityUser.getUsername(); // UserDetails에서 username을 가져옴
        }
        else if (authentication.getPrincipal() instanceof String) {
            username = (String) authentication.getPrincipal(); // 문자열로 된 주체의 경우, username으로 설정
        }

        return Optional.ofNullable(username); // Optional로 username 반환 (null인 경우에도 처리)
    }
}
728x90
Comments