Spring Security + Filter

문진영·2022년 9월 14일
0

파이널 프로젝트

목록 보기
4/9

CustomAuthorizationFilter.java

아래의 코드는 프로젝트에 사용한 커스텀 필터입니다.
JWT 디코드 및 토큰유효성 검사와 그 결과에 따른

public class CustomAuthorizationFilter extends OncePerRequestFilter { //OncePerRequestFilter가 application에 오는 모든 request를 다 인터셉트함(토큰확인 후 서비스 하기위함)


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if(request.getServletPath().equals("/user/signin") || request.getServletPath().equals("/user/token/refresh") || request.getServletPath().equals("/test/subscribe") ) { //로그인이나 토큰 재발급은 필터를 거칠 필요없어서 바로 보내버린다.
            filterChain.doFilter(request, response);
        } else {
            String authorizationHeader = request.getHeader(AUTHORIZATION);//token의 key를 확인하기위해 header를 불러오는 변수 생성
            if(authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { //header에 값이 있고, Bearer로 시작할떄(client에서 Bearer에 한칸 띄우고 토큰을 보냄, 문자열을 넣는 이유는 우리의 토큰 이라는 것을 인증 하기 위해서)
                try {
                    String token = authorizationHeader.substring("Bearer ".length()); //token만 가져오기 위해서 앞에 적어두었던 문자열 뺴고 변수에 담는다.

                    Algorithm algorithm = Algorithm.HMAC256("secret".getBytes());// 토큰 생성한거와 같은 알고리즘 사용 해야한다(인증을 위해서)

                    JWTVerifier verifier = JWT.require(algorithm).build(); //기존 알고리즘과 같은 JWTVerifier의 verifier 변수에 빌드

                    DecodedJWT decodedJWT = verifier.verify(token); //verifier에 있는 알고리즘으로 token decode한다

                    String email = decodedJWT.getSubject(); //decode해서 유저정보를 얻어오는 변수

                    String role = decodedJWT.getClaim("role").toString(); //roles라는 key값의 value는 유저의 권한이 있으므로 권한을 가져오는 변수

                    Collection<SimpleGrantedAuthority> authorities = new ArrayList<>(); //authorities 변수를 Collection형식으로 생성, Parameter를 SimpleGrantedAuthority로 하는 이유는
                                                                                        // security에서의 권한은 GrantedAuthority 인터페이스를 구현한 객체로 만들면되는데 총 4가지중 문자열만 저장하는 SimpleGrandAuthority사용 하였다
                    authorities.add(new SimpleGrantedAuthority(role));

                    UsernamePasswordAuthenticationToken authenticationToken = //UsernamePasswordAuthenticationToken를 쓰는 이유는 security에 유저정보 전달을 위해서
                            new UsernamePasswordAuthenticationToken(email,null, authorities); //유저정보(email, 권한) 정보를 담은 authenticationToken 변수 생성, password는 필요없어서 null

                    SecurityContextHolder.getContext().setAuthentication(authenticationToken); //갖고있는 정보를 토대로 security가 권한에따라서 접근 할수 있는지 없는지 판단하기 위해 authention으로 설정, SecurityContextHolder는 security가 인증한 내용들을 갖고 있는것

                    filterChain.doFilter(request, response);//추출한 권한별 필터링을 진행해야 하므로 다음 필터로 넘긴다.

                }catch (Exception exception) {

                    if (exception.getMessage().startsWith("The Token's Signature")) { //조작된 토큰 일때
                        log.error("Error logging in: {}", exception.getMessage());
                        response.setHeader("error", "Incorrect Token Do Re-Login");
                    }

                    else if (exception.getMessage().startsWith("The Token has expired")) {//유효기간 만료 시
                        log.error("Error logging in: {}", exception.getMessage());
                        response.setHeader("error", "Token Has Expired Do Refresh");
                    }

                    else {
                        log.error("Error logging in: {}", exception.getMessage());
                        response.setHeader("error", "Unexpected Error...");
                    }

                    response.setStatus(FORBIDDEN.value()); //forbidden error code로 보낸다.

                    Map<String, String> error = new HashMap<>();

                    error.put("error_message", "Token Error"); //문자열과 함께 error메세지 저장

                    response.setContentType(APPLICATION_JSON_VALUE);

                    new ObjectMapper().writeValue(response.getOutputStream(), error); //ObjectMapper
                }
            } else {
                filterChain.doFilter(request, response);
            }
        }
    }
}

SecurityConfig.java

아래의 코드는 프로젝트에 사용한 HttpSecurity Configurer입니다.
cors, csrf설정과, 필터의 순서와 커스텀 필터에서 불러온 권한에 따른 mapping 필터링을 진행하는 코드입니다.

@Configuration //Spring이 설정 파일 or Bean을 만들기 위함
@EnableWebSecurity //FilterChain자동 포함
public class SecurityConfig extends WebSecurityConfigurerAdapter { //WebSecurityConfigurerAdapter Override하기 위해 상속

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable(); //csrf방지
        http.cors(); //서로 출처가 다른 웹 애플리케이션에서 자원을 공유하는 것, react 연동시 proxy 설정

        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);//session사용 안하므로 STATELESS로 끄기

        http.authorizeRequests().antMatchers( "/user/check","/user/token/refresh","/category/**", "/chat/**","/test/subscribe/**", "/kakao/**", "/survey/**", "/quotationSubmit/**", "/matchedList/**", "/matchedgosulist/**").permitAll();
        http.authorizeRequests().antMatchers().hasAuthority("ROLE_USER");
        http.authorizeRequests().antMatchers(GET, "/user/**").hasAuthority("ROLE_USER");
        http.authorizeRequests().antMatchers(POST, "/user/**").permitAll();
        http.authorizeRequests().antMatchers("/gosu/**", "/quotation/**").hasAuthority("ROLE_GOSU");
        http.authorizeRequests().anyRequest().authenticated(); //나머지 리퀘스트들은 인증이 필요하다

        http.addFilterBefore(new CustomAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class); //지정된 필터 앞에 커스텀 필터를 추가 하여 먼저 실행
    }
}
profile
개발 하는 게 좋은 사람입니다.

0개의 댓글