-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
656 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,50 @@ | ||
plugins { | ||
id 'java' | ||
id 'org.springframework.boot' version '3.0.11' | ||
id 'io.spring.dependency-management' version '1.1.3' | ||
id 'java' | ||
id 'org.springframework.boot' version '3.0.11' | ||
id 'io.spring.dependency-management' version '1.1.3' | ||
} | ||
|
||
group = 'com.server.dosopt' | ||
version = '0.0.1-SNAPSHOT' | ||
|
||
java { | ||
sourceCompatibility = '17' | ||
sourceCompatibility = '17' | ||
} | ||
|
||
configurations { | ||
compileOnly { | ||
extendsFrom annotationProcessor | ||
} | ||
compileOnly { | ||
extendsFrom annotationProcessor | ||
} | ||
} | ||
|
||
repositories { | ||
mavenCentral() | ||
mavenCentral() | ||
} | ||
|
||
dependencies { | ||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa' | ||
implementation 'org.springframework.boot:spring-boot-starter-web' | ||
compileOnly 'org.projectlombok:lombok' | ||
annotationProcessor 'org.projectlombok:lombok' | ||
testImplementation 'org.springframework.boot:spring-boot-starter-test' | ||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa' | ||
implementation 'org.springframework.boot:spring-boot-starter-web' | ||
compileOnly 'org.projectlombok:lombok' | ||
annotationProcessor 'org.projectlombok:lombok' | ||
testImplementation 'org.springframework.boot:spring-boot-starter-test' | ||
|
||
// database | ||
// database | ||
// runtimeOnly 'com.h2database:h2' | ||
implementation 'mysql:mysql-connector-java:8.0.32' | ||
implementation 'mysql:mysql-connector-java:8.0.32' | ||
|
||
//security | ||
implementation 'org.springframework.boot:spring-boot-starter-security' | ||
testImplementation 'org.springframework.security:spring-security-test' | ||
//security | ||
implementation 'org.springframework.boot:spring-boot-starter-security' | ||
testImplementation 'org.springframework.security:spring-security-test' | ||
|
||
// AWS sdk | ||
implementation("software.amazon.awssdk:bom:2.21.0") | ||
implementation("software.amazon.awssdk:s3:2.21.0") | ||
|
||
implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.5' | ||
implementation group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5' | ||
implementation group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5' | ||
} | ||
|
||
tasks.named('test') { | ||
useJUnitPlatform() | ||
useJUnitPlatform() | ||
} |
53 changes: 53 additions & 0 deletions
53
ThirdSeminar/src/main/java/com/server/dosopt/seminar/config/AWSConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package com.server.dosopt.seminar.config; | ||
|
||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import software.amazon.awssdk.auth.credentials.SystemPropertyCredentialsProvider; | ||
import software.amazon.awssdk.regions.Region; | ||
import software.amazon.awssdk.services.s3.S3Client; | ||
|
||
@Configuration | ||
public class AWSConfig { | ||
|
||
private static final String AWS_ACCESS_KEY_ID = "aws.accessKeyId"; | ||
private static final String AWS_SECRET_ACCESS_KEY = "aws.secretAccessKey"; | ||
|
||
// @Value("${aws-property.access-key}") --> 이렇게도 가능 | ||
private final String accessKey; | ||
private final String secretKey; | ||
private final String regionString; | ||
|
||
public AWSConfig(@Value("${aws-property.access-key}") final String accessKey, | ||
@Value("${aws-property.secret-key}") final String secretKey, | ||
@Value("${aws-property.aws-region}") final String regionString) { | ||
this.accessKey = accessKey; | ||
this.secretKey = secretKey; | ||
this.regionString = regionString; | ||
} | ||
|
||
|
||
// 시스템에 환경변수로 키를 등록 | ||
// 자격증명을 얻는 여러 방법 중 하나! | ||
@Bean | ||
public SystemPropertyCredentialsProvider systemPropertyCredentialsProvider() { | ||
System.setProperty(AWS_ACCESS_KEY_ID, accessKey); | ||
System.setProperty(AWS_SECRET_ACCESS_KEY, secretKey); | ||
return SystemPropertyCredentialsProvider.create(); | ||
} | ||
|
||
@Bean | ||
public Region getRegion() { | ||
return Region.of(regionString); | ||
} | ||
|
||
// S3Client : S3에 요청을 보내는 객체 | ||
// S3Client를 Spring Bean에 등록 | ||
@Bean | ||
public S3Client getS3Client() { | ||
return S3Client.builder() | ||
.region(getRegion()) | ||
.credentialsProvider(systemPropertyCredentialsProvider()) | ||
.build(); | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
ThirdSeminar/src/main/java/com/server/dosopt/seminar/config/BCryptPasswordConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package com.server.dosopt.seminar.config; | ||
|
||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | ||
import org.springframework.security.crypto.password.PasswordEncoder; | ||
|
||
@Configuration | ||
public class BCryptPasswordConfig { | ||
|
||
// salt 할 때 보안 강도를 어느정도로 할지 설정 | ||
// 높을수록 세지는데, 어느정도 높아지면 비슷함 | ||
// default가 10 | ||
private static final int STRENGTH = 10; | ||
|
||
@Bean | ||
public PasswordEncoder bCryptPasswordEncoder() { | ||
return new BCryptPasswordEncoder(STRENGTH); | ||
} | ||
} |
41 changes: 37 additions & 4 deletions
41
ThirdSeminar/src/main/java/com/server/dosopt/seminar/config/SecurityConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
...Seminar/src/main/java/com/server/dosopt/seminar/config/jwt/CustomAccessDeniedHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.server.dosopt.seminar.config.jwt; | ||
|
||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import org.springframework.security.access.AccessDeniedException; | ||
import org.springframework.security.web.access.AccessDeniedHandler; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.io.IOException; | ||
|
||
@Component | ||
public class CustomAccessDeniedHandler implements AccessDeniedHandler { | ||
@Override | ||
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { | ||
setResponse(response); | ||
} | ||
|
||
private void setResponse(HttpServletResponse response) { | ||
response.setStatus(HttpServletResponse.SC_FORBIDDEN); | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
...src/main/java/com/server/dosopt/seminar/config/jwt/CustomJwtAuthenticationEntryPoint.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package com.server.dosopt.seminar.config.jwt; | ||
|
||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import org.springframework.security.core.AuthenticationException; | ||
import org.springframework.security.web.AuthenticationEntryPoint; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
public class CustomJwtAuthenticationEntryPoint implements AuthenticationEntryPoint { | ||
|
||
@Override | ||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) { | ||
setResponse(response); | ||
} | ||
|
||
private void setResponse(HttpServletResponse response) { | ||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); | ||
} | ||
} |
52 changes: 52 additions & 0 deletions
52
ThirdSeminar/src/main/java/com/server/dosopt/seminar/config/jwt/JwtAuthenticationFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package com.server.dosopt.seminar.config.jwt; | ||
|
||
import jakarta.servlet.FilterChain; | ||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import lombok.NonNull; | ||
import lombok.RequiredArgsConstructor; | ||
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 java.io.IOException; | ||
|
||
import static com.server.dosopt.seminar.domain.JwtValidationType.VALID_JWT; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
public class JwtAuthenticationFilter extends OncePerRequestFilter { | ||
|
||
private final JwtTokenProvider jwtTokenProvider; | ||
|
||
@Override | ||
protected void doFilterInternal(@NonNull HttpServletRequest request, | ||
@NonNull HttpServletResponse response, | ||
@NonNull FilterChain filterChain) throws ServletException, IOException { | ||
try { | ||
final String token = getJwtFromRequest(request); | ||
if (jwtTokenProvider.validateToken(token) == VALID_JWT) { | ||
Long memberId = jwtTokenProvider.getUserFromJwt(token); | ||
// authentication 객체 생성 -> principal에 유저정보를 담는다. | ||
UserAuthentication authentication = new UserAuthentication(memberId.toString(), null, null); | ||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); | ||
SecurityContextHolder.getContext().setAuthentication(authentication); | ||
} | ||
} catch (Exception exception) { | ||
throw new RuntimeException(); | ||
} | ||
// 다음 필터로 요청 전달 | ||
filterChain.doFilter(request, response); | ||
} | ||
|
||
private String getJwtFromRequest(HttpServletRequest request) { | ||
String bearerToken = request.getHeader("Authorization"); | ||
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { | ||
return bearerToken.substring("Bearer ".length()); | ||
} | ||
return null; | ||
} | ||
} |
81 changes: 81 additions & 0 deletions
81
ThirdSeminar/src/main/java/com/server/dosopt/seminar/config/jwt/JwtTokenProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package com.server.dosopt.seminar.config.jwt; | ||
|
||
import com.server.dosopt.seminar.domain.JwtValidationType; | ||
import io.jsonwebtoken.*; | ||
import io.jsonwebtoken.security.Keys; | ||
import jakarta.annotation.PostConstruct; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.stereotype.Component; | ||
|
||
import javax.crypto.SecretKey; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.Base64; | ||
import java.util.Date; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
public class JwtTokenProvider { | ||
|
||
private static final String MEMBER_ID = "memberId"; | ||
|
||
@Value("${jwt.secret}") | ||
private String JWT_SECRET; | ||
|
||
@PostConstruct | ||
protected void init() { | ||
//base64 라이브러리에서 encodeToString을 이용해서 byte[] 형식을 String 형식으로 변환 | ||
JWT_SECRET = Base64.getEncoder().encodeToString(JWT_SECRET.getBytes(StandardCharsets.UTF_8)); | ||
} | ||
|
||
public String generateToken(Authentication authentication, Long tokenExpirationTime) { | ||
final Date now = new Date(); | ||
|
||
final Claims claims = Jwts.claims() | ||
.setIssuedAt(now) | ||
.setExpiration(new Date(now.getTime() + tokenExpirationTime)); // 만료 시간 | ||
|
||
claims.put(MEMBER_ID, authentication.getPrincipal()); | ||
|
||
return Jwts.builder() | ||
.setHeaderParam(Header.TYPE, Header.JWT_TYPE) // Header | ||
.setClaims(claims) // Claim | ||
.signWith(getSigningKey()) // Signature | ||
.compact(); | ||
} | ||
|
||
private SecretKey getSigningKey() { | ||
String encodedKey = Base64.getEncoder().encodeToString(JWT_SECRET.getBytes()); //SecretKey 통해 서명 생성 | ||
return Keys.hmacShaKeyFor(encodedKey.getBytes()); //일반적으로 HMAC (Hash-based Message Authentication Code) 알고리즘 사용 | ||
} | ||
|
||
public JwtValidationType validateToken(String token) { | ||
try { | ||
final Claims claims = getBody(token); | ||
return JwtValidationType.VALID_JWT; | ||
} catch (MalformedJwtException ex) { | ||
return JwtValidationType.INVALID_JWT_TOKEN; | ||
} catch (ExpiredJwtException ex) { | ||
return JwtValidationType.EXPIRED_JWT_TOKEN; | ||
} catch (UnsupportedJwtException ex) { | ||
return JwtValidationType.UNSUPPORTED_JWT_TOKEN; | ||
} catch (IllegalArgumentException ex) { | ||
return JwtValidationType.EMPTY_JWT; | ||
} | ||
} | ||
|
||
private Claims getBody(final String token) { | ||
return Jwts.parserBuilder() | ||
.setSigningKey(getSigningKey()) | ||
.build() | ||
.parseClaimsJws(token) | ||
.getBody(); | ||
} | ||
|
||
public Long getUserFromJwt(String token) { | ||
Claims claims = getBody(token); | ||
return Long.valueOf(claims.get(MEMBER_ID).toString()); | ||
} | ||
|
||
} |
Oops, something went wrong.