Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
devxb committed Jan 15, 2024
2 parents 0f40ec1 + 7802643 commit ccdcb9e
Show file tree
Hide file tree
Showing 21 changed files with 432 additions and 114 deletions.
6 changes: 6 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ projectVersion=1.0
### TEST ###
junitVersion=5.10.1
assertJVersion=3.24.2
testContainer=1.17.6
junitTestContainer=5.8.1
### LOMBOK ###
lombokVersion=1.18.30
### SPRING ###
Expand All @@ -19,3 +21,7 @@ mockWebServerVersion=4.12.0
sentryVersion=4.1.1
### AWS-CLOUD ###
springCloudAwsVersion=3.1.0
### JWT ###
jwtVersion=0.9.1
jwtBindingVersion=4.0.1
jwtJaxbApiVersion=2.3.1
9 changes: 8 additions & 1 deletion gradle/devtool.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ allprojects {
dependencies {
compileOnly "org.projectlombok:lombok:${lombokVersion}"
annotationProcessor "org.projectlombok:lombok"
implementation 'io.jsonwebtoken:jjwt:0.9.1'

implementation "io.jsonwebtoken:jjwt:${jwtVersion}"

// com.sun.xml.bind
implementation "com.sun.xml.bind:jaxb-impl:${jwtBindingVersion}"
implementation "com.sun.xml.bind:jaxb-core:${jwtBindingVersion}"
// javax.xml.bind
implementation "javax.xml.bind:jaxb-api:${jwtJaxbApiVersion}"

testCompileOnly "org.projectlombok:lombok:${lombokVersion}"
testAnnotationProcessor "org.projectlombok:lombok"
Expand Down
5 changes: 5 additions & 0 deletions gradle/test.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ allprojects {
}

dependencies {
// test container for redis
testImplementation "org.junit.jupiter:junit-jupiter:${junitTestContainer}"
testImplementation "org.testcontainers:testcontainers:${testContainer}"
testImplementation "org.testcontainers:junit-jupiter:${testContainer}"

testImplementation "org.junit.jupiter:junit-jupiter-api:${junitVersion}"
testImplementation "org.junit.jupiter:junit-jupiter-params:${junitVersion}"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junitVersion}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,18 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.teumteum.auth.domain.response.TokenResponse;
import net.teumteum.auth.service.OAuthService;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;

@Slf4j
@RequestMapping
@RequiredArgsConstructor
public class OAuthLoginController {

private final net.teumteum.auth.service.OAuthService oAuthService;
private final OAuthService oAuthService;

@GetMapping("/logins/callbacks/{provider}")
@ResponseStatus(HttpStatus.OK)
Expand Down
9 changes: 4 additions & 5 deletions src/main/java/net/teumteum/auth/service/OAuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,10 @@ private OAuthUserInfo getOAuthUserInfo(ClientRegistration clientRegistration, Au

private TokenResponse checkUserAndMakeResponse(OAuthUserInfo oAuthUserInfo, Authenticated authenticated) {
String oauthId = oAuthUserInfo.getOAuthId();
Optional<User> user = getUser(oauthId, authenticated);
if (user.isEmpty()) {
return new TokenResponse(oAuthUserInfo.getOAuthId());
}
return jwtService.createServiceToken(user.get());

return getUser(oauthId, authenticated)
.map(jwtService::createServiceToken)
.orElseGet(() -> new TokenResponse(oauthId));
}

private Map<String, Object> getOAuthAttribute(ClientRegistration clientRegistration, OAuthToken oAuthToken) {
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/net/teumteum/core/error/ErrorResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;

@Getter
@NoArgsConstructor
Expand All @@ -16,4 +15,8 @@ public class ErrorResponse {
public static ErrorResponse of(Throwable exception) {
return new ErrorResponse(exception.getMessage());
}

public static ErrorResponse of(String message) {
return new ErrorResponse(message);
}
}
52 changes: 29 additions & 23 deletions src/main/java/net/teumteum/core/security/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@

import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;


import java.util.Collections;
import java.util.List;
import lombok.RequiredArgsConstructor;
import net.teumteum.core.security.filter.JwtAccessDeniedHandler;
import net.teumteum.core.security.filter.JwtAuthenticationEntryPoint;
import net.teumteum.core.security.filter.JwtAuthenticationFilter;
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.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
Expand All @@ -27,43 +28,48 @@
@RequiredArgsConstructor
public class SecurityConfig {

private static final String[] PATTERNS = {"/", "/css/**", "/images/**", "/js/**", "/favicon.ico", "/h2-console/**",
"/docs/index.html", "/common/*.html", "/jwt-test", "/auth/**"};
private static final String[] PATTERNS = {"/css/**", "/images/**", "/js/**", "/favicon.ico", "/h2-console/**",
"/logins/**"};

private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
private final JwtAccessDeniedHandler accessDeniedHandler;
private final JwtAuthenticationFilter jwtAuthenticationFilter;

@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedHeaders(Collections.singletonList("*"));
config.setAllowedMethods(Collections.singletonList("*"));
config.setAllowedOriginPatterns(Collections.singletonList("/**")); // 허용할 origin
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}

@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return web -> web.ignoring().requestMatchers("/h2-console/**");
return web -> web.ignoring()
.requestMatchers("/css/**", "/js/**", "/img/**", "/favicon.ico", "/error");
}

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable)
.cors(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(
request -> request.requestMatchers("/**").permitAll()
.requestMatchers(PATTERNS).permitAll().anyRequest()
.authenticated()).httpBasic(AbstractHttpConfigurer::disable).formLogin(AbstractHttpConfigurer::disable)
http.csrf(AbstractHttpConfigurer::disable).cors(cors -> cors.configurationSource(corsConfigurationSource()))
.authorizeHttpRequests(request -> request.requestMatchers("/auth/**", "/logins/**").permitAll()
.requestMatchers(HttpMethod.POST, "/users/registers").permitAll().requestMatchers(PATTERNS).permitAll()
.anyRequest().authenticated()).httpBasic(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable)
.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(STATELESS))
.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin))
.exceptionHandling(
exceptionHandling -> exceptionHandling.authenticationEntryPoint(jwtAuthenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler))
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}

@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("http://localhost:3000");
config.addAllowedOrigin("https://api.teum.org");
config.addAllowedHeader("*");
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
config.addExposedHeader("Authorization");
config.addExposedHeader("Authorization-refresh");
config.setAllowCredentials(true);

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,34 @@
package net.teumteum.core.security.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import lombok.extern.slf4j.Slf4j;
import net.teumteum.core.error.ErrorResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import java.io.IOException;

import static jakarta.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;

@Slf4j
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {

@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authenticationException
HttpServletResponse response,
AuthenticationException authenticationException
) throws IOException {
this.sendUnAuthenticatedError(response, authenticationException);
}

private void sendUnAuthenticatedError(HttpServletResponse response,
Exception exception) throws IOException {
Exception exception) throws IOException {
OutputStream os = response.getOutputStream();
ObjectMapper objectMapper = new ObjectMapper();
log.error("Responding with unauthenticated error. Message - {}", exception.getMessage());
response.sendError(SC_UNAUTHORIZED, exception.getMessage());
objectMapper.writeValue(os, ErrorResponse.of("인증 과정에서 오류가 발생했습니다."));
os.flush();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public String extractRefreshToken(HttpServletRequest request) {

public String getUserIdFromToken(String token) {
try {
return Jwts.parser().setSigningKey(jwtProperty.getSecret())
return Jwts.parser().setSigningKey(jwtProperty.getSecret().getBytes())
.parseClaimsJws(token).getBody().getSubject();
} catch (Exception exception) {
throw new JwtException("Access Token is not valid");
Expand Down Expand Up @@ -79,13 +79,14 @@ private String createToken(String payload, Long tokenExpiration) {
.setClaims(claims)
.setIssuedAt(new Date())
.setExpiration(tokenExpiresIn)
.signWith(SignatureAlgorithm.HS512, jwtProperty.getSecret())
.signWith(SignatureAlgorithm.HS512, jwtProperty.getSecret().getBytes())
.compact();
}

public boolean validateToken(String token) {
try {
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(jwtProperty.getSecret()).parseClaimsJws(token);
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(jwtProperty.getSecret().getBytes())
.parseClaimsJws(token);
return !claimsJws.getBody().getExpiration().before(new Date());
} catch (ExpiredJwtException exception) {
log.warn("만료된 jwt 입니다.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ public static void clearSecurityContext() {
SecurityContextHolder.clearContext();
}

private UserAuthentication getUserAuthentication() {
return (UserAuthentication) SecurityContextHolder.getContext().getAuthentication();
}


public Long getCurrentUserId() {
return getUserAuthentication() == null ? userConnector.findAllUser().get(0).getId()
: getUserAuthentication().getId();
Expand All @@ -36,4 +31,8 @@ public void setUserId(Long userId) {
UserAuthentication userAuthentication = getUserAuthentication();
userAuthentication.setUserId(userId);
}

private UserAuthentication getUserAuthentication() {
return (UserAuthentication) SecurityContextHolder.getContext().getAuthentication();
}
}
39 changes: 19 additions & 20 deletions src/main/java/net/teumteum/user/controller/UserController.java
Original file line number Diff line number Diff line change
@@ -1,32 +1,20 @@
package net.teumteum.user.controller;

import io.sentry.Sentry;
import java.util.Arrays;
import java.util.List;
import lombok.RequiredArgsConstructor;
import net.teumteum.core.error.ErrorResponse;
import net.teumteum.core.security.service.SecurityService;
import net.teumteum.user.domain.response.UserMeGetResponse;
import net.teumteum.user.domain.request.UserRegisterRequest;
import net.teumteum.user.domain.request.UserUpdateRequest;
import net.teumteum.user.domain.response.FriendsResponse;
import net.teumteum.user.domain.response.InterestQuestionResponse;
import net.teumteum.user.domain.response.UserGetResponse;
import net.teumteum.user.domain.response.UserRegisterResponse;
import net.teumteum.user.domain.response.UsersGetByIdResponse;
import net.teumteum.user.domain.response.*;
import net.teumteum.user.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

import java.util.Arrays;
import java.util.List;

@RestController
@RequiredArgsConstructor
Expand All @@ -53,6 +41,12 @@ public UsersGetByIdResponse getUsersById(@RequestParam("id") String userIds) {
return userService.getUsersById(parsedUserIds);
}

@GetMapping("/me")
@ResponseStatus(HttpStatus.OK)
public UserMeGetResponse getMe() {
return userService.getMe(getCurrentUserId());
}

@PutMapping
@ResponseStatus(HttpStatus.OK)
public void updateUser(@RequestBody UserUpdateRequest request) {
Expand All @@ -78,18 +72,23 @@ public InterestQuestionResponse getInterestQuestion(@RequestParam("user-id") Lis
return userService.getInterestQuestionByUserIds(userIds, balance);
}

@DeleteMapping("/withdraws")
@DeleteMapping
@ResponseStatus(HttpStatus.OK)
public void withdraw() {
userService.withdraw(getCurrentUserId());
}

@PostMapping("/registers")
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public UserRegisterResponse register(@RequestBody UserRegisterRequest request) {
return userService.register(request);
}

@PostMapping("/logouts")
@ResponseStatus(HttpStatus.OK)
public void logout() {
userService.logout(getCurrentUserId());
}

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
Expand Down
Loading

0 comments on commit ccdcb9e

Please sign in to comment.