이 분의 강의를 들으면서 작성했다. https://youtu.be/l58zLT5l6BY?si=_Sn2XwiYUtKAaaqi
처음에 JWT토큰을 만들고, 필터를 만든 뒤 config를 만들었다.
package imsh.project.domain.provider;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
@Component
public class JwtProvider {
@Value("${jwt.secretKey}")
private String secretKey;
public String create(String email){
// Instant.now() : 현재 시간 , .plus(1,ChronoUnit.HOURS) : 1시간 추가
Date expiredDate = Date.from(Instant.now().plus(1, ChronoUnit.HOURS)); // 유효시간
String jwt = Jwts.builder()
.signWith(SignatureAlgorithm.ES256,secretKey)
.setSubject(email).setIssuedAt(new Date()).setExpiration(expiredDate)
.compact();
// Jwts.builder(): JWT를 생성하기 위한 빌더 객체를 생성
// signWith(SignatureAlgorithm.ES256, secretKey): JWT에 서명을 추가
// ES256은 ECDSA (Elliptic Curve Digital Signature Algorithm)의 SHA-256 구현을 사용하는 서명 알고리즘
// setSubject(email): JWT의 주제(sub)를 설정 // setIssuedAt(new Date()): 토큰이 발급된 시간
// setExpiration : 유효시간 // compact(): 모든 설정이 완료된 JWT를 생성하고, 문자열로 변환하여 반환
return jwt;
}
public String validate(String jwt){ // JWT 토큰의 유효성 검사
Claims claims = null;
try{
claims = Jwts.parser().setSigningKey(secretKey)
.parseClaimsJws(jwt).getBody();
// Jwts.parser(): Jwts 클래스의 정적 메서드로, JWT 토큰을 파싱
// setSigningKey(secretKey): 토큰을 검증하기 위해 사용되는 비밀 키를 설정
// parseClaimsJws(jwt): 주어진 JWT 토큰을 파싱하고, 토큰이 유효한 서명을 가지고 있는지 확인
// getBody(): 토큰의 본문을 가져옴. 이 부분에는 클레임이 들어 있음
}catch (Exception e){
e.printStackTrace();
return null;
}
return claims.getSubject();
// claims.getSubject(): 클레임에서 서브젝트를 가져와 반환.
// 서브젝트는 일반적으로 토큰이 속한 사용자를 식별하는 데 사용됨.
}
}
package imsh.project.domain.filter;
import imsh.project.domain.provider.JwtProvider;
import lombok.RequiredArgsConstructor;
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 javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtProvider jwtProvider;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try{
String token = parseBearerToken(request);
if(token==null){
filterChain.doFilter(request,response);
return;
}
String email = jwtProvider.validate(token);
if(email == null){
filterChain.doFilter(request,response);
return;
}
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);
//parseBearerToken(request): HTTP 요청 헤더에서 Bearer 토큰을 추출하는 메서드입니다.
//
//if(token==null): 토큰이 없는 경우, 다음 필터로 요청을 전달하고 메서드를 종료합니다.
//
//String email = jwtProvider.validate(token): 추출한 토큰을 검증하고, 유효한 경우 해당 토큰에 포함된 사용자의 이메일을 가져옵니다.
//
//if(email == null): 토큰이 유효하지 않은 경우, 다음 필터로 요청을 전달하고 메서드를 종료합니다.
//
//AbstractAuthenticationToken authenticationToken = ...: 검증이 성공하면, 사용자의 인증 토큰을 생성합니다. 여기서는 UsernamePasswordAuthenticationToken을 사용하고, 권한은 없음(AuthorityUtils.NO_AUTHORITIES)으로 설정합니다.
//
//authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)): 추가적인 인증 세부 정보를 설정합니다.
//
//SecurityContext securityContext = SecurityContextHolder.createEmptyContext(): 빈 SecurityContext를 생성합니다.
//
//securityContext.setAuthentication(authenticationToken): 보안 컨텍스트에 앞에서 생성한 인증 토큰을 설정합니다.
//
//SecurityContextHolder.setContext(securityContext): 현재 스레드의 SecurityContextHolder에 보안 컨텍스트를 설정합니다.
//
//filterChain.doFilter(request, response): 나머지 필터 체인을 계속 진행합니다.
}
private String parseBearerToken(HttpServletRequest request){
String authorization = request.getHeader("Authorization");
boolean hasAuthorization = StringUtils.hasText(authorization);
if(!hasAuthorization) return null;
boolean isBearer = authorization.startsWith("Bearer "); // "Bearer " 로 시작하느냐?
if(!isBearer) return null;
String token = authorization.substring(7);
return token;
}
}
package imsh.project.domain.config;
import imsh.project.domain.filter.JwtAuthenticationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@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()
.antMatchers("/","/api/v1/auth/**","/file/**").permitAll()
.antMatchers(HttpMethod.GET,"/api/v1/board/**","/api/v1/search/**","/api/v1/user/*").permitAll()
.antMatchers().permitAll()
.anyRequest().authenticated().and()
.exceptionHandling().authenticationEntryPoint(new FailedAuthenticationEntryPoint());
httpSecurity.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return httpSecurity.build();
}
}
class FailedAuthenticationEntryPoint implements AuthenticationEntryPoint{
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.getWriter().write("{\"code:\": \"NP\", \"message\": \"Do Not have permission.\"}");
}
}
package imsh.project.domain.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 registry) {
registry.addMapping("/**")
.allowedMethods("*")
.allowedOrigins("*");
}
}
처음엔 모르는 메서드, 클래스를 GPT와 구글링으로 찾아다니면서 이해하려고 했는데
스프링 시큐리티를 하나도 모르는 상태이다 보니깐 일단은 작성하고 이후에 시간을 내서 따로 공부를 하는게 맞다고 느껴졌다.
처음엔 '금방금방 배우고 적용시켜야지' 라는 생각이 강했는데, 그냥 하나 배울 때 제대로 배워야겠다고 생각한다.
노션, 깃, 시큐리티.. 배울게 많네
'프로젝트' 카테고리의 다른 글
[개인 프로젝트] 중고 거래 사이트 만들기. ( ~ 2.5) (0) | 2024.01.15 |
---|---|
Spring Boot 2.7 -> Spring Boot 3.2 마이그레이션 (0) | 2024.01.05 |
Input 컴포넌트 (1) | 2023.12.05 |
컴포넌트 만들기(게시물 목록)-1 (1) | 2023.12.05 |
파일 업로드 (1) | 2023.10.24 |