-
Spring Boot JWT Tutorial (4)권한 다른 APISpring/SpringSecurity 2022. 4. 13. 01:17
===
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-jwt/dashboard
[무료] Spring Boot JWT Tutorial - 인프런 | 강의
Spring Boot, Spring Security, JWT를 이용한 튜토리얼을 통해 인증과 인가에 대한 기초 지식을 쉽고 빠르게 학습할 수 있습니다., - 강의 소개 | 인프런...
www.inflearn.com
===
- 회원가입 API 생성
- 권한검증 확인
===
간단한 유틸리티 메소드를 만들기 위해 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이라는 메소드를 하나 갖고 있는 클래스
SecurityContext에서 Authentication객체를 꺼내와서 이걸 통해 username을 return해주는 간단한 util성 메소드
*SecurityContext에 Authentication객체가 저장되는 시점은
우리가 전에 만들었던 JwtFilter의 doFilter메소드를 보면
request가 들어오는 시점에 SecurityContext에 Authentication객체가 저장이 됨. 이때 저장된게 꺼내지게 되는것
===
회원가입, 유저정보조회 등의 메소드를 만들기 위해 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 UserDto 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 UserDto.from(userRepository.save(user)); } @Transactional(readOnly = true) public UserDto getUserWithAuthorities(String username) { return UserDto.from(userRepository.findOneWithAuthoritiesByUsername(username).orElse(null)); } @Transactional(readOnly = true) public UserDto getMyUserWithAuthorities() { return UserDto.from(SecurityUtil.getCurrentUsername().flatMap(userRepository::findOneWithAuthoritiesByUsername).orElse(null)); } }
UserRepository, PasswordEncoder를 주입받음
signup메소드는 회원가입 로직 수행 메소드
파라미터로 받은 userDto안에 username을 기준으로 이미 DB에 이 username으로 저장돼 있는지 먼저 찾아보고
없으면 권한정보를 만들고
권한정보를 넣어 유저정보를 만듦
그리고 userRepository의 save로 DB저장
이 유저(signup메소드를 통해 가입한 회원)는 ROLE_USER라는 권한 하나 갖고 있다!
data.sql에서 자동 생성되는 admin 계정은
권한을 두개 갖고 있다. ROLE_USER와 ROLE_ADMIN
이 차이를 통해 추후에 권한 검증 부분을 테스트
===
그리고 유저 권한정보를 갖고 오는 메소드가 2개
위에건 username을 받아서 username에 해당하는 user객체와 권한정보를 갖고 오는 메소드
아래건 현재 시큐리티 컨텍스트에 저장이 돼 있는 username에 해당하는 유저정보와 권한정보만 받아갈 수 있다.
이 두가지 메소드의 허용권한을 다르게 해서 권한 검증에 대한 부분을 테스트할 것
===
UserService의 메소드들을 호출할 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("/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')") public ResponseEntity<UserDto> getMyUserInfo(HttpServletRequest request) { return ResponseEntity.ok(userService.getMyUserWithAuthorities()); } @GetMapping("/user/{username}") @PreAuthorize("hasAnyRole('ADMIN')") public ResponseEntity<UserDto> getUserInfo(@PathVariable String username) { return ResponseEntity.ok(userService.getUserWithAuthorities(username)); } }
/signup는 UserDto객체를 받아서 우리가 만들었던 userService의 signup메소드를 수행
/user는 @PreAuthorize 를 통해 USER, ADMIN 이 두 롤 모두 호출할 수 있는 API
/user/{username}은
ADMIN만 호출할 수 있다
=>UserService에서 만들었던
얘
이 3개의 API를 테스트해보기
===
정상리턴
H2 Console보면
이렇게 하는구만?
===
권한이 다른 두 계정을 갖고, 허용권한이 달랐던 두개의 API 테스트
헤더에 토큰을 안넣어서
라고 하면
자동으로 채워져 있는데
authenticate request의 tests탭에서 response를 파싱해서 token필드에 있는 값을 jwt_tutorial_token이라는 변수에 담았었다. 이 변수를 다른 Request에서 사용할 수 있다.
다시 보내면
토큰이 admin계정의 토큰이었어서 다른 계정의 정보를 가져올 수 있었다.
===
회원계정으로 보내기
기존 로그인 API를 복사
send눌러서 토큰을 받으면
여기 써져있던(▽복사한 API이니 이미 써져있다) 변수에 담김
이제
이걸로 요청해보면 403 Forbidden. 저기 Token에 일반 회원것이 들어있어. 권한없음.
403 Forbidden에러는
이게 잘 작동한 것
===
다되는 API는 잘 호출됨
'Spring > SpringSecurity' 카테고리의 다른 글
인증, 인가를 프로젝트에 끼워넣기 (0) 2022.04.23 Spring Boot JWT Tutorial (3) (0) 2022.04.12 Spring Boot JWT Tutorial (2)JWT (0) 2022.04.12 Spring Boot JWT Tutorial (1)설정, DB (0) 2022.04.12