package five.group.server.security;
import five.group.server.auth.*;
import five.group.server.auth.jwt.JwtAuthenticationFilter;
import five.group.server.auth.jwt.JwtTokenizer;
import five.group.server.auth.jwt.JwtVerificationFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
@Configuration
public class SecurityConfiguration {
private final JwtTokenizer jwtTokenizer;
private final MemberAuthority memberAuthority;
public SecurityConfiguration(JwtTokenizer jwtTokenizer, MemberAuthority memberAuthority) {
this.jwtTokenizer = jwtTokenizer;
this.memberAuthority = memberAuthority;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.headers().frameOptions().sameOrigin()
.and()
.cors(Customizer.withDefaults())
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.formLogin().disable()
.httpBasic().disable()
.exceptionHandling()
.authenticationEntryPoint(new MemberAuthenticationEntryPoint())
.accessDeniedHandler(new MemberDeniedHandler())
.and()
.csrf()
.disable()
.apply(new CustomFilterConfigurer())
.and()
.authorizeHttpRequests(authorize -> authorize
.antMatchers(HttpMethod.GET,"/members").hasRole("USER")
.antMatchers(HttpMethod.PATCH,"/members").hasRole("USER")
.antMatchers(HttpMethod.DELETE,"/members").hasRole("USER")
.antMatchers(HttpMethod.POST,"/questions").hasRole("USER")
.antMatchers(HttpMethod.PATCH,"/questions/*").hasRole("USER")
.antMatchers(HttpMethod.DELETE,"/questions/*").hasRole("USER")
.antMatchers(HttpMethod.POST,"/answers/*").hasRole("USER")
.antMatchers(HttpMethod.PATCH,"/answers/*").hasRole("USER")
.antMatchers(HttpMethod.DELETE,"/answers/*").hasRole("USER")
.antMatchers(HttpMethod.POST,"/comments/*").hasRole("USER")
.antMatchers(HttpMethod.PATCH,"/comments/*").hasRole("USER")
.antMatchers(HttpMethod.DELETE,"/comments/*").hasRole("USER")
.anyRequest().permitAll()
);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
CorsConfigurationSource corsConfigurationSource() { // CORS 필터 처리
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:8080","http://localhost:3000"));
configuration.setAllowedMethods(Arrays.asList("GET","PATCH","DELETE","POST","OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
public class CustomFilterConfigurer extends AbstractHttpConfigurer<CustomFilterConfigurer, HttpSecurity> {
@Override
public void configure(HttpSecurity builder) throws Exception {
AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);
JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager, jwtTokenizer);
jwtAuthenticationFilter.setAuthenticationSuccessHandler(new MemberAuthenticationSuccessHandler());
jwtAuthenticationFilter.setAuthenticationFailureHandler(new MemberAuthenticationFailureHandler());
JwtVerificationFilter jwtVerificationFilter = new JwtVerificationFilter(jwtTokenizer,memberAuthority);
builder
.addFilter(jwtAuthenticationFilter)
.addFilterAfter(jwtVerificationFilter, JwtAuthenticationFilter.class);
}
}
}
전체 글
- 1 2023.08.21
- JAVA Spring API 계층 2023.07.02
- 2023-06-18 회고 (3) 2023.06.18
1
JAVA Spring API 계층
JAVA Spring API 계층
-. Controller
-. DTO
EntryPoint
@SpringBootApplication
@Component // (1)
@Configuration // (2)
(1) 어플리케이션 내 @Component가 붙은 클래스를 검색한 후 Spring Bean 으로 등록함
(2) 어플리케이션 내 @Configuration 이 붙은 클래스를 자동으로 찾아주고 추가적으로 Spring Bean으로 등록하는 기능을 활성화
1. Controller
@RestController // (1)
@RequestMapping("/v1/members/") // (2)
// public class ...
(1) 해당 어노테이션을 추가한 클래스는 Rest API의 리소스를 처리하기 위한 API EndPoint로 정의함
Spring Bean으로 등록함
(2) 핸들러 메서드(Handler Method)를 매핑해주는 역활을 수행함
(ex : localhost:8080/v1/members 으로 접근 가능함)
1-1. Handler Method (Post)
// public class ...
@PostMapping // (1)
public Stirng postMember(@RequestParam ("name") String name) (2)
(1) 클라이언트의 요청데이터를 서버에 생성(Post)할때 사용되는 어노테이션
HTTP Method 타입을 맞춰줘야 함 (Postman에서 HTTP타입 POST으로 요청할 때 대응하는 클래스의 어노테이션)
(ex : POST http://localhost:8080/v1/members)
(2) "name" 이라는 요청 파라미터를 name 이라는 매개변수를 생성 후 연결해 준다
1-2. Handler Method (Get)
@GetMapping("/{member-id}") // (1)
public String getMember(@PathVariable("member-id") long memberId) // (2)
(1) 클라이언트의 요청데이터를 조회(Get)할때 사용되는 어노테이션
(1) 괄호 안의 매개변수의 이름과 (2)의 URL 경로가 일치해야 함
(2) @PathVariable 어노테이션을 사용하면 URL 경로의 특정 부분을 매개변수로 받아올 수 있다
이때, @PathVariable 어노테이션은 매개변수의 이름("/{member-id}") 과 URL 경로의 일부("member-id")가
일치해야 값을 바인딩할 수 있다
2. ResponseEntity
@PostMapping
public ResponseEntity postMember(@RequestParam ("email") String email) //(1)
Map<String, String> map = new HashMap<>();
map.put("email", email);
retune new ResponseEntity<>(map, Httpstatus.CREATED); // (2)
(1) ResponseEntity란 일반적으로 스프링 MVC 또는 스프링 WebFlux를 사용하여 웹 애플리케이션을 개발할 때,
Controller의 메서드에서 클라이언트에게 응답을 반환할 때 ResponseEntity를 사용합니다.
ResponseEntity는 HTTP 응답의 상태 코드, 헤더 및 본문 데이터를 포함할 수 있으며, 다양한 유형의 응답을 처리할 수 있습니다.
(2) ResponseEntity는 다음과 같은 기능을 제공합니다:
상태 코드 설정: ResponseEntity를 사용하여 응답의 상태 코드를 설정할 수 있습니다.
(상태코드 종류 : https://developer.mozilla.org/ko/docs/Web/HTTP/Status 참조)
헤더 설정: ResponseEntity를 사용하여 응답의 헤더를 설정할 수 있습니다.
본문 데이터 설정: ResponseEntity를 사용하여 응답의 클라이언트에게 반환될 본문 데이터를 설정할 수 있습니다.
일반적으로 JSON, XML, HTML 등의 형식으로 데이터를 반환할 수 있습니다.
(위 코드에선 Map 객체를 이용하여 JSON타입으로 변환 후 반환)
(ResponseEntity에 대한 참고자료 : https://itvillage.tistory.com/44)
2.DTO
Data Transfer Object의 약자로 데이터패턴 중 하나
근본적인 목적은 Http 요청을 최소화 할 수 있음
@PatchMapping("/{member-id}")
public ResponseEntity patchMember(@PathVariable("member-id") long memberId,
@RequestParam String phone) {
Map<String, Object> body = new HashMap<>();
body.put("memberId", memberId);
body.put("email", "icc99031@gmail.com");
body.put("name", "hyeonwoo");
body.put("phone", "010-1111-2222");
return new ResponseEntity<>(body, HttpStatus.CREATED);
}
(DTO 적용 전)
@PostMapping
public ResponseEntity postMember(@RequestBody MemberPostDto memberPostDto) {
return new ResponseEntity<>(memberPostDto, HttpStatus.CREATED);
}
public class MemberPostDto {
private String email;
private String name;
private String phone;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
DTO 적용 후
하나의 객체로 선언하여 해당 기능에 대한 모든 데이터를 이 객체를 통하여 전달받을 수 있음
클라이언트와 데이터를 주고받을 때 String -> Json으로 변환하거나 하는 역할을 수행
추가로 유효성 검사 역할도 수행할 수 있음
2-1. 직렬화, 역직렬화
JSON 직렬화 : Java 객체 -> JSON
JSON 역직렬화 : JSON -> Java 객체
2-2. 유효성 검증
Dto 객체에 유효성 검증 로직을 추가할 수 있음
build.gradle에 의존 라이브러리를 추가
implementation 'org.springframework.boot:spring-boot-starter-validation'
public class MemberPostDto {
@Email
@NotBlank
private String email;
@NotBlank(message = "이름은 공백이 아니어야 합니다.")
private String name;
@Pattern(regexp = "^010-\\d{3,4}-\\d{4}$",
message = "휴대폰 번호는 010으로 시작하는 11자리 숫자와 '-'로 구성되어야 합니다.")
private String phone;
}
등의 유효성 검증에 필요한 어노테이션을 사용할 수 있음.
정규 표현식에 대한 자료 (https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Regular_Expressions)
@PostMapping
public ResponseEntity postMember(@Valid @RequestBody MemberPostDto memberPostDto) {
^
return new ResponseEntity<>(memberPostDto, HttpStatus.CREATED);
}
유효성 검증을 실행하기 위해선 Controller에서 @Valid 어노테이션을 추가해야 함
@Validated
public class MemberController_v3 {
...
@PatchMapping("/{member-id}")
public ResponseEntity patchMember(@PathVariable("member-id") @Min(1) long memberId,
@Valid @RequestBody MemberPatchDto memberPatchDto) {
@PathVariable 이 유효성 검증을 실행하려면 클래스 레벨에 @Validate 어노테이션이 필요함
(@Min(1) = 1이상의 숫자일 것, memberId >= 1 )
-. 보충수업 필요 목록
* @RequestBody, @ResponseBody 개념
* 유효성 검증에 있어 커스텀 어노테이션 정의 (Custom Vaildator)
* RestClient (중요도 Lv.3)
'Solo Study' 카테고리의 다른 글
2023-05-16 Coding Test (1) (1) | 2023.05.16 |
---|
2023-06-18 회고 (3)
코드스테이츠 백엔드 부트캠프과정을 시작하고서 약 2달가량 지났다
지금까지 신나게 머리깨지며 배웠던 것들은 사실 계란으로 머리를 치고있었고
스프링 섹션이 시작하니 매일같이 뚝배기로 얻어맞고 있는듯한 기분이 들고있다
겉으로는 뭐 잘 따라가는 것 처럼 보일진 모르겠지만
내 뇌를 속을 열어보면 이게 왜 이렇게되는건지 전혀 모르겠다
코테 문제(기본중의 기본)좀 풀면서 이렇게하면 되겠지 하며 자위하고있던게
본격적으로 상속을 다루고 하다보니 자바 기본문법은 당연히 중요하지만
내가 너무 만만하게 생각했었던 것 같다
컬렉션부분을 꾸준히 복습해야겠다
그렇다고 지금 배우는 과정이 재미없는것은 또 아니다
드디어 아기다리 고기다리던 DB를 배우고있으며
인메모리 데이터베이스이지만 드디어 본격적으로 무언가 시작되는구나 하며
재미는 있다
재미는 있지만 내 늙은뇌의 이해력으로는 따라가기가 벅차다
그렇다고 공부를 하고있느냐?
공부는 안하고 당장에 눈앞에 놓인 수익을 쫓아가고 있다
아ㅋㅋ 이미 내 캘린더에는 수업과정보단 민팅일정이 그득하다고
그래도 이왕 시작한거
내 대가리가 먼저 깨지나 너가 먼저 함락당하냐 한번 해보자고
사실 백엔드 지원한 계기가 스프링을 배우고 싶어서가 아니라
자바가 베이스라고 생각이들어 지원한 것이지 스프링을 배우니까
백엔드를 지원한것은 아니였다
10월에 부트캠프 수료하고 나면 일본으로 건너가서
24년 4월학기부터 2년정도 AI 교육과정 전문학교에 들어갈까 생각도 든다
(26년이면 나이가 몇살이되는거야 씨불...거 아 만나이로치니 아직 삼십대 초반^^)
수료하고나서 자격증도 있으니 국내 일본계기업으로 들어가는것도
괜찮을 것 같지만 고졸따리인 내게 과연 기회를 줄까 싶기도 하고
일단 8월 9월 되면 계획이 좀 더 상세히 그려질 것 같으니 그때 생각해봐야겠다
그냥 이대로 본격적으로 느프트 관련해서 활동하며 레퍼럴뿌려가면서
운좋으면 앰버서더제의까지 들어와주면 그냥 그렇게 먹고살고싶긴하다
아 불로소득 그 놈 존나갖고싶다~~!
'Remembrance' 카테고리의 다른 글
2023-05-24 回顧 (0) | 2023.05.24 |
---|---|
2023-05-15 悩み (1) | 2023.05.15 |
2023-04-24 [코드스테이츠] 회고 (0) | 2023.04.24 |