본문 바로가기
프로젝트/SpringSecurity

[Spring Security] 17. JWT 받아서 유효성 검사하기

by dantriss 2023. 7. 20.

유튜브에서 스프링시큐리티를 심화교육하면서 조금더 레벨업 하고자 한다.

IDE : IntelliJ

언어 : Java 8

스프링부트 버전 : 2.7.13

DB : MySQL

빌드관리 도구 : Maven

OS : iOS

참고유튜브 : 메타코딩


지난번에 클라이언트측에서 JWT을 응답받아서 해당정보로 유저의 정보를 db에서 찾아내서 세션에 저장까지 했었다.

이제 해당유저가 가진 권한에 따라서 접근할 수 있는 주소가 다른데 어떻게 진행이 되는지 알아보자

JwtAutorizationFilter 클래스를 생성한다.

 

BasicAuthenticationFilter를 오버라이딩

 

commend + n을 눌러서 생성자를 생성해준다.

 

테스트를 진행하기 위해 해당 필터로 값이 넘어오는지 콘솔에 찍어보고

 

securityConfig로 이동해서 MyCustomDsl에 필터를 추가해준다.

 

저번에 테스트로 진행했던 myFilter3는 삭제하고 MyCustomDsl은 잘 들어가있는지 확인해준다.

값을 아무렇게 넣어주면 콘솔에 미리 지정한대로 출력이된다. 

즉 값을 입력하면 해당 필터로 이동한다는 뜻.

 

다시 JwtAutorizationFilter로 이동해서 doFilterInternal을 오버라이딩 해준다.

 

헤더에서 Authorization이라는 key를 가진 value를 출력해본다

 

값을 넣어준대로 잘 응답받는다.

저번 포스트에서 Authorization을 key로 가진 "Bearer ~~~" value를 보내주면 클라이언트측에서 JWT을 받는것이다.

 

클라이언트측에서 로그인요청을 했을때 서버측에서 header에 담아서 보내준 토큰을 복사해서 value에 넣고 보내보면

test와 마찬가지로 당연하게 값이 넘어오는걸 확인할 수 있다.

 

조건문을 활용해 응답받은 헤더가 값이 없거나 Bearer로 시작하지 않는다면 해당 사용자는 유효하지 않은 접근이므로 다음필터로 넘겨준다.

 

Authorization을 key로가진 Bearer 111111 처럼된 구성을 11111로만 바꿔서 jwtToken에 저장해준다.

 

JWT에서 서버만 알고있는 비밀키를 이용해서 회원아이디를 추출해 username에 저장해준다.

 

이제 추출한 회원아이디로 db에 검색을 해봐서 해당 유저가 db에 저장되어 있는 유저인지 확인해보기 위해서 PrincipalDetailsService에 있는 loadUserByUsername 함수를 사용하기 위해서  

SecurityConfig로 이동해서 userRepository를 파라메터로 넣어준다.

생성자에서 userRepository를 받아주면된다.

 

그렇게해서 username에 값이 저장되었다면 username을 기준으로 db에서 회원의 아이디를 기준으로 회원의 정보를 userEntity에 저장

해당 회원의 정보를 PrincipalDetails에 넣어준 후 Authentication 객체를 만들어 시큐리티 세션에 해당 객체를 저장해준다.

public class JwtAutorizationFilter extends BasicAuthenticationFilter {

    private UserRepository userRepository;

    public JwtAutorizationFilter(AuthenticationManager authenticationManager, UserRepository userRepository) {
        super(authenticationManager);
        this.userRepository = userRepository;

    }
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {

        String jwtHeader = request.getHeader("Authorization");

        if(jwtHeader ==null || !jwtHeader.startsWith("Bearer")){
            chain.doFilter(request,response);
            return;
        }

        String jwtToken = request.getHeader("Authorization").replace("Bearer ","");

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

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

            chain.doFilter(request,response);

        }

    }
}

 

이제 가지고 있는 권한대로 접근이 막아지는지 확인해보기 위해서 RestApiController에 아래에 3가지처럼 만든다.

 

접근권한이 없는 아이디 비밀번호로 회원들만 접근이 가능한 주소로 접근을 시도하면 403 에러가 뜨면서 접근을 막는다.

 

그에반해 회원이라면 해당 주소로 접근이 가능하다.

 

하지만 일반유저가 manager권한이 없는데 manager권한을 가진 유저만 접근이 가능한 주소로 접근을 시도하면 역시 403 에러가 뜨면서 접근할 수 가 없다.

 


 

JwtAuthenticationFilter에서 다음과 같이 코드를 작성하고나면 나중에 관리하기가 복잡하고 실수가 있을 수 도 있으니 조금더 깔끔하게 정리를 위해서

 

JwtProperties 인터페이스를 생성해서 관리해주자

 

보기에도 깔끔할 뿐더러 나중에 토큰의 만료시간이나 개인키를 변경한 후에 일일이 찾아서 바꿔주지 않아도 되기때문에 관리하기 편하다

댓글