# [Security] JWT ๊ตฌํ˜„ (6) - ํšŒ์› ๊ฐ€์ž… & Authorization Validation
Study Repository

[Security] JWT ๊ตฌํ˜„ (6) - ํšŒ์› ๊ฐ€์ž… & Authorization Validation

by rlaehddnd0422

๋งˆ์ง€๋ง‰์œผ๋กœ ํšŒ์› ๊ฐ€์ž…์„ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ํšŒ์›์„ ์ €์žฅํ•˜๊ณ  ๋กœ๊ทธ์ธ์„ ํ†ตํ•ด JWT๋ฅผ ๋ฐœ๊ธ‰๋ฐ›์•„, ์ œํ•œ๋œ ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผ ์‹œ ๋ฐœ๊ธ‰๋ฐ›์€ JWT๋ฅผ

๊ฒ€์ฆํ•˜๋Š” ๋กœ์ง์„ ๊ฐœ๋ฐœํ•ด๋ด…์‹œ๋‹ค.

 

์šฐ์„ , ํšŒ์›๊ฐ€์ž… ๋กœ์ง์„ ๊ฐœ๋ฐœํ•˜๊ธฐ ์œ„ํ•ด MemberService๋ฅผ ๋งŒ๋“ค์–ด์ค์‹œ๋‹ค.

 

MemberService

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MemberService {

    private final MemberRepository memberRepository;
    private final PasswordEncoder passwordEncoder;
	
    // (1)
    @Transactional
    public MemberDto signup(MemberDto memberDto) {
        Member memberInDb = memberRepository.findOneWithAuthoritiesByUsername(memberDto.getUsername()).orElse(null);

        if (memberInDb != null) {
            throw new RuntimeException("์ด๋ฏธ ๊ฐ€์ž…๋˜์–ด ์žˆ๋Š” ์œ ์ €์ž…๋‹ˆ๋‹ค.");
        }

        Member member = Member.create(memberDto, passwordEncoder.encode(memberDto.getPassword()));
        return MemberDto.toDto(memberRepository.save(member));
    }

	// (2)
    public MemberDto getMemberWithAuthorities(String username) {
        return MemberDto.toDto(
                memberRepository.findOneWithAuthoritiesByUsername(username).orElse(null));
    }
	
    
    // (3)
    // SecurityContext ๋‚ด๋ถ€ Authentication ๊ฐ์ฒด์˜ usrename
    public MemberDto me() {
        return MemberDto.toDto(SecurityUtil.getCurrentUsername()
                .flatMap(memberRepository::findOneWithAuthoritiesByUsername).orElse(null));
    }
}

singup() ๋ฉ”์†Œ๋“œ

์‹ค์งˆ์ ์œผ๋กœ ํšŒ์›๊ฐ€์ž…์‹œ ๋™์ž‘ํ•˜๋Š” ๋ฉ”์†Œ๋“œ์ž…๋‹ˆ๋‹ค.

 

  1. MemberDto๋ฅผ ์ธ์ž๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค.
  2. findOneWithAuthorityByUsername() : JPA ์ฟผ๋ฆฌ ๋ฉ”์†Œ๋“œ์ž…๋‹ˆ๋‹ค.
    1. memberDto์˜ username์œผ๋กœ Repository์—์„œ member๋ฅผ ์ฐพ๊ณ  member์˜ authority๊นŒ์ง€ @EntityGraph๋กœ ํ•จ๊ป˜ ํƒ์ƒ‰ํ•ด์„œ memberInDb์— ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.
  3. ๋งŒ์•ฝ ํ•ด๋‹น ๋ฉค๋ฒ„๋ฅผ ์ฐพ์•˜๋‹ค๋ฉด ์ด๋ฏธ ๊ฐ€์ž…๋˜์–ด ์žˆ๋Š” ์œ ์ €์ด๋ฏ€๋กœ RuntimeException์„ ๋˜์ง€๊ณ ,
public static Member create(MemberDto memberDto, String encodedPw) {
    return Member.builder()
            .username(memberDto.getUsername())
            .password(encodedPw)
            .nickname(memberDto.getNickname())
            .activated(true)
            .authorities(Collections.singleton(Authority.builder().authorityName("ROLE_USER").build()))
            .build();
}

4. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด Member์˜ static๋ฉ”์†Œ๋“œ์˜ builder๋กœ ํšŒ์›์„ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด Repository์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ์ด ๋•Œ Password๋Š” ๋ฐ˜๋“œ์‹œ Encodingํ•˜์—ฌ ์ €์žฅํ•ฉ์‹œ๋‹ค.

 

5. ์ €์žฅํ•œ Member โžก๏ธ MemberDto๋กœ ๋ณ€ํ™˜, ๋ฆฌํ„ด 

 

getMemberWithAuthoritiesForAdmin()

public MemberDto getMemberWithAuthoritiesForAdmin(String username) {
    return MemberDto.toDto(
            memberRepository.findOneWithAuthoritiesByUsername(username).orElse(null));
}
  • ์ผ๋ฐ˜์ ์œผ๋กœ username์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ member๋ฅผ ์ฐพ๊ณ  authority๊นŒ์ง€ ํ•จ๊ป˜ ๊ฒ€์ƒ‰ํ•˜๋Š” ๋ฉ”์†Œ๋“œ์ž…๋‹ˆ๋‹ค.
  • (MemberController์—์„œ admin๊ถŒํ•œ์˜ ์‚ฌ์šฉ์ž๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ•  ์˜ˆ์ •

 

getMemberWithAuthoritiesForUser()

// SecurityContext ๋‚ด๋ถ€ Authentication ๊ฐ์ฒด์˜ usrename
public MemberDto me() {
    return MemberDto.toDto(SecurityUtil.getCurrentUsername()
            .flatMap(memberRepository::findOneWithAuthoritiesByUsername).orElse(null));
}
public static Optional<String> getCurrentUsername() {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    if (authentication == null) {
        log.info("no authentication info found");
        return Optional.empty();
    }

    Object principal = authentication.getPrincipal();
    if (principal instanceof UserDetails) {
        UserDetails userDetails = (UserDetails) principal;
        return Optional.ofNullable(userDetails.getUsername());
    }
    if (principal instanceof String) {
        return Optional.of(principal.toString());
    }
    throw new IllegalStateException("invalid authentication");
}
  • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ž์‹ ์˜ ์ •๋ณด๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ๊ฒ€์ƒ‰ํ•˜๋Š” ๋ฉ”์†Œ๋“œ์ž…๋‹ˆ๋‹ค. 
  • SecurityContext์—์„œ Authentication ๊ฐ์ฒด๋ฅผ ๊บผ๋‚ด์–ด UserDetails์˜ Username์„ ๊บผ๋‚ด ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.
  • (MemberController์—์„œ admin, user ๊ถŒํ•œ์˜ ์‚ฌ์šฉ์ž๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ•  ์˜ˆ์ •)

 

๋งˆ์ง€๋ง‰์œผ๋กœ ํšŒ์›๊ฐ€์ž…์„ ์š”์ฒญํ•  ์ปจํŠธ๋กค๋Ÿฌ ๊ณ„์ธต์„ ๊ฐœ๋ฐœํ•ด๋ด…์‹œ๋‹ค.

MemberController

@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class MemberController {
    private final MemberService memberService;

    @PostMapping(value = "/signup")
    public ResponseEntity<MemberDto> signUp(@Valid @RequestBody MemberDto memberDto) {
        return ResponseEntity.ok(memberService.signup(memberDto));
    }

    @GetMapping(value = "/member", produces = MediaType.APPLICATION_JSON_VALUE)
    @PreAuthorize("hasAnyRole('USER', 'ADMIN')")
    public ResponseEntity<MemberDto> findMyInfoForUserAndAdmin() {
        return ResponseEntity.ok(memberService.getMemberWithAuthoritiesForUser());
    }

    @GetMapping(value = "/member/{username}", produces = MediaType.APPLICATION_JSON_VALUE)
    @PreAuthorize("hasRole('ADMIN')")
    @ResponseBody
    public ResponseEntity<MemberDto> findInfoForAdmin(@PathVariable String username) {
        return ResponseEntity.ok(memberService.getMemberWithAuthoritiesForAdmin(username));
    }
}
  • ํšŒ์›๊ฐ€์ž…์„ ์š”์ฒญ, ์ž์‹ ์˜ ์ •๋ณด๋ฅผ ์กฐํšŒ, username์œผ๋กœ ํšŒ์›์˜ ์ •๋ณด๋ฅผ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋Š” API๋ฅผ MemberController์— ์„ค๊ณ„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
@PostMapping(value = "/signup")
public ResponseEntity<MemberDto> signUp(@Valid @RequestBody MemberDto memberDto) {
    return ResponseEntity.ok(memberService.signup(memberDto));
}

signUp() - "/api/signup"

  • RequestBody : MemberDto
  • memberService์— ์ ‘๊ทผํ•ด RequestBody์ •๋ณด๋กœ ํšŒ์›๊ฐ€์ž…์„ ์ง„ํ–‰ํ•˜๊ณ  ์˜ˆ์™ธ ๋ฐœ์ƒ ์—†์ด ์™„๋ฃŒ ๋˜๋ฉด ResponseEntity์— Dto ๋‹ด์•„ ๋ฆฌํ„ด 
@GetMapping(value = "/member", produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAnyRole('USER', 'ADMIN')")
public ResponseEntity<MemberDto> findMyInfoForUserAndAdmin() {
    return ResponseEntity.ok(memberService.getMemberWithAuthoritiesForUser());
}

findMyInfoUserAndAdmin() - "/api/member" 

  • @PreAuthorize : USER, ADMIN
  • ์ž์‹ ์˜ ์ •๋ณด๋ฅผ ์กฐํšŒํ•˜๋Š” API์ž…๋‹ˆ๋‹ค. 
  • ์œ„์—์„œ ์–ธ๊ธ‰ํ•œ memberService์˜ getMemberWithAuthoritiesForUser() ๋ฉ”์†Œ๋“œ๋กœ ์ž์‹ ์˜ ์ •๋ณด๋ฅผ MemberDto์— ๋‹ด์•„ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.
@GetMapping(value = "/member/{username}", produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasRole('ADMIN')")
@ResponseBody
public ResponseEntity<MemberDto> findInfoForAdmin(@PathVariable String username) {
    return ResponseEntity.ok(memberService.getMemberWithAuthoritiesForAdmin(username));
}

findInfoForAdmin() - "/api/member/{username}" 

  • @PathVariable : username
  • @PreAuthorize : ADMIN 
  • ํšŒ์›์˜ ์ •๋ณด๋ฅผ username์„ ํ†ตํ•ด ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”์†Œ๋“œ์ž…๋‹ˆ๋‹ค.
  • memberService์˜ getMemberWithAuthorityForAdmin() ๋ฉ”์†Œ๋“œ๋กœ ์œ ์ €์˜ ์ •๋ณด๋ฅผ ๊ฒ€์ƒ‰ํ•˜์—ฌ ResponseEntity์— MemberDto๋ฅผ ๋‹ด์•„ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.

 

Postman Test

๋งˆ์ง€๋ง‰์œผ๋กœ ํฌ์ŠคํŠธ๋งจ์„ ์‚ฌ์šฉํ•˜์—ฌ API๋ฅผ ํ…Œ์ŠคํŠธ ํ•ด๋ด…์‹œ๋‹ค.

 

ํšŒ์›๊ฐ€์ž… โžก๏ธ ๋กœ๊ทธ์ธ โžก๏ธ ๋ฆฌ์†Œ์Šค ์š”์ฒญ 

ํšŒ์›๊ฐ€์ž…

POST : /api/signup

 

๋กœ๊ทธ์ธ

POST : api/authenticate -> JWT ๋ฐœ๊ธ‰

 

๊ถŒํ•œ ๊ฒ€์ฆ OK

GET : /api/member

 

๊ถŒํ•œ ๊ฒ€์ฆ Forbidden

GET : api/member/{username}

  • USER ๊ถŒํ•œ์„ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž์˜ Token์œผ๋กœ ADMIN ๊ถŒํ•œ์œผ๋กœ ์ œํ•œ๋œ ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผ ์‹œ Forbidden 403 Error๋ฅผ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.

 

 

<์ฐธ๊ณ  ์ž๋ฃŒ>

๋ณธ ํฌ์ŠคํŒ…์€ ์ •์€๊ตฌ๋‹˜์˜ Spring Boot JWT Tutorial ๊ฐ•์˜๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.

 

[๋ฌด๋ฃŒ] Spring Boot JWT Tutorial - ์ธํ”„๋Ÿฐ | ๊ฐ•์˜

Spring Boot, Spring Security, JWT๋ฅผ ์ด์šฉํ•œ ํŠœํ† ๋ฆฌ์–ผ์„ ํ†ตํ•ด ์ธ์ฆ๊ณผ ์ธ๊ฐ€์— ๋Œ€ํ•œ ๊ธฐ์ดˆ ์ง€์‹์„ ์‰ฝ๊ณ  ๋น ๋ฅด๊ฒŒ ํ•™์Šตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค., - ๊ฐ•์˜ ์†Œ๊ฐœ | ์ธํ”„๋Ÿฐ

www.inflearn.com

 

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

Study Repository

rlaehddnd0422

ํ™œ๋™ํ•˜๊ธฐ