Skip to content

Commit

Permalink
Merge pull request #66 from SWM-SMART/feat/#63
Browse files Browse the repository at this point in the history
�인증 및 AI 요청 에러 수정
  • Loading branch information
noparamin authored Nov 19, 2023
2 parents 8c94330 + 22984ad commit 7036f57
Show file tree
Hide file tree
Showing 11 changed files with 123 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseCookie;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
Expand All @@ -18,6 +19,7 @@
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Optional;

@RequiredArgsConstructor
Expand Down Expand Up @@ -67,7 +69,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
* reIssueRefreshToken()로 리프레시 토큰 재발급 & DB에 리프레시 토큰 업데이트 메소드 호출
* 그 후 JwtService.sendAccessTokenAndRefreshToken()으로 응답 헤더에 보내기
*/
public void checkRefreshTokenAndReIssueAccessToken(HttpServletResponse response, String refreshToken) {
public void checkRefreshTokenAndReIssueAccessToken(HttpServletResponse response, String refreshToken) throws UnsupportedEncodingException {
if (jwtService.isTokenValid(refreshToken)) {
String reIssuedRefreshToken = reIssueRefreshToken(jwtService.extractUserId(refreshToken).get());
jwtService.sendAccessAndRefreshToken(response, jwtService.createAccessToken(jwtService.extractUserId(refreshToken).get()),
Expand All @@ -77,8 +79,12 @@ public void checkRefreshTokenAndReIssueAccessToken(HttpServletResponse response,
userRepository.findById(userId)
.ifPresent(user -> {
String reIssuedRefreshToken = reIssueRefreshToken(user.getId());
jwtService.sendAccessAndRefreshToken(response, jwtService.createAccessToken(user.getId()),
reIssuedRefreshToken);
try {
jwtService.sendAccessAndRefreshToken(response, jwtService.createAccessToken(user.getId()),
reIssuedRefreshToken);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
});
}

Expand All @@ -104,8 +110,10 @@ public void checkAccessTokenAndAuthentication(HttpServletRequest request, HttpSe
FilterChain filterChain) throws ServletException, IOException {
log.info("checkAccessTokenAndAuthentication() 호출");
jwtService.extractAccessToken(request)
.filter(jwtService::isTokenValid);

.filter(jwtService::isTokenValid)
.ifPresent(accessToken -> jwtService.extractUserId(accessToken)
.ifPresent(userId -> userRepository.findById(userId)
.ifPresent(this::saveAuthentication)));
filterChain.doFilter(request, response);
}

Expand All @@ -126,7 +134,6 @@ public void checkAccessTokenAndAuthentication(HttpServletRequest request, HttpSe
*/
public void saveAuthentication(User myUser) {
String password = "asd";

UserDetails userDetailsUser = org.springframework.security.core.userdetails.User.builder()
.username(myUser.getEmail())
.password(password)
Expand All @@ -136,9 +143,7 @@ public void saveAuthentication(User myUser) {
Authentication authentication =
new UsernamePasswordAuthenticationToken(userDetailsUser, null,
authoritiesMapper.mapAuthorities(userDetailsUser.getAuthorities()));

log.info("saveAuthentication");
SecurityContextHolder.getContext().setAuthentication(authentication);
}


}
18 changes: 14 additions & 4 deletions src/main/java/com/smart/watchboard/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,27 @@
import com.smart.watchboard.service.CustomOAuth2UserService;
import com.smart.watchboard.service.JwtService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
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.logout.LogoutFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;

@EnableWebSecurity
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
@Value("${front-url}")
private String frontUrl;

private final OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
private final OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
private final CustomOAuth2UserService customOAuth2UserService;
Expand All @@ -31,15 +37,19 @@ public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.formLogin().disable();
http.httpBasic().disable();
http.cors().configurationSource(corsConfigurationSource()).and().authorizeHttpRequests(authorize -> authorize.requestMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html", "/**").permitAll()
.anyRequest().authenticated()).csrf().disable();
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.cors().configurationSource(corsConfigurationSource()).and().authorizeHttpRequests(authorize -> authorize.requestMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html", "/users/token").permitAll()
.anyRequest().authenticated());
http.oauth2Login()
.successHandler(oAuth2AuthenticationSuccessHandler)
.failureHandler(oAuth2AuthenticationFailureHandler)
.userInfoEndpoint()
.userService(customOAuth2UserService);

http.addFilterAfter(jwtAuthenticationProcessingFilter(), LogoutFilter.class);

return http.build();
}
Expand All @@ -50,7 +60,7 @@ CorsConfigurationSource corsConfigurationSource() {
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setExposedHeaders(Arrays.asList("Authorization"));
configuration.setAllowedOrigins(Arrays.asList("https://4988-221-148-248-129.ngrok-free.app")); // 추후 수정
configuration.setAllowedOrigins(Arrays.asList(frontUrl)); // 추후 수정
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.parameters.P;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

Expand All @@ -39,6 +40,7 @@ public class GraphController {
private final KeywordService keywordService;
private final JwtService jwtService;
private final QuestionService questionService;
private final SummaryService summaryService;
@PostMapping("/graph/{documentID}")
@Operation(summary = "마인드맵 생성", description = "ai 서버에 마인드맵 요청한다.")
public void createMindmap(@PathVariable(value = "documentID") long documentId, @RequestBody KeywordsBodyDto keywordsBodyDto, @RequestHeader("Authorization") String accessToken) throws JsonProcessingException {
Expand Down Expand Up @@ -70,8 +72,8 @@ public ResponseEntity<?> getMindmap(@PathVariable(value = "documentID") long doc
if (document.getDataType().equals("pdf")) {
String pdfUrl = fileService.getPath(documentId);
if (mindmapDto == null) {
sseService.notifyKeywords(documentId, pdfUrl);
sseService.notifySummary(documentId, pdfUrl);
sseService.notifyKeywords(documentId, pdfUrl);
return new ResponseEntity<>(HttpStatus.ACCEPTED);
}
} else {
Expand All @@ -98,10 +100,16 @@ public ResponseEntity<?> updateKeywords(@PathVariable(value = "documentID") long
@Operation(summary = "키워드 질문", description = "키워드 AI에 질문")
public ResponseEntity<AnswerDto> getAnswer(@PathVariable(value = "documentID") long documentId, @PathVariable String keywordLabel, @RequestHeader("Authorization") String accessToken) throws JsonProcessingException {
AnswerDto answerDto = questionService.getAnswer(documentId, keywordLabel);
if (!summaryService.checkSummary(documentId)) {
return new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE);
}
if (answerDto == null) {
sseService.notifyAnswer(documentId, keywordLabel);
return new ResponseEntity<>(HttpStatus.ACCEPTED);
}
if (answerDto.getText().equals("processing")) {
return new ResponseEntity<>(HttpStatus.ACCEPTED);
}

return new ResponseEntity<>(answerDto, HttpStatus.OK);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class UserController {
@GetMapping("/info")
@Operation(summary = "사용자 정보 조회", description = "요청받은 사용자의 정보를 조회한다.")
public ResponseEntity<?> getUserInformation(@RequestHeader("Authorization") String accessToken) {
UserInformationDto userInformationDto = new UserInformationDto(1L, "권민석", "[email protected]");
UserInformationDto userInformationDto = jwtService.getUserInformation(accessToken);

return new ResponseEntity<>(userInformationDto, HttpStatus.OK);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.smart.watchboard.domain.WhiteboardData;
import com.smart.watchboard.dto.*;
import com.smart.watchboard.service.JwtService;
import com.smart.watchboard.service.WhiteboardService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand All @@ -19,6 +20,7 @@
public class WhiteboardController {

private final WhiteboardService whiteboardService;
private final JwtService jwtService;

@GetMapping()
@Operation(summary = "문서 목록 조회", description = "사용자가 속해 있는 모든 문서 목록을 조회한다.")
Expand All @@ -31,6 +33,11 @@ public ResponseEntity<?> getAllDocuments(@RequestHeader("Authorization") String
@GetMapping("/{documentID}")
@Operation(summary = "문서 데이터 조회", description = "특정 문서의 데이터를 조회한다.")
public ResponseEntity<DocumentResponseDto> getDocument(@PathVariable(value = "documentID") long documentId, @RequestHeader("Authorization") String accessToken) {
String extractedAccessToken = jwtService.extractAccessToken(accessToken);
Long userId = jwtService.extractUserId(extractedAccessToken).orElse(null);
if (!whiteboardService.checkAuthorization(documentId, userId)) {
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
DocumentResponseDto response = whiteboardService.findDocument(documentId, accessToken);

return new ResponseEntity<>(response, HttpStatus.OK);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseCookie;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
Expand All @@ -21,6 +22,8 @@
@Slf4j
@RequiredArgsConstructor
public class OAuth2AuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Value("${front-url}")
private String frontUrl;

private final JwtService jwtService;
private final UserRepository userRepository;
Expand All @@ -30,12 +33,12 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo
log.info("OAuth2 Login 성공!");
try {
CustomOAuth2User oAuth2User = (CustomOAuth2User) authentication.getPrincipal();

if (oAuth2User.getRole() == Role.USER) {
String refreshToken = jwtService.createRefreshToken(oAuth2User.getUserId());
ResponseCookie cookie = jwtService.setCookieRefreshToken(refreshToken);
response.setHeader("Set-Cookie", cookie.toString());
response.sendRedirect("https://4988-221-148-248-129.ngrok-free.app"); // 추후 수정
response.sendRedirect(frontUrl); // 추후 수정
// response.sendRedirect("http//localhost:8081");
User findUser = userRepository.findByEmail(oAuth2User.getEmail())
.orElseThrow(() -> new IllegalArgumentException("이메일에 해당하는 유저가 없습니다."));
findUser.authorizeUser();
Expand Down
40 changes: 35 additions & 5 deletions src/main/java/com/smart/watchboard/service/JwtService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.smart.watchboard.domain.User;
import com.smart.watchboard.dto.UserInformationDto;
import com.smart.watchboard.repository.UserRepository;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Getter;
Expand Down Expand Up @@ -45,6 +49,8 @@ public class JwtService {
private static final String ISSUER_CLAIM_VALUE = "wb";
private static final String BEARER = "Bearer ";

private final UserRepository userRepository;

/**
* AccessToken 생성 메소드
*/
Expand Down Expand Up @@ -85,7 +91,7 @@ public void sendAccessToken(HttpServletResponse response, String accessToken) {
/**
* AccessToken + RefreshToken 헤더에 실어서 보내기
*/
public void sendAccessAndRefreshToken(HttpServletResponse response, String accessToken, String refreshToken) {
public void sendAccessAndRefreshToken(HttpServletResponse response, String accessToken, String refreshToken) throws UnsupportedEncodingException {
response.setStatus(HttpServletResponse.SC_OK);

setAccessTokenHeader(response, accessToken);
Expand All @@ -100,7 +106,18 @@ public void sendAccessAndRefreshToken(HttpServletResponse response, String acces
* 헤더를 가져온 후 "Bearer"를 삭제(""로 replace)
*/
public Optional<String> extractRefreshToken(HttpServletRequest request) {
return Optional.ofNullable(request.getHeader(refreshHeader))
Cookie[] cookies = request.getCookies();
String bearerRefreshToken = "";
if (cookies != null) {
for (int i = 0; i < cookies.length && cookies[i] != null; i++) {
if (cookies[i].getName().equals("refreshToken")) {
bearerRefreshToken = cookies[i].getValue();
break;
}
}
}

return Optional.ofNullable(bearerRefreshToken)
.filter(refreshToken -> refreshToken.startsWith(BEARER))
.map(refreshToken -> refreshToken.replace(BEARER, ""));
}
Expand All @@ -119,6 +136,7 @@ public Optional<String> extractAccessToken(HttpServletRequest request) {
public String extractAccessToken(String accessToken) {
try {
isAccessTokenFormatValid(accessToken);
return accessToken.substring(7);
} catch (IllegalArgumentException e) {
log.info("Error: " + e);
}
Expand Down Expand Up @@ -157,14 +175,17 @@ public Optional<Long> extractUserId(String token) {
* AccessToken 헤더 설정
*/
public void setAccessTokenHeader(HttpServletResponse response, String accessToken) {
response.setHeader(accessHeader, accessToken);
String bearerAccessToken = "Bearer " + accessToken;
response.setHeader(accessHeader, bearerAccessToken);
}

/**
* RefreshToken 헤더 설정
*/
public void setRefreshTokenHeader(HttpServletResponse response, String refreshToken) {
response.setHeader(refreshHeader, refreshToken);
public void setRefreshTokenHeader(HttpServletResponse response, String refreshToken) throws UnsupportedEncodingException {
ResponseCookie cookie = setCookieRefreshToken(refreshToken);
response.setHeader("Set-Cookie", cookie.toString());
//response.setHeader(refreshHeader, refreshToken);
}


Expand Down Expand Up @@ -237,4 +258,13 @@ public HttpHeaders createHeaderWithDeletedCookie(String accessToken) {

return headers;
}

public UserInformationDto getUserInformation(String accessToken) {
String extractedToken = extractDecodedToken(accessToken);
Long userId = extractUserId(extractedToken).orElse(null);
Optional<User> user = userRepository.findById(userId);
UserInformationDto userInformationDto = new UserInformationDto(userId, user.get().getNickname(), user.get().getEmail());

return userInformationDto;
}
}
17 changes: 10 additions & 7 deletions src/main/java/com/smart/watchboard/service/QuestionService.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,21 @@ public class QuestionService {
private final AnswerRepository answerRepository;

public void createAnswer(Long documentId, String keyword, ResponseEntity<AnswerDto> responseEntity) {
AnswerDto answerDto = getAnswer(documentId, keyword);
if (answerDto.getText().equals("processing")) {
Answer answer = getAnswerForCreate(documentId, keyword);
answer.setText(responseEntity.getBody().getText());
answerRepository.save(answer);
} else if(answerDto == null) {
String text = responseEntity.getBody().getText();
if (text.equals("init")) {
Answer answer = Answer.builder()
.documentId(documentId)
.keyword(keyword)
.text(responseEntity.getBody().getText())
.text("processing")
.build();
answerRepository.save(answer);
} else {
AnswerDto answerDto = getAnswer(documentId, keyword);
if (answerDto.getText().equals("processing")) {
Answer answer = getAnswerForCreate(documentId, keyword);
answer.setText(responseEntity.getBody().getText());
answerRepository.save(answer);
}
}
}

Expand Down
Loading

0 comments on commit 7036f57

Please sign in to comment.