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

[Spring Security] JWT Tutorial (4) Repository ์ƒ์„ฑ, ๋กœ๊ทธ์ธ

by oliviarla 2022. 4. 28.

DTO ๊ณ„์ธต ์ƒ์„ฑ

LoginDto

package com.oliviarla.tutorial.dto;

import lombok.*;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class LoginDto {
    @NotNull
    @Size(min = 3, max = 50)
    private String username;

    @NotNull
    @Size(min = 3, max = 100)
    private String password;
}

TokenDto

package com.oliviarla.tutorial.dto;

import lombok.*;

@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class TokenDto {
    private String token;
}

UserDto

package com.oliviarla.tutorial.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.*;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserDto {
    @NotNull
    @Size(min = 3, max = 50)
    private String username;

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @NotNull
    @Size(min = 3, max = 100)
    private String password;

    @NotNull
    @Size(min = 3, max = 50)
    private String nickname;
}

@JsonProperty ์‚ฌ์šฉํ•˜๋Š” ์ด์œ : ์‘๋‹ต๊ฐ’์— password ํฌํ•จํ•˜์ง€ ์•Š๊ธฐ ์œ„ํ•ด JsonIgnore ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ Deserialize๊นŒ์ง€ ๋ฌด์‹œํ•˜๊ฒŒ ๋˜์–ด ์œ ํšจ์„ฑ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒ -> ์“ฐ๋ ค๋Š” ๊ฒฝ์šฐ(deserialize)์—๋งŒ ์ ‘๊ทผ ํ—ˆ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ

 

Repository ์ƒ์„ฑ

UserRepository.java

package com.oliviarla.tutorial.repository;


import com.oliviarla.tutorial.entity.User;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;


import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {
    @EntityGraph(attributePaths = "authorities")
    Optional<User> findOneWithAuthoritiesByUsername(String username);
}

username์„ ๊ธฐ์ค€์œผ๋กœ User ์ •๋ณด ๊ฐ€์ ธ์˜ค๋Š” ์—ญํ•  ์ˆ˜ํ–‰

authorities ์ •๋ณด๋Š” Eager ์กฐํšŒ๋กœ User์™€ ์กฐ์ธํ•ด์„œ ๊ฐ€์ ธ์˜ด

 

์„œ๋น„์Šค ๊ณ„์ธต ์ƒ์„ฑ

package com.oliviarla.tutorial.service;

import com.oliviarla.tutorial.entity.User;
import com.oliviarla.tutorial.repository.UserRepository;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

@Component("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
    private final UserRepository userRepository;

    public CustomUserDetailsService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    @Transactional
    public UserDetails loadUserByUsername(final String username) {
        return userRepository.findOneWithAuthoritiesByUsername(username)
                .map(user -> createUser(username, user))
                .orElseThrow(() -> new UsernameNotFoundException(username + " -> ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."));
    }

    private org.springframework.security.core.userdetails.User createUser(String username, User user) {
        if (!user.isActivated()) {
            throw new RuntimeException(username + " -> ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");
        }
        List<GrantedAuthority> grantedAuthorities = user.getAuthorities().stream()
                .map(authority -> new SimpleGrantedAuthority(authority.getAuthorityName()))
                .collect(Collectors.toList());
        return new org.springframework.security.core.userdetails.User(user.getUsername(),
                user.getPassword(),
                grantedAuthorities);
    }
}

UserDetailsService ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  UserRepository๋ฅผ ์ฃผ์ž…๋ฐ›์Œ

loadUserByUsername(): ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ User ์ •๋ณด ์กฐํšŒ

createUser(): ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒํ•ด์˜จ User ๋ฐ ๊ถŒํ•œ ์ •๋ณด๋ฅผ userdetails ํŒจํ‚ค์ง€์˜ User ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋ฐ˜ํ™˜

 

์ปจํŠธ๋กค๋Ÿฌ ๊ณ„์ธต ์ƒ์„ฑ

AuthController.java

package com.oliviarla.tutorial.controller;

import com.oliviarla.tutorial.dto.LoginDto;
import com.oliviarla.tutorial.dto.TokenDto;
import com.oliviarla.tutorial.jwt.JwtFilter;
import com.oliviarla.tutorial.jwt.TokenProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RestController
@RequestMapping("/api")
public class AuthController {
    private final TokenProvider tokenProvider;
    private final AuthenticationManagerBuilder authenticationManagerBuilder;

    public AuthController(TokenProvider tokenProvider, AuthenticationManagerBuilder authenticationManagerBuilder) {
        this.tokenProvider = tokenProvider;
        this.authenticationManagerBuilder = authenticationManagerBuilder;
    }

    @PostMapping("/authenticate")
    public ResponseEntity<TokenDto> authorize(@Valid @RequestBody LoginDto loginDto) {

        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(loginDto.getUsername(), loginDto.getPassword());

        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);

        String jwt = tokenProvider.createToken(authentication);

        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add(JwtFilter.AUTHORIZATION_HEADER, "Bearer " + jwt);

        return new ResponseEntity<>(new TokenDto(jwt), httpHeaders, HttpStatus.OK);
    }
}

- authorize(): username, password๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ž…๋ ฅ๋ฐ›์•„ UserPasswordAuthenticationToken ๊ฐ์ฒด ์ƒ์„ฑ

- UserPasswordAuthenticationToken ๊ฐ์ฒด๋กœ authenticate ๋ฉ”์†Œ๋“œ ๋กœ์ง์„ ์ˆ˜ํ–‰

- ์ธ์ฆ ์ •๋ณด authentication์„ ํ˜„์žฌ ์‹คํ–‰์ค‘์ธ ์Šค๋ ˆ๋“œ์— ์ €์žฅ

- authentication ๊ธฐ๋ฐ˜์œผ๋กœ jwt ํ† ํฐ ์ƒ์„ฑ 

- ์ƒ์„ฑ๋œ ํ† ํฐ์„ httpHeaders์— ๋„ฃ๊ณ  Tokendto ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•ด Response Body์—๋„ ๋„ฃ์–ด ๋ฐ˜ํ™˜

 

๋กœ๊ทธ์ธ ํ…Œ์ŠคํŠธ

 

 

Tests ํƒญ์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋”ฉํ•˜๋ฉด Postman ์ „์—ญ ๋ณ€์ˆ˜์— ํ•ด๋‹น ํ† ํฐ ๋‹ด์•„ ๋‹ค๋ฅธ ํ˜ธ์ถœ ์ž‘์—…์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

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

 

 

 

์ถœ์ฒ˜: https://eglowc.tistory.com/28, https://silvernine.me/wp/?p=1152

 

๋Œ“๊ธ€