Skip to content

Commit

Permalink
Merge branch 'develop' into bug/#140-login_response
Browse files Browse the repository at this point in the history
  • Loading branch information
tidavid1 committed Feb 23, 2024
2 parents 8c96723 + b9b2168 commit a2ccdde
Show file tree
Hide file tree
Showing 18 changed files with 304 additions and 93 deletions.
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-redis:3.1.8'
// Swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'
// Spring Boot WebFlux
implementation 'org.springframework.boot:spring-boot-starter-webflux:3.1.8'

// SpringBoot Test
testImplementation 'org.springframework.boot:spring-boot-starter-test:3.1.8'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.oeid.mogakgo.exception.dto.ErrorResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.media.Schema;
Expand All @@ -34,16 +35,20 @@ ResponseEntity<Void> manageFCMToken(@Parameter(hidden = true) Long userId,
FCMTokenApiRequest request);

@Operation(summary = "알림 조회", description = "회원의 알림을 조회할 때 사용하는 API")
@ApiResponse(responseCode = "200", description = "알림 조회 성공",
content = @Content(schema = @Schema(implementation = NotificationPublicApiRes.class)))
@ApiResponse(responseCode = "200", description = "알림 조회 성공")
@ApiResponse(responseCode = "404", description = "요청한 유저가 존재하지 않음", content = @Content(
mediaType = APPLICATION_JSON_VALUE,
schema = @Schema(implementation = ErrorResponse.class),
examples = {
@ExampleObject(name = "E020301", value = SwaggerUserErrorExamples.USER_NOT_FOUND)
})
)
@Parameters({
@Parameter(name = "cursorId", description = "기준이 되는 커서 ID", example = "1"),
@Parameter(name = "pageSize", description = "요청할 데이터 크기", example = "5", required = true),
@Parameter(name = "sortOrder", description = "정렬 방향", example = "ASC"),
})
ResponseEntity<CursorPaginationResult<NotificationPublicApiRes>> getByUserId(
@Parameter(hidden = true) Long id,
CursorPaginationInfoReq pageable);
@Parameter(hidden = true) CursorPaginationInfoReq pageable);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import io.oeid.mogakgo.core.properties.swagger.error.SwaggerUserErrorExamples;
import io.oeid.mogakgo.domain.user.presentation.dto.req.UserSignUpApiRequest;
import io.oeid.mogakgo.domain.user.presentation.dto.res.UserDevelopLanguageApiRes;
import io.oeid.mogakgo.domain.user.presentation.dto.res.UserPublicApiResponse;
import io.oeid.mogakgo.domain.user.presentation.dto.res.UserSignUpApiResponse;
import io.oeid.mogakgo.exception.dto.ErrorResponse;
Expand All @@ -15,6 +16,7 @@
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import org.springframework.http.ResponseEntity;

@Tag(name = "User", description = "회원 관련 API")
Expand Down Expand Up @@ -65,4 +67,17 @@ ResponseEntity<UserSignUpApiResponse> userSignUpApi(
examples = @ExampleObject(name = "E020301", value = SwaggerUserErrorExamples.USER_NOT_FOUND))),
})
ResponseEntity<UserPublicApiResponse> userGetApi(@Parameter(hidden = true) Long userId);


@Operation(summary = "회원 개발 언어 조회", description = "회원의 주요 개발 언어를 조회할 때 사용하는 API")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "회원 개발 언어 조회 성공"),
@ApiResponse(responseCode = "404", description = "해당 유저가 존재하지 않음",
content = @Content(
mediaType = APPLICATION_JSON_VALUE,
schema = @Schema(implementation = ErrorResponse.class),
examples = @ExampleObject(name = "E020301", value = SwaggerUserErrorExamples.USER_NOT_FOUND))),
})
ResponseEntity<List<UserDevelopLanguageApiRes>> userDevelopLanguageApi(
@Parameter(hidden = true) Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,15 @@
import io.oeid.mogakgo.domain.auth.jwt.JwtAccessDeniedHandler;
import io.oeid.mogakgo.domain.auth.jwt.JwtAuthenticationEntryPoint;
import io.oeid.mogakgo.domain.auth.jwt.JwtAuthenticationFilter;
import io.oeid.mogakgo.domain.auth.oauth.GithubOAuth2UserService;
import io.oeid.mogakgo.domain.auth.oauth.OAuth2AuthenticationSuccessHandler;
import java.util.Arrays;
import java.util.List;
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.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
Expand All @@ -26,18 +23,15 @@
@Configuration
public class SecurityConfig {

private final OAuth2UserService<OAuth2UserRequest, OAuth2User> oAuth2UserService;
private final AuthenticationSuccessHandler authenticationSuccessHandler;
private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
private final JwtAccessDeniedHandler jwtAccessDeniedHandler;
private final JwtAuthenticationFilter jwtAuthenticationFilter;

public SecurityConfig(GithubOAuth2UserService oAuth2UserService,
OAuth2AuthenticationSuccessHandler authenticationSuccessHandler,
public SecurityConfig(OAuth2AuthenticationSuccessHandler authenticationSuccessHandler,
JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint,
JwtAccessDeniedHandler jwtAccessDeniedHandler,
JwtAuthenticationFilter jwtAuthenticationFilter) {
this.oAuth2UserService = oAuth2UserService;
this.authenticationSuccessHandler = authenticationSuccessHandler;
this.jwtAuthenticationEntryPoint = jwtAuthenticationEntryPoint;
this.jwtAccessDeniedHandler = jwtAccessDeniedHandler;
Expand All @@ -64,9 +58,10 @@ SecurityFilterChain filterChainApi(HttpSecurity http) throws Exception {
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();

configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE","PATCH","OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowedOrigins(List.of("*"));
configuration.setAllowedMethods(
Arrays.asList("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
configuration.setAllowedHeaders(List.of("*"));
// configuration.setAllowCredentials(true);

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
Expand All @@ -78,9 +73,7 @@ public CorsConfigurationSource corsConfigurationSource() {
public SecurityFilterChain filterChainOAuth2(HttpSecurity http) throws Exception {
configureCommonSecuritySettings(http);
return http
.oauth2Login(oauth2 -> oauth2
.userInfoEndpoint(userInfo -> userInfo.userService(oAuth2UserService))
.successHandler(authenticationSuccessHandler))
.oauth2Login(oauth2 -> oauth2.successHandler(authenticationSuccessHandler))
.authorizeHttpRequests(
requests -> requests.anyRequest().permitAll())
.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.oeid.mogakgo.domain.auth.application;

import io.oeid.mogakgo.domain.user.application.dto.res.UserOAuth2Response;
import io.oeid.mogakgo.domain.user.domain.User;
import io.oeid.mogakgo.domain.user.infrastructure.UserJpaRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class AuthUserService {

private final UserJpaRepository userRepository;

@Transactional
public UserOAuth2Response manageOAuth2User(long githubPk, String githubId, String avatarUrl,
String githubUrl,
String repositoryUrl) {
User user = userRepository.findByGithubPk(githubPk).orElseGet(() -> userRepository.save(
User.of(githubPk, githubId, avatarUrl, githubUrl, repositoryUrl)));
user.updateGithubInfo(githubId, avatarUrl, githubUrl, repositoryUrl);
return UserOAuth2Response.from(user);
}


}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,48 +1,82 @@
package io.oeid.mogakgo.domain.auth.oauth;

import io.oeid.mogakgo.domain.auth.application.AuthUserService;
import io.oeid.mogakgo.domain.auth.jwt.JwtAuthenticationToken;
import io.oeid.mogakgo.domain.auth.jwt.JwtHelper;
import io.oeid.mogakgo.domain.auth.jwt.JwtRedisDao;
import io.oeid.mogakgo.domain.auth.jwt.JwtToken;
import io.oeid.mogakgo.domain.user.domain.enums.Role;
import io.oeid.mogakgo.domain.user.application.dto.res.UserOAuth2Response;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@RequiredArgsConstructor
public class OAuth2AuthenticationSuccessHandler implements AuthenticationSuccessHandler {

private final JwtHelper jwtHelper;
private final JwtRedisDao jwtRedisDao;
private final AuthUserService authUserService;

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
log.info("OAuth2 Login Success -> onAuthenticationSuccess");
// 인증 완료된 유저 정보를 불러오기
OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal();
JwtToken jwtToken = jwtHelper.sign(Long.parseLong(oAuth2User.getName()),
new String[]{Role.ROLE_USER.name()});
// 인증 완료된 유저 정보 기반 유저 엔티티 관리 및 결과 반환
var userOAuth2Response = manageUserEntity(Long.parseLong(oAuth2User.getName()), oAuth2User);
// JWT 토큰 생성 및 저장
var jwtToken = generateJwtToken(userOAuth2Response);
// 완료된 인증 정보 기반 새로운 authentication 생성
oAuth2User = generateAuthentication(userOAuth2Response, jwtToken);
// 새로운 authentication을 SecurityContextHolder에 저장
authentication = new JwtAuthenticationToken(oAuth2User, null,
oAuth2User.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
// 로그인 성공 페이지로 리다이렉트
response.sendRedirect("/oauth2/login/success");
}

private UserOAuth2Response manageUserEntity(long githubPk, OAuth2User oAuth2User) {
String githubId = oAuth2User.getAttribute("login");
String avatarUrl = oAuth2User.getAttribute("avatar_url");
String githubUrl = oAuth2User.getAttribute("html_url");
String repositoryUrl = oAuth2User.getAttribute("repos_url");
return authUserService.manageOAuth2User(githubPk, githubId, avatarUrl, githubUrl,
repositoryUrl);
}

private JwtToken generateJwtToken(UserOAuth2Response response) {
long userId = response.getUserId();
String[] roles = response.getAuthorities().stream().map(GrantedAuthority::getAuthority)
.toArray(String[]::new);
var jwtToken = jwtHelper.sign(userId, roles);
jwtRedisDao.saveTokens(jwtToken.getAccessToken(), jwtToken.getRefreshToken());
return jwtToken;
}

private OAuth2User generateAuthentication(UserOAuth2Response oAuth2Response,
JwtToken jwtToken) {
Map<String, Object> attributes = new HashMap<>();
attributes.put("id", Long.parseLong(oAuth2User.getName()));
attributes.put("signUpComplete", oAuth2User.getAttribute("signupComplete"));
attributes.put("id", oAuth2Response.getUserId());
attributes.put("signUpComplete", oAuth2Response.getSignUpComplete());
attributes.put("accessToken", jwtToken.getAccessToken());
attributes.put("refreshToken", jwtToken.getRefreshToken());
attributes.put("refreshTokenExpireTime", jwtToken.getRefreshTokenExpirySeconds());
oAuth2User = new DefaultOAuth2User(oAuth2User.getAuthorities(), attributes, "id");
authentication = new JwtAuthenticationToken(oAuth2User, null,
oAuth2User.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
response.sendRedirect("/oauth2/login/success");
return new DefaultOAuth2User(oAuth2Response.getAuthorities(), attributes, "id");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class AuthController implements AuthSwagger {
@PostMapping("/reissue")
public ResponseEntity<AuthAccessTokenResponse> reissue(
@RequestHeader("Authorization") String accessToken, @RequestBody AuthReissueRequest request) {
accessToken = accessToken.substring(7);
var accessTokenDto = authService.reissue(accessToken, request.getRefreshToken());
return ResponseEntity.ok(AuthAccessTokenResponse.of(accessTokenDto.getAccessToken(), null));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package io.oeid.mogakgo.domain.user.application;

import io.oeid.mogakgo.domain.user.application.dto.req.UserSignUpRequest;
import io.oeid.mogakgo.domain.user.application.dto.res.UserDevelopLanguageRes;
import io.oeid.mogakgo.domain.user.application.dto.res.UserProfileResponse;
import io.oeid.mogakgo.domain.user.application.dto.res.UserSignUpResponse;
import io.oeid.mogakgo.domain.user.domain.User;
import io.oeid.mogakgo.domain.user.domain.UserDevelopLanguageTag;
import io.oeid.mogakgo.domain.user.domain.UserWantedJobTag;
import io.oeid.mogakgo.domain.user.domain.enums.DevelopLanguage;
import io.oeid.mogakgo.domain.user.domain.enums.WantedJob;
import io.oeid.mogakgo.domain.user.exception.UserException;
import io.oeid.mogakgo.domain.user.infrastructure.UserDevelopLanguageTagJpaRepository;
import io.oeid.mogakgo.domain.user.infrastructure.UserWantedJobTagJpaRepository;
import io.oeid.mogakgo.domain.user.util.UserGithubUtil;
import io.oeid.mogakgo.exception.code.ErrorCode400;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
Expand All @@ -24,6 +29,8 @@ public class UserService {

private final UserCommonService userCommonService;
private final UserWantedJobTagJpaRepository userWantedJobTagRepository;
private final UserDevelopLanguageTagJpaRepository userDevelopLanguageTagRepository;
private final UserGithubUtil userGithubUtil;

@Transactional
public UserSignUpResponse userSignUp(UserSignUpRequest userSignUpRequest) {
Expand All @@ -40,6 +47,26 @@ public UserSignUpResponse userSignUp(UserSignUpRequest userSignUpRequest) {
return UserSignUpResponse.from(user);
}

@Transactional
public List<UserDevelopLanguageRes> updateUserDevelopLanguages(long userId) {
User user = userCommonService.getUserById(userId);
user.deleteAllDevelopLanguageTags();
var languages = userGithubUtil.updateUserDevelopLanguage(user.getRepositoryUrl());
languages.forEach((key, value) -> {
UserDevelopLanguageTag developLanguageTag = UserDevelopLanguageTag.builder()
.user(user)
.developLanguage(DevelopLanguage.of(key))
.byteSize(value)
.build();
userDevelopLanguageTagRepository.save(developLanguageTag);
});
List<UserDevelopLanguageRes> response = new ArrayList<>();
for (var developLanguage : user.getUserDevelopLanguageTags()) {
response.add(UserDevelopLanguageRes.from(developLanguage));
}
return response;
}

public UserProfileResponse getUserProfile(Long userId) {
User user = userCommonService.getUserById(userId);
return UserProfileResponse.from(user);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.oeid.mogakgo.domain.user.application.dto.res;

import io.oeid.mogakgo.domain.user.domain.UserDevelopLanguageTag;
import io.oeid.mogakgo.domain.user.domain.enums.DevelopLanguage;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class UserDevelopLanguageRes {

private final DevelopLanguage language;
private final Integer byteSize;

public static UserDevelopLanguageRes from(UserDevelopLanguageTag userDevelopLanguageTag) {
return new UserDevelopLanguageRes(userDevelopLanguageTag.getDevelopLanguage(),
userDevelopLanguageTag.getByteSize());
}
}
Loading

0 comments on commit a2ccdde

Please sign in to comment.