Programming/Web Projects
SPRING+REACT+MYSQL 프로젝트 (5, 6, 7, 8, 9)
서린이1
2024. 9. 7. 23:16
1. react 프로젝트 생성 (board-frontend)
- npx create-app react --template typescript
- App.tsx 초기화
- index.tsx 초기화
- src 하위 폴더 구조 생성
2. Authentication 방식
1. Basic Authentication
- 사용자 이름 / 비밀번호를 Base64로 인코딩하여 Authorizaiton 헤더에 포함하여 전송
- 매우 안전하지 않음, SSL/TLS와 함께 사용됨
Ex: Authorization: Basic ~~~
2. Bearer Token Authentication
- 헤더에 토큰을 포함하여 전송 Authorization 헤더에 포함하여 전송
- JWT을 사용하여 인증
- 간단한 방식, 상태를 유지하지 않음, 확장성이 높음
- 단점 : 토큰 노출 위험, 토큰 관리가 힘듬, 귀찮음
Ex: Authorizaiton: Bearer ~~~~~
3. OAuth 방식
- 토큰기반 인증 방식, 사용자가 직접 자격을 증명 X 미리 인증 받아서 토큰을 발급 받고
- 이 토큰을 이용하여 API를 요청하는 방식, OAuth 2.0
Ex: Kakao, Naver, Git, Facebook login ..
4. API Key 방식
- 발급 받은 키를 요청
5. Session based Authentication
- Session ID를 생성하여 세션이나 쿠키에 포함하여 인증
- JWT (JSON Web Token) : 클레임이라고 불리는 정보를 JSON 형태로 안전하게 전송하기 위한 토큰
- 인증과 정보 교환에 사용, 서명이 되어 있어서 신뢰성 확보가 가능
1. Header : 토큰의 타입과 사용된 알고리즘 정보를 담고 있음, Base64 URL로 인코딩
2. Payload : 클레임 정보, 대상, 발행자, 만료 시간 등 다양한 정보가 포함, Base64 URL로 인코딩
3. Signature : Header와 Payload, Secret Key를 사용하여 생성된 서명
인증, 정보교환
장점 :
상태유지하지 않는다. = stateless, 서버가 클라이언트의 상태를 유지안한다
간단하고 자기 포함적
확장성이 높다, 토큰을 만들어 놓으면 여러 시스템에서 사용 가능
단점 :
크기 : 클레임이 많을수록 토큰의 크기가 커짐
보안 : 서명은 되어있지만, 암호화는 되어있지 않음, 중요한 정보를 JWT에 포함할 수 없음
토큰 관리 : 만료 시간, 갱신 잘해줘야 함
3. Spring Initializr로 Gradle Project 생성 (Gradle 2.7.14)
- src/main/java/test 하위 삭제및 src/main/java 하위 폴더 구조 잡기
- applicaiton.properties 설정
- JwtProvider.java 생성
package kr.co.sorin.board_backend.provider;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
@Component
public class JwtProvider {
private String secretKey = "S3cr3tK3y";
public String create(String email) {
Date expiredDate = Date.from(Instant.now().plus(1, ChronoUnit.HOURS));
String jwt = Jwts.builder()
.signWith(SignatureAlgorithm.HS256, secretKey) // 수정: SignatureAlgorithm.ES256 ->
.setSubject(email)
.setIssuedAt(new Date())
.setExpiration(expiredDate)
.compact();
return jwt;
}
public String validate(String jwt) {
Claims claims = null;
try {
claims = Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(jwt)
.getBody();
} catch (Exception e) {
e.printStackTrace();
return null;
}
return claims.getSubject();
}
}
- WebSecurityConfig.java 생성
package kr.co.sorin.board_backend.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import kr.co.sorin.board_backend.filter.JWTAuthenticationFilter;
import lombok.RequiredArgsConstructor;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class WebSecurityConfig {
private final JWTAuthenticationFilter jwtAuthenticationFilter;
@Bean
protected SecurityFilterChain configure(HttpSecurity HttpSecurity) throws Exception {
HttpSecurity
.cors().and()
.csrf().disable()
.httpBasic().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.anyRequest().permitAll();
}
}
- JwtAuthenticationFilter.java 생성
package kr.co.sorin.board_backend.filter;
import java.io.IOException;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import kr.co.sorin.board_backend.provider.JwtProvider;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtProvider jwtProvider;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String token = parseBearerToken(request);
try {
if (token == null) {
filterChain.doFilter(request, response);
return;
}
String email = jwtProvider.validate(token);
if (email == null) {
filterChain.doFilter(request, response);
return;
}
// context에 등록
AbstractAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(email, null,
AuthorityUtils.NO_AUTHORITIES);
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); // 웹 인증정보 세부정보
// 셋팅
SecurityContext SecurityContext = SecurityContextHolder.createEmptyContext();
SecurityContext.setAuthentication(authenticationToken);
SecurityContextHolder.setContext(SecurityContext); // 외부에서 사용 가능
} catch (Exception e) {
e.printStackTrace();
}
filterChain.doFilter(request, response); // 다음 필터로 request를 넘긴다.
}
private String parseBearerToken(HttpServletRequest request) {
String authorization = request.getHeader("Authorization");
boolean hasAuthorization = StringUtils.hasText(authorization);
if (!hasAuthorization)
return null;
boolean isBearer = authorization.startsWith("Bearer ");
if (!isBearer)
return null;
String token = authorization.substring(7);
return token;
}
}
- WebSecurityConfig.java 작성
package kr.co.sorin.board_backend.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import kr.co.sorin.board_backend.filter.JwtAuthenticationFilter;
import lombok.RequiredArgsConstructor;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class WebSecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
protected SecurityFilterChain configure(HttpSecurity HttpSecurity) throws Exception {
HttpSecurity
.cors().and()
.csrf().disable()
.httpBasic().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.anyRequest().permitAll();
}
}
- CorsConfig.java 작성
package kr.co.sorin.board_backend.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings (CorsRegistry corsRegistry) {
corsRegistry
.addMapping("/**")
.allowedMethods("*")
.allowedOrigins("*");
}
}