Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/#21 oauth2 develop language #153

Merged
merged 6 commits into from
Feb 23, 2024
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 {
public ResponseEntity<AuthAccessTokenResponse> reissue(
@RequestHeader("Authorization") String accessToken,
@CookieValue("refreshToken") String refreshToken) {
accessToken = accessToken.substring(7);
var accessTokenDto = authService.reissue(accessToken, refreshToken);
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
Loading