Skip to content

Commit

Permalink
Merge pull request #102 from TeamSynergyy/feature/queryDsl
Browse files Browse the repository at this point in the history
#100 RefreshToken을 통해 AccessToken 발급 기능
  • Loading branch information
rivkode authored May 28, 2024
2 parents 184df3b + 3af4998 commit d4cc2d4
Show file tree
Hide file tree
Showing 10 changed files with 246 additions and 60 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.seoultech.synergybe.domain.user;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.seoultech.synergybe.domain.user.vo.RefreshToken;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import jakarta.persistence.*;


@Getter
@Setter
@NoArgsConstructor
Expand All @@ -23,19 +24,19 @@ public class UserRefreshToken {
@Column(name = "user_id")
private String userId;

@Column(name = "refresh_token", length = 256)
private String refreshToken;

@Embedded
private RefreshToken refreshToken;

public UserRefreshToken(
String userId,
String refreshToken
String userId
) {
this.userId = userId;
this.refreshToken = refreshToken;
this.refreshToken = new RefreshToken();
}

public UserRefreshToken updateRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
public UserRefreshToken updateRefreshToken() {
this.refreshToken = new RefreshToken();
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.seoultech.synergybe.domain.user.repository;

import com.seoultech.synergybe.domain.user.UserRefreshToken;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
@Transactional
@RequiredArgsConstructor
public class UserRefreshTokenFactory {
private final UserRefreshTokenRepository userRefreshTokenRepository;

public void save(UserRefreshToken userRefreshToken) {
userRefreshTokenRepository.save(userRefreshToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface UserRefreshTokenRepository extends JpaRepository<UserRefreshToken, Long> {
UserRefreshToken findByUserId(String userId);
UserRefreshToken findByUserIdAndRefreshToken(String userId, String refreshToken);
Optional<UserRefreshToken> findByUserId(String userId);

UserRefreshToken findByRefreshTokenRefreshToken(String token);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.seoultech.synergybe.domain.user.service;

import com.seoultech.synergybe.domain.user.UserRefreshToken;
import com.seoultech.synergybe.domain.user.repository.UserRefreshTokenRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.Optional;

@Component
@RequiredArgsConstructor
public class UserRefreshTokenReader {
private final UserRefreshTokenRepository userRefreshTokenRepository;

public Optional<UserRefreshToken> readByUserId(String userId) {
return userRefreshTokenRepository.findByUserId(userId);
}

public UserRefreshToken readByRefreshToken(String token) {
return userRefreshTokenRepository.findByRefreshTokenRefreshToken(token);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.seoultech.synergybe.domain.user.vo;

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.Getter;

import java.util.UUID;

@Getter
@Embeddable
public class RefreshToken {
@Column(name = "refresh_token")
private String refreshToken;

public RefreshToken() {
this.refreshToken = String.valueOf(UUID.randomUUID());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@

import com.seoultech.synergybe.domain.auth.JwtAuthenticationProvider;
import com.seoultech.synergybe.domain.common.CustomPasswordEncoder;
import com.seoultech.synergybe.domain.user.repository.UserRefreshTokenFactory;
import com.seoultech.synergybe.domain.user.service.UserRefreshTokenReader;
import com.seoultech.synergybe.system.security.*;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
Expand All @@ -31,6 +30,8 @@
public class SecurityConfig {

private final JwtUtil jwtUtil;
private final UserRefreshTokenReader userRefreshTokenReader;
private final UserRefreshTokenFactory userRefreshTokenFactory;
private final UserDetailsServiceImpl userDetailsService;
private final AuthenticationConfiguration authenticationConfiguration;
private final JwtAuthenticationProvider authenticationProvider;
Expand All @@ -48,7 +49,7 @@ public AuthenticationManager authenticationManager(AuthenticationConfiguration c
// Authenticatoin, 토큰에 대해 인증
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
JwtAuthenticationFilter filter = new JwtAuthenticationFilter(jwtUtil);
JwtAuthenticationFilter filter = new JwtAuthenticationFilter(jwtUtil, userRefreshTokenReader, userRefreshTokenFactory);
filter.setAuthenticationManager(authenticationManager(authenticationConfiguration));

return filter;
Expand All @@ -58,7 +59,7 @@ public JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
// Authorization, 식별된 사용자에 대해 권한 부여, 인가
@Bean
public JwtAuthorizationFilter jwtAuthorizationFilter() {
return new JwtAuthorizationFilter(jwtUtil, userDetailsService, authenticationProvider);
return new JwtAuthorizationFilter(jwtUtil, userDetailsService, authenticationProvider, userRefreshTokenReader,userRefreshTokenFactory);
}
@Bean
public SecurityFilterChain securityFilterChain(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package com.seoultech.synergybe.system.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.seoultech.synergybe.domain.user.UserRefreshToken;
import com.seoultech.synergybe.domain.user.dto.request.LoginRequest;
import com.seoultech.synergybe.domain.user.repository.UserRefreshTokenFactory;
import com.seoultech.synergybe.domain.user.service.UserRefreshTokenReader;
import com.seoultech.synergybe.system.apiresponse.ApiResponseDto;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Optional;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
Expand All @@ -21,9 +26,13 @@
@Slf4j(topic = "로그인, JWT 생성")
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter{
private final JwtUtil jwtUtil;
private final UserRefreshTokenReader userRefreshTokenReader;
private final UserRefreshTokenFactory userRefreshTokenFactory;

public JwtAuthenticationFilter(JwtUtil jwtUtil) {
public JwtAuthenticationFilter(JwtUtil jwtUtil, UserRefreshTokenReader userRefreshTokenReader, UserRefreshTokenFactory userRefreshTokenFactory) {
this.jwtUtil = jwtUtil;
this.userRefreshTokenReader = userRefreshTokenReader;
this.userRefreshTokenFactory = userRefreshTokenFactory;
setFilterProcessesUrl("/api/v1/users/login");
}

Expand Down Expand Up @@ -60,15 +69,10 @@ public void successfulAuthentication(HttpServletRequest request, HttpServletResp
FilterChain chain, Authentication authentication) throws IOException {
UserDetailsImpl userDetails = ((UserDetailsImpl) authentication.getPrincipal());

// Jwt token 생성 access token 생성
String token = jwtUtil.createToken(userDetails.getUserId(), userDetails.getEmail());
response.addHeader(JwtUtil.AUTHORIZATION_HEADER, token);
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("application/json");
String result = new ObjectMapper().writeValueAsString(
new ApiResponseDto(HttpStatus.OK.value(), "login success")
);

response.getOutputStream().print(result);
handleLoginSuccess(response, userDetails, token);
}

@Override
Expand All @@ -82,4 +86,47 @@ public void unsuccessfulAuthentication(HttpServletRequest request,

response.getOutputStream().print(result);
}

private void handleLoginSuccess(HttpServletResponse response, UserDetailsImpl userDetails, String token) throws IOException {
UserRefreshToken userRefreshToken = getOrGenerate(userDetails.getUserId());

// Add refresh token as a cookie
addRefreshTokenCookie(response, userRefreshToken);

// Add JWT token in the Authorization header
response.addHeader(JwtUtil.AUTHORIZATION_HEADER, token);

// Set response status and content type
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("application/json");
}

private UserRefreshToken getOrGenerate(String userId) {
Optional<UserRefreshToken> userRefreshToken = userRefreshTokenReader.readByUserId(userId);

if (userRefreshToken.isPresent()) {
userRefreshToken.get().updateRefreshToken();
log.info("userRefreshToken" + userRefreshToken.get().getRefreshToken().getRefreshToken());
userRefreshTokenFactory.save(userRefreshToken.get());

return userRefreshToken.get();
} else {
// refresh Token 생성 저장
UserRefreshToken newUserRefreshToken = new UserRefreshToken(userId);
userRefreshTokenFactory.save(newUserRefreshToken);

return newUserRefreshToken;
}
}

private void addRefreshTokenCookie(HttpServletResponse response, UserRefreshToken userRefreshToken) {
String refreshToken = userRefreshToken.getRefreshToken().getRefreshToken();
Cookie cookie = new Cookie("refreshToken", refreshToken);
cookie.setHttpOnly(true);
cookie.setSecure(true); // Set to true if using HTTPS
cookie.setPath("/");
cookie.setMaxAge(2 * 7 * 24 * 60 * 60); // Set expiration time if needed
cookie.setAttribute("SameSite", "Strict"); // Can be "Lax" or "Strict" depending on your requirements
response.addCookie(cookie);
}
}
Loading

0 comments on commit d4cc2d4

Please sign in to comment.