๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Spring Boot/๊ฐœ๋ฐœ ๊ธฐ๋ก

[Spring Security] JWT Tutorial (5) ํšŒ์›๊ฐ€์ž…, ๊ถŒํ•œ ๊ฒ€์ฆ

by oliviarla 2022. 4. 28.

SecurityUtil ํด๋ž˜์Šค ์ƒ์„ฑ

package com.oliviarla.tutorial.util;

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() {
    }

    public static Optional<String> getCurrentUsername() {
        final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        if (authentication == null) {
            logger.debug("Security Context์— ์ธ์ฆ ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.");
            return Optional.empty();
        }

        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);
    }
}

JwtFilter ํด๋ž˜์Šค์˜ DoFilter ๋ฉ”์†Œ๋“œ์—์„œ ์ €์žฅํ•œ Security Context์˜ ์ธ์ฆ ์ •๋ณด์—์„œ username์„ ๋ฐ˜ํ™˜

UserSerivice ํด๋ž˜์Šค ์ƒ์„ฑ

package com.oliviarla.tutorial.service;

import com.oliviarla.tutorial.dto.UserDto;
import com.oliviarla.tutorial.entity.Authority;
import com.oliviarla.tutorial.entity.User;
import com.oliviarla.tutorial.repository.UserRepository;
import com.oliviarla.tutorial.util.SecurityUtil;
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;

@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 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);
    }

    @Transactional(readOnly = true)
    public Optional<User> getUserWithAuthorities(String username) {
        return userRepository.findOneWithAuthoritiesByUsername(username);
    }

    @Transactional(readOnly = true)
    public Optional<User> getMyUserWithAuthorities() {
        return SecurityUtil.getCurrentUsername().flatMap(userRepository::findOneWithAuthoritiesByUsername);
    }
}

UserRepository, PasswordEncoder๋ฅผ ์ฃผ์ž…๋ฐ›์Œ

signup(): ๊ฐ€์ž…๋˜์–ด ์žˆ๋Š” ์‚ฌ์šฉ์ž์ธ์ง€ ํ™•์ธํ•˜๊ณ  UserDto ๊ฐ์ฒด์˜ ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ Authority, User ๊ฐ์ฒด ์ƒ์„ฑํ•ด DB์— ์ €์žฅ, ์ด ๋ฉ”์†Œ๋“œ๋กœ ์ƒ์„ฑ๋œ ์‚ฌ์šฉ์ž๋Š” ROLE_USER ๊ถŒํ•œ๋งŒ ์†Œ์œ  ๊ฐ€๋Šฅ

getUserWithAuthorities(): username์œผ๋กœ ์‚ฌ์šฉ์ž์™€ ๊ถŒํ•œ ์ •๋ณด๋ฅผ ๊ฒ€์ƒ‰

getMyUserWithAuthorities(): ํ˜„์žฌ ์‚ฌ์šฉ์ž์˜ username ๊ฐ€์ ธ์™€ ์‚ฌ์šฉ์ž ์ •๋ณด, ๊ถŒํ•œ ์ •๋ณด ๋ฐ˜ํ™˜

UserController ํด๋ž˜์Šค ์ƒ์„ฑ

package com.oliviarla.tutorial.controller;

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

import javax.validation.Valid;

@RestController
@RequestMapping("/api")
public class UserController {
    private final UserService userService;

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

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

    @GetMapping("/user")
    @PreAuthorize("hasAnyRole('USER','ADMIN')")
    public ResponseEntity<User> getMyUserInfo() {
        return ResponseEntity.ok(userService.getMyUserWithAuthorities().get());
    }

    @GetMapping("/user/{username}")
    @PreAuthorize("hasAnyRole('ADMIN')")
    public ResponseEntity<User> getUserInfo(@PathVariable String username) {
        return ResponseEntity.ok(userService.getUserWithAuthorities(username).get());
    }
}

signup(): ํšŒ์›๊ฐ€์ž… API, permitAll์œผ๋กœ ๊ถŒํ•œ ์—†์ด ํ˜ธ์ถœ ๊ฐ€๋Šฅ

getMyUserInfo(): ํ˜„์žฌ Security Context์— ์ €์žฅ๋˜์–ด ์žˆ๋Š” ์ธ์ฆ ์ •๋ณด์˜ username์„ ๊ธฐ์ค€์œผ๋กœ ํ•œ ์‚ฌ์šฉ์ž ์ •๋ณด ๋ฐ ๊ถŒํ•œ ์ •๋ณด ๋ฐ˜ํ™˜

getUserInfo(): username์„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ž…๋ ฅ๋ฐ›์•„ ์‚ฌ์šฉ์ž์™€ ๊ถŒํ•œ ์ •๋ณด ๋ฐ˜ํ™˜, @PreAuthorize๋ฅผ ์‚ฌ์šฉํ•ด ROLE_ADMIN ๊ถŒํ•œ์„ ์†Œ์œ ํ•œ ํ† ํฐ๋งŒ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ

 

ํšŒ์› ๊ฐ€์ž… ํ…Œ์ŠคํŠธ

๊ถŒํ•œ ๊ฒ€์ฆ ํ…Œ์ŠคํŠธ

authenticate API๋ฅผ ํ†ตํ•ด oliviarla ์œ ์ €์˜ ํ† ํฐ ํš๋“

 

Authenticate API ํ˜ธ์ถœ ์‹œ Tests ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ๋‹ด์•„๋‘” ์ „์—ญ๋ณ€์ˆ˜ jwt_tutorial_token์œผ๋กœ ์œ ์ € ์ •๋ณด ๋ฐ ๊ถŒํ•œ ์ •๋ณด ๋ฐ˜ํ™˜๋ฐ›์Œ

var jsonData = JSON.parse(responseBody)
pm.globals.set("jwt_tutorial_token", jsonData.token);
 
 

 

/api/user/{username}์€ ROLE_ADMIN ๊ถŒํ•œ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ 403 ์—๋Ÿฌ ๋ฐ˜ํ™˜

 

 

์ถœ์ฒ˜: https://silvernine.me/wp/?p=1178

๋Œ“๊ธ€