Spring Boot/Spring Security

[Spring Security] @AuthenticationPrincipal 어노테이션 사용하기

수수한개발자 2022. 8. 17.
728x90

https://techjisu.tistory.com/97  저번글과 이어지는 내용입니다.

 

일반로그인 테스트까지는 성공하였는데 뷰에서 OAuth2로그인을 하여서 모임을 만들려고 하니 

PrincipalDetails를 member로 캐스트 할수없다는 에러가나면서안되었습니다.

 

이제 진짜 어떻게 해야되나..하...

그래서 security를 처음부터 공부하자는 마음으로 전체코드를 처음부터 다시 쭉 보고 있었는데 저번글의 마지막에 썼던내용이 생각났습니다.

 

스프링 시큐리티는 SecurityContext에 인증된 Authentication 객체를 넣어두고 현재 스레드 내에서 공유되도록 관리하고 있는데요.

그래서 스프링 시큐리티가 가지고 있는 UsernamePasswordAuthenticationToken을 이용하여 어댑터 역할로 사용했습니다.여기서 떠오른게 저번글에서 UsernamePasswordAuthenticationToken생성자 파라미터 첫번째에 멤버자체를 넣어줬었습니다. 

이게 왜 이렇게 했냐면 테스트 코드 말고 제 시큐리티 provider도 아래와 같이 되어있었습니다.

 

CustomAuthenticationProvider

package team1.togather.security.provider;

import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import team1.togather.security.auth.PrincipalDetails;
import team1.togather.security.auth.PrincipalDetailsService;

//UserDetails 반환 이 객체를 받아서 추가적인 검증하는 authenticationProvider구현해야함
@RequiredArgsConstructor
public class CustomAuthenticationProvider implements AuthenticationProvider {

    private final PrincipalDetailsService principalDetailsService;

    private final PasswordEncoder passwordEncoder;

    /**
     * 검증을 위한 구현
     * authentication 는 authenticationManager로 부터 받는 인증객체이다. 사용자 정보가 담겨져있다.
     * @param authentication the authentication request object.
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = (String)authentication.getCredentials();

        //userDetailsService 구현한 User를 상속받은 custom 객체 AccountContext
        //CustomUserDetailsService 여기서 accountContext 잘 건내받은거면 아이디는 검증된거다.
        PrincipalDetails principalDetails = (PrincipalDetails) principalDetailsService.loadUserByUsername(username);

        //패스워드가 일치하지 않으면 인증실패
        if(!passwordEncoder.matches(password, principalDetails.getPassword())) {
            throw new BadCredentialsException("BadCredentialsException123123");
        }

        //UsernamePasswordAuthenticationToken 두번쨰 생성자의 인증객체
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(principalDetails.getMembeR(), null, principalDetails.getAuthorities());

        return authenticationToken;
    }

    /**
     * 파라미터로 전달되는 클래스의 타입과 클래스가 사용하고자하는 토큰이 일치할때.
     * @param authentication
     * @return
     */
    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

여기서 PrincipalDetails 은 커스텀한 UserDetails 입니다.

보면 인증객체에 principalDetials.getMember() 로 멤버객체를 저장하고 있었습니다. 

그래서 여기를 pricipalDetials 로 바꿔 주었습니다.

 

UsernamePasswordAuthenticationToken authenticationToken =
        new UsernamePasswordAuthenticationToken(principalDetails, null, principalDetails.getAuthorities());

이러면 OAuth2이든, 폼로그인이든 같은 객체로 받아올수있게 되었습니다.

 

@AuthenticationPrincipal


이 어노테이션은 인증이후 편의적으로 현재 인증된 세션유저를 가져오기 위해 @AuthenticationPrincipal 어노테이션을 통해 UserDetails 인터페이스를 구현한 유저 객체를 주입할 때 사용하는 편입니다.

그래서 위에서 인증객체에는 principalDetails가 들어있습니다.

 

@PostMapping("/new")
public String saveGroupTab(@Valid @ModelAttribute("groupTab") GroupTabRequestDto groupTabRequestDto, BindingResult bindingResult, @AuthenticationPrincipal PrincipalDetails principalDetails) throws IOException {
    if (bindingResult.hasErrors()) {
        log.info("error={}", bindingResult);
        return "groupTabs/createGroupTabForm";
    }
    UploadFile uploadFile = fileStore.storeFile(groupTabRequestDto.getAttachFile()); // UUID에서 리턴받은 파일네임
    groupTabRequestDto.setUploadFile(uploadFile);

    groupTabService.saveGroupTab(groupTabRequestDto.toDto(principalDetails.getMember()));
    return "redirect:/";
}

컨트롤러에서 @AuthenticationPrincipal 어노테이션으로 PrincipalDetails를 받아서 사용하면 됩니다.

728x90

댓글