Restful API 구현을 위한 Spring Security 설정해보기 #5, @AuthenticationPrincipal 어노테이션으로 Controller 에서 로그인한 객체 받아오기(feat. @EnableWebSecurity, @EnableGlobalMethodSecurity, @Secured)

안녕하세요.

 

지난 시간에는 401(UnAuthorized)과 403(Forbidden) ExceptionHandler를 Spring Security에서 어떻게 처리하는지에 대해서 알아보았습니다.

 

이때 /auth 라는 Path에 대하여 인증과 권한(ROLE_AUTH)을 부여하여 성공적으로 스프링 시큐리티에서 인증하는 것을 볼 수 있었습니다.

 

https://sas-study.tistory.com/362

 

Restful API 구현을 위한 Spring Security 설정해보기 #4 (AuthenticationEntryPoint, AccessDeniedHandler 구현)

안녕하세요. 지난 시간에는 /auth Path에 대하여 무조건 인증을 해보고 성공적인 Response를 반환받아보는 과정을 겪어보았습니다. https://sas-study.tistory.com/360 Restful API 구현을 위한 Spring Security..

sas-study.tistory.com


이번 포스팅에서는 인증된 객체를 사용할 경우가 많은데요. 예를 들어서, 게시판 글을 등록해야할 때, 로그인 객체를 사용하게 될 수 있습니다.

 

이러한 경우 유용하게 쓸 수 있는 어노테이션을 소개하고자 합니다.

 

@AuthenticationPrincipal 어노테이션

먼저 이 어노테이션을 활용하기 위해서 스프링 시큐리티 설정 클래스를 수정해야합니다.

@Configuration
//이부분 추가해주세요.
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomUserDetailsService customUserDetailsService;
    @Autowired
    private AuthenticationEntryPoint authenticationEntryPoint;
    @Autowired
    private AccessDeniedHandler accessDeniedHandler;
    
..이후 코드 동일

@EnableWebSecurity 어노테이션이 바로 @AuthenticationPrincipal 어노테이션을 통해 Authentication 객체 속에 들어있는 principal 필드를 가져올 수 있게 합니다. 이 부분에 대한 처리는 아래에서 설명드리겠습니다.

 

@EnableGlobalMethodSecurity 어노테이션은 부가적인 내용이지만 Controller 메소드에 직접적으로 Role을 부여할 수 있습니다.

지난 시간 SecurityConfig에 

antMatchers("/api/v1/test/auth").hasRole("AUTH")

다음과 같이 Role을 설정하였는데, 이렇게 되면 설정파일이 굉장히 길어지게 됩니다. 

 

따라서 이부분을 아래의 TestController 클래스의 getTest2() 메소드처럼 @Secured("ROLE_AUTH")를 이용하여 처리할 수 있게 됩니다.

 


 

TestController.java

@Secured("ROLE_AUTH")
    @GetMapping("/auth")
    public Object getTest2(@AuthenticationPrincipal SecurityUser securityUser) 
    		                                                  throws Exception {
        return testService.getTest2(securityUser);
    }

즉 다음과 같이 Controller의 메소드 Parameter 영역에 @AuthenticationPrincipal을 선언하고 SecurityUser 타입 변수를 선언하게되면 스프링 시큐리티 내부 XXXProvider라는 객체에서 Authentication에 저장된 Principal 객체를 꺼내주게 됩니다.

 

헌데 이전시간에 제가 다르게 처리한 부분이 있어서 이를 수정하고 진행해야 합니다.!

 


 

SecurityAuthenticationFilter.java

@Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String username = "test";
        UserDetails authentication = customUserDetailsService.loadUserByUsername(username);
        //이 부분을 수정해주세요. -> authentication.getUsername() -> authentication
        UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(authentication, "test", authentication.getAuthorities());
        
        SecurityContextHolder.getContext().setAuthentication(auth);
        auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        filterChain.doFilter(request, response);
    }

UsernamePasswordAuthenticationToken 객체는 principalcredentials, authorities로 구분되어 있습니다. 

 

이전에는 principal 부분에 UserDetails 객체의 username을 넣어놨었는데, 이 부분을 UserDetails 객체로 넣어놔야 @AuthenticationPrincipal 어노테이션이 가져오는 getPrincipal() 메소드를 호출할 때 SecurityUser로 가져오게 됩니다.

 

이제 결과를 확인해보겠습니다.

 

디버깅을 걸어 확인한 결과 @AuthenticationPrincipal 어노테이션을 설정한 SecurityUser 객체에 loadUserByUsername메소드에서 반환한 객체가 들어있음을 확인할 수 있습니다.

 

감사합니다.!!

댓글

Designed by JB FACTORY