Skip to content

Commit

Permalink
Merge pull request #103 from EWHA-LUX/feature/#40
Browse files Browse the repository at this point in the history
[Feat] 회원관리api에 redis추가, logout 구현
  • Loading branch information
julia-heo authored May 28, 2024
2 parents 6052467 + 9368e09 commit 588422e
Show file tree
Hide file tree
Showing 12 changed files with 297 additions and 133 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ dependencies {
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'

implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'

//FCM
implementation 'com.google.firebase:firebase-admin:9.2.0'
Expand Down
7 changes: 7 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
version: '3'
services:

redis:
image: redis
container_name: redis
ports:
- 6379:6379
restart: always

web:
container_name: web
image: ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPO }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,9 @@
import ewha.lux.once.domain.home.dto.FCMTokenDto;
import ewha.lux.once.domain.home.service.FirebaseCloudMessageService;
import ewha.lux.once.domain.user.dto.*;
import ewha.lux.once.domain.user.entity.Users;
import ewha.lux.once.domain.user.service.UserService;
import ewha.lux.once.global.common.CommonResponse;
import ewha.lux.once.global.common.CustomException;
import ewha.lux.once.global.common.ResponseCode;
import ewha.lux.once.global.common.UserAccount;
import ewha.lux.once.global.security.JwtProvider;
import ewha.lux.once.global.common.*;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.data.repository.query.Param;
import org.springframework.http.MediaType;
Expand All @@ -18,6 +14,7 @@
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import ewha.lux.once.global.security.JwtProvider;
import java.io.IOException;
import java.text.ParseException;

Expand All @@ -35,13 +32,7 @@ public class UserController {
@PostMapping("/signup")
public CommonResponse<?> signup(@RequestBody SignupRequestDto request) throws ParseException {
try {
Users users = userService.signup(request);

String accessToken = jwtProvider.generateAccessToken(users.getLoginId());
String refreshToken = jwtProvider.generateRefreshToken(users.getLoginId());

LoginResponseDto loginResponseDto = new LoginResponseDto(users.getId(), accessToken, refreshToken);
return new CommonResponse<>(ResponseCode.SUCCESS, loginResponseDto);
return new CommonResponse<>(ResponseCode.SUCCESS, userService.signup(request));
} catch (CustomException e) {
return new CommonResponse<>(e.getStatus());
}
Expand All @@ -51,19 +42,31 @@ public CommonResponse<?> signup(@RequestBody SignupRequestDto request) throws Pa
@PostMapping("/login")
public CommonResponse<?> signin(@RequestBody SignInRequestDto request) {
try {
Users user = userService.authenticate(request);

String accessToken = jwtProvider.generateAccessToken(user.getLoginId());
String refreshToken = jwtProvider.generateRefreshToken(user.getLoginId());

LoginResponseDto loginResponseDto = new LoginResponseDto(user.getId(), accessToken, refreshToken);
return new CommonResponse<>(ResponseCode.SUCCESS, userService.authenticate(request));
} catch (CustomException e) {
return new CommonResponse<>(e.getStatus());
}
}
// [Post] 자동로그인
@PostMapping("/auto")
public CommonResponse<?> autologinPage() {
return new CommonResponse<>(ResponseCode.VALID_ACCESS_TOKEN);
}

return new CommonResponse<>(ResponseCode.SUCCESS, loginResponseDto);
// [Post] 로그아웃
@PostMapping("/logout")
@ResponseBody
public CommonResponse<?> logoutPage(HttpServletRequest request,@AuthenticationPrincipal UserAccount userAccount) {
try {
userService.postLogout(request, userAccount.getUsers());
return new CommonResponse<>(ResponseCode.SUCCESS);
} catch (CustomException e) {
return new CommonResponse<>(e.getStatus());
}
}



// [Delete] 회원 탈퇴
@DeleteMapping("/quit")
@ResponseBody
Expand Down
38 changes: 38 additions & 0 deletions src/main/java/ewha/lux/once/domain/user/service/RedisService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package ewha.lux.once.domain.user.service;

import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor
public class RedisService {
private final RedisTemplate redisTemplate;
private static final String REFRESH_TOKEN_PREFIX = "refreshToken:";
private static final String ACCESS_TOKEN_BLACKLIST_PREFIX = "blacklistAccessToken:";


public void setRefreshValueWithTTL(String user_id, String refreshToken, long timeout, TimeUnit unit) {
String key = REFRESH_TOKEN_PREFIX + user_id;
ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
valueOperations.set(key, refreshToken, timeout, unit);
}
public void setAccessBlackValueWithTTL(String accessToken, String value, long timeout, TimeUnit unit) {
String key = ACCESS_TOKEN_BLACKLIST_PREFIX + accessToken;
ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
valueOperations.set(key, value, timeout, unit);
}

public Object getValue(String key) {
ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
Object object = valueOperations.get(key);
return object;
}

public void deleteValue(String key) {
redisTemplate.delete(key);
}
}
45 changes: 41 additions & 4 deletions src/main/java/ewha/lux/once/domain/user/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@
import ewha.lux.once.domain.user.entity.Users;
import ewha.lux.once.global.common.CustomException;
import ewha.lux.once.global.common.ResponseCode;
import ewha.lux.once.global.security.JwtAuthFilter;
import ewha.lux.once.global.security.JwtProvider;
import jakarta.servlet.http.HttpServletRequest;
import ewha.lux.once.global.repository.*;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
Expand All @@ -24,7 +28,9 @@
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

import java.util.stream.Collectors;
import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor
Expand All @@ -34,14 +40,22 @@ public class UserService implements UserDetailsService {
private final CardRepository cardRepository;
private final CardCompanyRepository cardCompanyRepository;
private final OwnedCardRepository ownedCardRepository;

private final JwtAuthFilter jwtAuthFilter;
private final JwtProvider jwtProvider;

private final AnnouncementRepository announcementRepository;
private final ChatHistoryRepository chatHistoryRepository;
private final ConnectedCardCompanyRepository connectedCardCompanyRepository;
private final FavoriteRepository favoriteRepository;
private final FCMTokenRepository fcmTokenRepository;

private final S3Uploader s3Uploader;
private final RedisTemplate redisTemplate;
private final RedisService redisService;
private static final String REFRESH_TOKEN_PREFIX = "refreshToken:";

public Users signup(SignupRequestDto request) throws CustomException, ParseException {
public LoginResponseDto signup(SignupRequestDto request) throws CustomException, ParseException {
String loginId = request.getLoginId();
String username = request.getUsername();
String password = request.getPassword();
Expand Down Expand Up @@ -77,10 +91,18 @@ public Users signup(SignupRequestDto request) throws CustomException, ParseExcep
usersBuilder.birthday(birthday);
}

return usersRepository.save(usersBuilder.benefitGoal(100000).build());
Users newUser = usersRepository.save(usersBuilder.benefitGoal(100000).build());

String accessToken = jwtProvider.createAccessToken(newUser.getLoginId());
String refreshToken = jwtProvider.createRefreshToken(newUser.getLoginId());

LoginResponseDto loginResponseDto = new LoginResponseDto(newUser.getId(), accessToken, refreshToken);
redisService.setRefreshValueWithTTL(newUser.getId().toString(), refreshToken, 14L, TimeUnit.DAYS);

return loginResponseDto;
}

public Users authenticate(SignInRequestDto request) throws CustomException {
public LoginResponseDto authenticate(SignInRequestDto request) throws CustomException {
String loginId = request.getLoginId();
String password = request.getPassword();

Expand All @@ -93,7 +115,22 @@ public Users authenticate(SignInRequestDto request) throws CustomException {

users.setLastLogin();
usersRepository.save(users);
return users;

String accessToken = jwtProvider.createAccessToken(users.getLoginId());
String refreshToken = jwtProvider.createRefreshToken(users.getLoginId());
LoginResponseDto loginResponseDto = new LoginResponseDto(users.getId(), accessToken, refreshToken);
redisService.setRefreshValueWithTTL(users.getId().toString(), refreshToken, 14L, TimeUnit.DAYS);

return loginResponseDto;
}

public void postLogout(HttpServletRequest request, Users nowuser) throws CustomException {
String accessToken = jwtAuthFilter.resolveToken(request,jwtAuthFilter.HEADER_KEY);
Long expiration = jwtProvider.getExpiration(accessToken);
redisService.setAccessBlackValueWithTTL(accessToken,"logout",expiration, TimeUnit.MILLISECONDS);
// 리프레시 토큰 삭제
redisService.deleteValue(REFRESH_TOKEN_PREFIX+nowuser.getId().toString());
System.out.println("왕왕왕왕왕");
}

public void deleteUsers(Users nowUser) throws CustomException {
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/ewha/lux/once/global/common/ResponseCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ public enum ResponseCode {
CHANGE_MYPAGE_SUCCESS(1002, true, "내 정보 수정을 성공했습니다."),
RELEASE_MAINCARD_SUCCESS(1003, true, "주카드 해제를 성공했습니다."),
DELETE_CARD_SUCCESS(1004, true, "등록 카드 삭제에 성공했습니다."),
VALID_ACCESS_TOKEN(1005, true, "유효한 access token입니다."),

/*
2000~ : Request 오류
*/


// =====================================
/*
3000~ : Response 오류
Expand All @@ -26,6 +28,10 @@ public enum ResponseCode {
INVALID_USER_ID(3001, false, "아이디가 존재하지 않습니다."),
FAILED_TO_LOGIN(3002, false, "비밀번호가 일치하지 않습니다."),
DUPLICATED_USER_NAME(3003, false,"이미 존재하는 아이디입니다."),
UNAUTHORIZED_REFRESH(3004, false, "refresh token이 만료되었습니다. 다시 로그인해주세요"),
UNAUTHORIZED(3005, false, "access token이 유효하지 않습니다."),
INVALID_REFRESH(3006, false, "refresh token이 유효하지 않습니다."),
BLACKLISTED_TOKEN(3007, false, "블랙리스트에 등록된 토큰입니다."),

// 3100~ : card 관련 오류
CARD_NOT_FOUND(3100, false,"존재하지 않는 카드입니다."),
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/ewha/lux/once/global/config/JwtSecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ewha.lux.once.global.config;

import ewha.lux.once.domain.user.service.RedisService;
import ewha.lux.once.global.security.JwtAuthFilter;
import ewha.lux.once.global.security.JwtProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@RequiredArgsConstructor
public class JwtSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private final JwtProvider jwtProvider;
private final RedisService redisService;

@Override
public void configure(HttpSecurity http) throws Exception{
http.addFilterBefore(new JwtAuthFilter(jwtProvider, redisService), UsernamePasswordAuthenticationFilter.class);
}
}
34 changes: 34 additions & 0 deletions src/main/java/ewha/lux/once/global/config/RedisConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package ewha.lux.once.global.config;

import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@RequiredArgsConstructor
@Configuration
@EnableRedisRepositories
public class RedisConfig {
private final RedisProperties redisProperties;

@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(redisProperties.getHost(), redisProperties.getPort());
// return new LettuceConnectionFactory("localhost", redisProperties.getPort()); // 로컬
}

@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
return redisTemplate;
}

}
7 changes: 5 additions & 2 deletions src/main/java/ewha/lux/once/global/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ewha.lux.once.global.config;

import ewha.lux.once.domain.user.service.RedisService;
import ewha.lux.once.global.security.JwtAuthFilter;
import ewha.lux.once.global.security.JwtProvider;
import lombok.RequiredArgsConstructor;
Expand All @@ -25,7 +26,7 @@
@Configuration
public class SecurityConfig {
private final JwtProvider jwtProvider;

private final RedisService redisService;

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
Expand Down Expand Up @@ -57,10 +58,12 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.requestMatchers("/user/duplicate").permitAll()
.requestMatchers("/user/login").permitAll()
.requestMatchers("/user/auto").permitAll()
.requestMatchers("/user/card/search").permitAll()
.requestMatchers("/user/logout").permitAll()
.requestMatchers("/user/card/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(new JwtAuthFilter(jwtProvider), UsernamePasswordAuthenticationFilter.class);
.addFilterBefore(new JwtAuthFilter(jwtProvider, redisService), UsernamePasswordAuthenticationFilter.class);


return http.build();
Expand Down
Loading

0 comments on commit 588422e

Please sign in to comment.