ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JWT 실습 프로젝트 - 4 (회원가입, 권한검증)
    SPRING/스프링 시큐리티 -JWT 2021. 9. 18. 00:19

    SecurityUtil

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

    getCurrentUsername() 메소드는 JwtFilter 클래스의 doFilter 메소드에서 저장한 Security Context의 인증 정보에서 username을 리턴함.

     

    UserService

    @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 DuplicateMemberException("이미 가입되어 있는 유저입니다.");
            }
            //빌더 패턴의 장점
            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);
        }
    }

     

    signup()은 이미 같은 username으로 가입된 유저가 있는지 확인하고, UserDto 객체의 정보들을 기반으로 권한 객체와 유저 객체를 생성하여 DB에 저장함.

     

    getMyUserWithAuthorities 메소드는 SecurityUtil의 getCurrentUsername() 메소드가 리턴하는 username의 유저 및 권한 정보를 리턴함.

     

    UserController

    @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("/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 메소드는 permitAll 이기 때문에 권한없이 호출이 가능하다.

    getMyUserInfo 메소드는 현재 SecurityContxt에 저장되어 있는 인증 정보의 username을 기준으로 한 유저 정보 및 권한 정보를 리턴하는 API이다. @PreAuthorize를 통하여 ROLE_USER, ROLE_ADMIN 권한 모두 호출 가능하게 설정한다.

    getUserInfo 메소드는 username을 파라미터로 받아 해당 username의 유저 정보 및 권한 정보를 리턴한다.

     

    회원 가입 테스트

    권한검증

     

    아래와 같이 로그인

    아래와 같이 Authorization Type을 지정하고, 포스트맨 전역 변수(jwt_tutorial_token)에 담았던 Token을 사용하여 권한 검증을 한다.

     

    ADMIN 권한이 없는 경우에는 아래와 같이 FORBIDDEN 403 에러가 리턴되는 것을 확인 할 수 있다.

     

Designed by Tistory.