@ServicepublicclassConcertService{privatefinalConcertRepositoryconcertRepository;publicConcertService(ConcertRepositoryconcertRepository){this.concertRepository=concertRepository;}// R) 제목으로 콘서트 가져오기publicConcertResponsegetConcertByTitle(Stringtitle){Optional<Concert>concert=concertRepository.findByTitle(title);ConcertResponseconcertResponse=null;if(concert.isPresent()){concertResponse=ConcertResponse.of(concert.get());}returnconcertResponse;}// C) 콘서트 생성publicConcertcreateConcert(ConcertDtoconcertDto)throwsException{try{Concertconcert=Concert.builder().title(concertDto.getTitle()).genre(concertDto.getGenre()).startDate(concertDto.getStartDate()).endDate(concertDto.getEndDate()).price(concertDto.getPrice()).description(concertDto.getDescription()).runningTime(concertDto.getRunningTime()).build();returnconcertRepository.save(concert);}catch(Exceptione){System.out.println(e.getMessage());thrownewException("잘못된 요청입니다.");}}}
콘서트 생성, 조회( C, R ) 를 구현한 서비스 코드입니다
📍 콘서트 생성
콘서트 정보를 RequestBody로 기입 후 /concert/create POST 요청
DB(RDS)에 콘서트가 생성
📍 콘서트 제목으로 조회
RequestParam에 조회하고자 하는 콘서트이름을 넣어 /concert/title GET 요청
@Slf4j@Configuration@RequiredArgsConstructor@EnableWebSecuritypublicclassSecurityConfig{privatefinalJwtProviderjwtProvider;@BeanpublicSecurityFilterChainfilterChain(HttpSecurityhttp)throwsException{http.csrf((csrf)->csrf.disable())// CORS 설정.cors(c->{CorsConfigurationSourcesource=request->{// Cors 허용 패턴CorsConfigurationconfig=newCorsConfiguration();config.setAllowedOrigins(List.of("*"));config.setAllowedMethods(List.of("*"));returnconfig;};c.configurationSource(source);})// 세션 관리 설정 제거 및 세션 생성 정책 설정.sessionManagement(session->session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))// 조건별로 요청 허용/제한 설정.authorizeRequests(authorize->authorize// 회원가입과 로그인은 모두 승인.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll().requestMatchers(newAntPathRequestMatcher("/member/register","POST")).permitAll().requestMatchers(newAntPathRequestMatcher("/member/login","POST")).permitAll().requestMatchers(newAntPathRequestMatcher("/member/logout","PATCH")).permitAll()// /admin으로 시작하는 요청은 ADMIN 권한이 있는 유저에게만 허용.requestMatchers("/admin/**").hasRole("ADMIN")// /user로 시작하는 요청은 USER 권한이 있는 유저에게만 허용.requestMatchers("/user/**").hasRole("USER").anyRequest().authenticated()// 나머지 요청은 모두 거부// .anyRequest().denyAll())// JWT 인증 필터 적용.addFilterBefore(newJwtAuthenticationFilter(jwtProvider),UsernamePasswordAuthenticationFilter.class)// 에러 핸들링.exceptionHandling(exception->exception.accessDeniedHandler((request,response,accessDeniedException)->{log.error("Access Denied: {}",accessDeniedException.getMessage());// 권한 문제가 발생했을 때 이 부분을 호출한다.response.setStatus(403);response.setCharacterEncoding("utf-8");response.setContentType("text/html; charset=UTF-8");response.getWriter().write("권한이 없는 사용자입니다.");}).authenticationEntryPoint((request,response,authException)->{log.error("Authentication Error: {}",authException.getMessage());// 인증문제가 발생했을 때 이 부분을 호출한다.response.setStatus(401);response.setCharacterEncoding("utf-8");response.setContentType("text/html; charset=UTF-8");response.getWriter().write("인증되지 않은 사용자입니다.");}));returnhttp.build();}@BeanpublicPasswordEncoderpasswordEncoder(){returnPasswordEncoderFactories.createDelegatingPasswordEncoder();}}
publicclassJwtAuthenticationFilterextendsOncePerRequestFilter{privatestaticfinalStringAUTHORIZATION_HEADER="Authorization";privatestaticfinalStringBEARER_TYPE="Bearer";privatefinalJwtProviderjwtProvider;privateObjectMapperobjectMapper;privateUserServiceuserService;privateRedisTemplate<String,String>redisTemplate;privateAuthenticationManagerauthenticationManager;publicJwtAuthenticationFilter(JwtProviderjwtProvider){this.jwtProvider=jwtProvider;}publicJwtAuthenticationFilter(JwtProviderjwtProvider,ObjectMapperobjectMapper,UserServiceuserService,RedisTemplate<String,String>redisTemplate){this.jwtProvider=jwtProvider;this.objectMapper=objectMapper;this.userService=userService;this.redisTemplate=redisTemplate;}publicvoidsetAuthenticationManager(AuthenticationManagerauthenticationManager){this.authenticationManager=authenticationManager;}@OverrideprotectedvoiddoFilterInternal(HttpServletRequestrequest,HttpServletResponseresponse,FilterChainfilterChain)throwsServletException,IOException{Stringtoken=jwtProvider.resolveToken(request);if(token!=null&&jwtProvider.validateToken(token)){// (추가) Redis 에 해당 accessToken logout 여부 확인StringisLogout=(String)redisTemplate.opsForValue().get(token);if(ObjectUtils.isEmpty(isLogout)){// 토큰이 유효할 경우 토큰에서 Authentication 객체를 가지고 와서 SecurityContext 에 저장Authenticationauthentication=jwtProvider.getAuthentication(token);SecurityContextHolder.getContext().setAuthentication(authentication);}// // check access token// token = token.split(" ")[1].trim();// Authentication auth = jwtProvider.getAuthentication(token);// SecurityContextHolder.getContext().setAuthentication(auth);}filterChain.doFilter(request,response);}// Request Header 에서 토큰 정보 추출privateStringresolveToken(HttpServletRequestrequest){StringbearerToken=request.getHeader(AUTHORIZATION_HEADER);if(StringUtils.hasText(bearerToken)&&bearerToken.startsWith(BEARER_TYPE)){returnbearerToken.substring(7);}returnnull;}}