본문 바로가기

Spring

[Spring Boot JWT Tutorial - 인프런] 스프링 JWT 적용하기 part4. 회원가입 API 구현 및 권한 검증 확인

인프런의 Spring Boot JWT Tutorial의 강의를 보고 정리해 보았습니다.

 

[무료] Spring Boot JWT Tutorial - 인프런 | 강의

Spring Boot, Spring Security, JWT를 이용한 튜토리얼을 통해 인증과 인가에 대한 기초 지식을 쉽고 빠르게 학습할 수 있습니다., - 강의 소개 | 인프런...

www.inflearn.com

이전 파트에서 Repository, 로그인 API 구현에 대해서 기록하였다.

 [Spring Boot JWT Tutorial - 인프런] 스프링 JWT 적용하기 part3. Repository, 로그인 API 구현


SecurityUtil

  • Security관련 간단한 메서드들 구현
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;

public class SecurityUtil {

    private static final Logger logger = LoggerFactory.getLogger(SecurityUtil.class);

    private SecurityUtil() {}

    //SecurityContext의 Authentication 객체를 이용해 username을 리턴해주는 간단한 유틸성 메서드
    public static Optional<String> getCurrentUsername() {
        final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

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

        //SecurityContext에 Authentication 객체가 저장되는 시점은 JwtFilter의 doFilter메서드에서 Request가 들어올 때 SecurityContext에 Authentication 객체를 저장해서 사용하게 된다.
        String username = null;
        if (authentication.getPrincipal() instanceof UserDetails) {
            UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
            username = springSecurityUser.getUsername();
        } else if (authentication.getPrincipal() instanceof String) {
            username = (String) authentication.getPrincipal();
        }

        return Optional.ofNullable(username);
    }
}

 

UserService

  • 회원 가입
  • 유저 조회
import java.util.Collections;

import com.example.tutorial.Util.SecurityUtil;
import com.example.tutorial.dto.UserDto;
import com.example.tutorial.entity.Authority;
import com.example.tutorial.entity.User;
import com.example.tutorial.exception.DuplicateMemberException;
import com.example.tutorial.exception.NotFoundMemberException;
import com.example.tutorial.repository.UserRepository;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {
    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;

    public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
    }

    // 회원가입 로직 수행
    @Transactional
    public UserDto signup(UserDto userDto) {
        파라미터로 받은 UserDto의 유저 이름을 가져와서 확인
        if (userRepository.findOneWithAuthoritiesByUsername(userDto.getUsername()).orElse(null) != null) {
            throw new DuplicateMemberException("이미 가입되어 있는 유저입니다.");
        }
        
        //권한 정보는 ROLE_USER로 초기화
        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 UserDto.from(userRepository.save(user));
    }

    //로그인한 사람과는 관계 없이 username을 기준으로  유저 정보와 권한정보를 가져옴 -> Admin 계정인 경우
    @Transactional(readOnly = true)
    public UserDto getUserWithAuthorities(String username) {
        return UserDto.from(userRepository.findOneWithAuthoritiesByUsername(username).orElse(null));
    }

    //SecurityContext에 저장된 username의 user 정보와 권한 정보만 가져옴
    @Transactional(readOnly = true)
    public UserDto getMyUserWithAuthorities() {
        return UserDto.from(
                SecurityUtil.getCurrentUsername()
                        .flatMap(userRepository::findOneWithAuthoritiesByUsername)
                        .orElseThrow(() -> new NotFoundMemberException("Member not found"))
        );
    }
}

 

UserController

import com.example.tutorial.dto.UserDto;
import com.example.tutorial.service.UserService;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;

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

    private final UserService userService;


    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/hello")
    public ResponseEntity<String> hello() {
        return ResponseEntity.ok("hello");
    }

    @PostMapping("/test-redirect")
    public void testRedirect(HttpServletResponse response) throws IOException {
        response.sendRedirect("/api/user");
    }

    @PostMapping("/signup")
    public ResponseEntity<UserDto> signup(
            @Valid @RequestBody UserDto userDto
    ) {
        return ResponseEntity.ok(userService.signup(userDto));
    }

    @GetMapping("/user")
    @PreAuthorize("hasAnyRole('USER','ADMIN')")  //USER와 ADMIN 두가지 권한 모두 허용, 이 설정을 하기위해 SecurityConfig에서 @EnableGlobalMethodSecurity를 적용하였다.
    public ResponseEntity<UserDto> getMyUserInfo(HttpServletRequest request) {
        return ResponseEntity.ok(userService.getMyUserWithAuthorities());
    }

    @GetMapping("/user/{username}")
    @PreAuthorize("hasAnyRole('ADMIN')")  // ADMIN 권한만 호출할 수 있음
    public ResponseEntity<UserDto> getUserInfo(@PathVariable String username) {
        return ResponseEntity.ok(userService.getUserWithAuthorities(username));
    }
}

 

GitHub - SilverNine/spring-boot-jwt-tutorial

Contribute to SilverNine/spring-boot-jwt-tutorial development by creating an account on GitHub.

github.com