Skip to content

Commit

Permalink
✨ Security 의존성 추가, securityConfig, jwtFilter, jwtProvider 생성
Browse files Browse the repository at this point in the history
  • Loading branch information
min-0 committed Aug 19, 2024
1 parent 915dee4 commit 4ce172d
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 0 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ dependencies {
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.3'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.3'

implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
Expand Down
48 changes: 48 additions & 0 deletions src/main/java/com/dnd/dndtravel/auth/config/JwtFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.dnd.dndtravel.auth.config;

import io.jsonwebtoken.io.IOException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.GenericFilterBean;

@RequiredArgsConstructor
public class JwtFilter extends GenericFilterBean {

public static final String ACCESS_HEADER= "Authorization";
private final JwtProvider jwtProvider;

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException, java.io.IOException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String token = resolveToken(httpServletRequest);
String requestURI = httpServletRequest.getRequestURI();

if (StringUtils.hasText(token) && jwtProvider.validateToken(token)) {
Authentication authentication = jwtProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
else {
System.out.println("유효한 jwt 토큰이 없습니다. uri: " + requestURI);
}

filterChain.doFilter(servletRequest, servletResponse);
}

// Request Header 토큰 정보 추출
private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader(ACCESS_HEADER);

if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}

return null;
}
}
101 changes: 101 additions & 0 deletions src/main/java/com/dnd/dndtravel/auth/config/JwtProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.dnd.dndtravel.auth.config;

import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SecurityException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;

import java.security.Key;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.stream.Collectors;

@Component
public class JwtProvider implements InitializingBean {

private static final String AUTHORITIES_KEY = "memberId";
private final long tokenValidityInMilliseconds;
private final String secretKey;
private Key key;

public JwtProvider(
@Value("${JWT_SECRET_KEY}") String secretKey,
@Value("86400") long tokenValidityInSeconds) {
this.secretKey = secretKey;
this.tokenValidityInMilliseconds = tokenValidityInSeconds * 1000;
}

// secretKey 값을 Base64 Decode 해서 key 변수에 할당
@Override
public void afterPropertiesSet() {
byte[] keyBytes = Decoders.BASE64.decode(secretKey);
this.key = Keys.hmacShaKeyFor(keyBytes);
}

// JWT Access 토큰 생성
public String createToken(Authentication authentication) {
Long memberId = Long.parseLong(authentication.getName());
long now = (new Date()).getTime();
Date validity = new Date(now + this.tokenValidityInMilliseconds); //1일

return Jwts.builder()
.setSubject(String.valueOf(memberId))
.claim(AUTHORITIES_KEY, memberId)
.signWith(key, SignatureAlgorithm.HS256)
.setExpiration(validity)
.compact();
}

// Refresh 토큰 생성
public String createRefreshToken(Long memberId) {
long now = (new Date()).getTime();
Date validity = new Date(now + this.tokenValidityInMilliseconds * 14); // 14일

return Jwts.builder()
.setSubject(String.valueOf(memberId))
.signWith(key, SignatureAlgorithm.HS256)
.setExpiration(validity)
.compact();
}

// authentication 객체 생성
public Authentication getAuthentication(String token) {
Claims claims = Jwts.parser()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody();
Collection<? extends GrantedAuthority> authorities =
Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());

return new UsernamePasswordAuthenticationToken(claims.get(AUTHORITIES_KEY), token, authorities);
}

// Token 유효성 검증
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(key).build().parseClaimsJws(token);
return true;
} catch (SecurityException | MalformedJwtException e) {
System.out.println("잘못된 JWT Signature");
} catch (ExpiredJwtException e) {
System.out.println("만료된 JWT 토큰");
} catch (UnsupportedJwtException e) {
System.out.println("지원 되지 않는 JWT 토큰");
} catch (IllegalArgumentException e) {
System.out.println("잘못된 JWT 토큰");
}

return false;
}
}
35 changes: 35 additions & 0 deletions src/main/java/com/dnd/dndtravel/auth/config/JwtSecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.dnd.dndtravel.auth.config;

import lombok.RequiredArgsConstructor;
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 org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class JwtSecurityConfig {

private final JwtProvider jwtProvider;

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeHttpRequests()
.requestMatchers("/login/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(new JwtFilter(jwtProvider), UsernamePasswordAuthenticationFilter.class);

return http.build();
}
}

0 comments on commit 4ce172d

Please sign in to comment.