Framework & Library/Spring Security

Spring Security JWT - JWT를 통한 인증

임빈영 2021. 10. 10. 19:42

JWT 검증

JwtAuthorizationFilter 클래스 생성
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {

    private UserRepository userRepository;

    public JwtAuthorizationFilter(AuthenticationManager authenticationManager, UserRepository userRepository) {
        super(authenticationManager);

        this.userRepository = userRepository;
    }

    @Override
    /** 인증이나 권한이 필요한 주소요청이 있을 때 실행되는 메서드 */
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("인증이나 권한이 필요한 주소 요청");

        String jwtHeader = request.getHeader("Authorization");
        System.out.println("jwtHeader : " + jwtHeader);

        /* 클라이언트 측에서 전달받은 JWT가 올바른지 확인 */
        if (jwtHeader == null || !jwtHeader.startsWith("Bearer")) {
            chain.doFilter(request, response);
            return;
        }

        /* 클라이언트 측에서 전달받은 JWT 검증 */
        String jwtToken = request.getHeader("Authorization").replace("Bearer ", "");

        String username = JWT.require(Algorithm.HMAC512("qlsdud0604")).build().verify(jwtToken).getClaim("username").asString();

        /* JWT 검증이 올바르게 된 경우 */
        if (username != null) {
            User userEntity = userRepository.findByUsername(username);

            PrincipalDetails principalDetails = new PrincipalDetails(userEntity);

            Authentication authentication = new UsernamePasswordAuthenticationToken(principalDetails, null, principalDetails.getAuthorities());

            SecurityContextHolder.getContext().setAuthentication(authentication);   // 시큐리티 세션 공간에 Authentication 저장

            chain.doFilter(request, response);
        }
    }
}

 로그인을 한 사용자가 권한이 필요한 페이지에 접근을 할 때 해당 사용자의 JWT를 이용한 인증 과정이 필요하다

 해당 과정을 처리하는 클래스인 JwtAuthorizationFilter 클래스를 jwt 패키지 내에 생성하고 BasicAuthenticationFilter 클래스를 상속받는다.

 BasicAuthenticationFilter 클래스는 시큐리티가 가지고 있는 필터 중 하나이며, 권한이나 인증이 필요한 특정 주소에 접근했을 때 해당 필터를 반드시 거치게 된다.

 doFilterInternal() 메서드에서는 클라이언트 측으로부터 전달받은 JWT를 검증하는 코드가 들어간다.

 JWT의 검증이 정상적으로 완료가 되면 클라이언트는 권한이 필요한 주소에 접근이 가능해진다.

 

JwtAuthorizationFilter 필터 등록

 

ㆍ JwtAuthorizationFilter 클래스가 필터의 역할을 수행하기 위해서는 시큐리티 필터에 등록을 해야 한다.

ㆍ SecurityConfig 클래스에 UserRepository 객체를 선언하고 .addFilter() 메서드를 이용해서 JwtAuthorizationFilter 클래스를 시큐리티 필터에 등록한다.

 

RestApiController 클래스 수정
/* user, manager, admin 권한 접근 가능 */
@GetMapping("/api/v1/user")
public String user() {
    return "user";
}

/* manager, admin 권한 접근 가능 */
@GetMapping("/api/v1/manager")
public String manager() {
    return "manager";
}

/* admin 권한 접근 가능 */
@GetMapping("/api/v1/admin")
public String admin() {
    return "admin";
}

ㆍ JWT 인증을 테스트하기 위해 RestApiController 클래스에 위 메서드들을 추가한다.

ㆍ 각 메서드들과 매핑된 주소에 접근하기 위해서는 서로 다른 권한이 요구된다.


테스트

로그인 요청

 

ㆍ username의 값을 "qlsdud0604"로 password의 값을 "1234"로 지정한 후 JSON 형태로 로그인 요청을 한다.

 

Header 정보 확인

 

ㆍ 로그인 요청을 한 후 서버 측에서 발급한 JWT 정보를 Header를 통해 확인한다.

 

"/api/v1/user" 주소 접근

 

ㆍ 로그인이 된 상태에서 서버로부터 발급받은 JWT를 Header에 포함하여  "/api/v1/user" 주소에 접근한다.

ㆍ ROLE_USER 권한을 가진 사용자는 해당 주소에 접근이 가능한 것을 확인할 수 있다.

 

"/api/v1/manager" 주소 접근

 

ㆍ 로그인이 된 상태에서 서버로부터 발급받은 JWT를 Header에 포함하여  "/api/v1/manager" 주소에 접근한다.

ㆍ ROLE_USER 권한을 가진 사용자는 해당 주소에 접근이 불가능한 것을 확인할 수 있다.

 

"/api/v1/admin" 주소 접근

 

ㆍ 로그인이 된 상태에서 서버로부터 발급받은 JWT를 Header에 포함하여  "/api/v1/admin" 주소에 접근한다.

ㆍ ROLE_USER 권한을 가진 사용자는 해당 주소에 접근이 불가능한 것을 확인할 수 있다.


 

GitHub - qlsdud0604/spring-security-basic: 스프링 시큐리티의 기초를 학습하는 공간

스프링 시큐리티의 기초를 학습하는 공간. Contribute to qlsdud0604/spring-security-basic development by creating an account on GitHub.

github.com

 

728x90