Skip to content

Commit

Permalink
✨ feat: Refresh 토큰 배포
Browse files Browse the repository at this point in the history
✨ feat: Refresh 토큰 배포
  • Loading branch information
sunnyineverywhere authored Sep 22, 2023
2 parents e603a9e + efa8292 commit 5197176
Show file tree
Hide file tree
Showing 22 changed files with 198 additions and 30 deletions.
16 changes: 16 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
version: '3'

services:
redis:
image: redis:alpine
ports:
- 6379:6379
qcard-server:
platform: linux/amd64
build:
context: .
dockerfile: ./Dockerfile
ports:
- 8080:8080
depends_on:
- redis
Empty file modified gradlew
100644 → 100755
Empty file.
2 changes: 2 additions & 0 deletions q-admin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'

implementation 'org.springframework.boot:spring-boot-starter-data-redis'

implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'
Expand Down
2 changes: 1 addition & 1 deletion q-admin/src/main/java/com/qcard/config/WebConfig.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.qcard.config;

import com.qcard.auth.AuthAccountArgumentResolver;
import com.qcard.resolver.AuthAccountArgumentResolver;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand Down
2 changes: 1 addition & 1 deletion q-admin/src/main/java/com/qcard/jwt/JwtFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Objects;

@RequiredArgsConstructor
@Slf4j
Expand Down Expand Up @@ -44,5 +45,4 @@ private String resolveToken(HttpServletRequest request) {

return null;
}

}
14 changes: 13 additions & 1 deletion q-admin/src/main/java/com/qcard/jwt/JwtService.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,36 @@
package com.qcard.jwt;

import com.qcard.redis.RedisService;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.time.Duration;

@Service
@RequiredArgsConstructor
public class JwtService {
private final JwtUtil jwtUtil;
private final AuthenticationManagerBuilder authenticationManagerBuilder;
private final PasswordEncoder passwordEncoder;
private final RedisService redisService;

public TokenRes createJwt(String email, String password) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(email, password);
Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
TokenRes tokenRes = jwtUtil.generateToken(authentication);
redisService.setValues(tokenRes.getRefreshToken(), email, Duration.ofDays(14));

return jwtUtil.generateToken(authentication);
return tokenRes;
}

public TokenRes reissueJwt(String email, String password, String refreshToken) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(email, password);
Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);

return jwtUtil.reissueToken(authentication, refreshToken);
}

public String encryptPassword(String password) {
Expand Down
20 changes: 20 additions & 0 deletions q-admin/src/main/java/com/qcard/jwt/JwtUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,25 @@ public TokenRes generateToken(Authentication authentication) {
.build();
}

public TokenRes reissueToken(Authentication authentication, String refreshToken) {
Long current = (new Date()).getTime();

String authorities = authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(","));

String accessToken = Jwts.builder()
.setSubject(authentication.getName())
.claim(AUTHORITIES_KEY, authorities)
.setExpiration(new Date(current + ACCESS_TOKEN_EXPIRE_TIME))
.signWith(key, SignatureAlgorithm.HS256).compact();

return TokenRes.builder()
.accessToken(accessToken)
.refreshToken(refreshToken)
.build();
}

public Authentication getAuthentication(String accessToken) {
Claims claims = parseClaims(accessToken);

Expand Down Expand Up @@ -84,6 +103,7 @@ public Long getExpiration(String accessToken) {
return (expirationDate.getTime() - (new Date()).getTime());
}


public boolean validateToken(String token) {
try {
Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
Expand Down
34 changes: 34 additions & 0 deletions q-admin/src/main/java/com/qcard/redis/RedisConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.qcard.redis;

import org.springframework.beans.factory.annotation.Value;
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.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

@Value("${spring.data.redis.host}")
private String host;

@Value("${spring.data.redis.port}")
private int port;

@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(host, port);
}

@Bean
public RedisTemplate<String, String> redisTemplate() {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
// 아래 두 라인을 작성하지 않으면, key값이 \xac\xed\x00\x05t\x00\x03sol 이렇게 조회된다.
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory());
return redisTemplate;
}
}
34 changes: 34 additions & 0 deletions q-admin/src/main/java/com/qcard/redis/RedisService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.qcard.redis;

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

import java.time.Duration;

@Service
@RequiredArgsConstructor
public class RedisService {
private final RedisTemplate<String, String> redisTemplate;

public void setValues(String key, String data) {
ValueOperations<String, String> values = redisTemplate.opsForValue();
values.set(key, data);
}

public void setValues(String key, String data, Duration duration) {
ValueOperations<String, String> values = redisTemplate.opsForValue();
values.set(key, data, duration);
}

public String getValues(String key) {
ValueOperations<String, String> values = redisTemplate.opsForValue();
return values.get(key);
}

public void deleteValues(String key) {
redisTemplate.delete(key);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.qcard.auth;
package com.qcard.resolver;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.qcard.auth;
package com.qcard.resolver;

import com.qcard.domains.account.service.AccountDomainService;
import lombok.RequiredArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.qcard;
package com.qcard.service;

import com.qcard.domains.account.entity.Account;
import com.qcard.domains.account.service.AccountDomainService;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package com.qcard.api.account.controller;

import com.qcard.api.account.dto.SignInReq;
import com.qcard.api.account.dto.SignUpRes;
import com.qcard.api.account.dto.*;
import com.qcard.domains.account.entity.Account;
import com.qcard.domains.account.service.AccountDomainService;
import com.qcard.api.account.dto.AccountReq;
import com.qcard.api.account.dto.AccountRes;
import com.qcard.api.account.service.AccountService;
import com.qcard.auth.AuthAccount;
import com.qcard.resolver.AuthAccount;
import com.qcard.jwt.TokenRes;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -23,6 +21,7 @@
public class AccountController {
private final AccountService accountService;
private final AccountDomainService accountDomainService;
private static final String ACCESS_HEADER = "Authorization";

@PostMapping("/signup")
public ResponseEntity<SignUpRes> signUp(@Valid @RequestBody AccountReq accountReq) {
Expand All @@ -36,6 +35,18 @@ public ResponseEntity<TokenRes> signIn(@Valid @RequestBody SignInReq signInReq)
return new ResponseEntity<>(response, HttpStatus.OK);
}

@GetMapping("/reissue")
public ResponseEntity<TokenRes> accessTokenReissue(HttpServletRequest request) {
TokenRes response = accountService.reissueToken(request.getHeader(ACCESS_HEADER));
return new ResponseEntity<>(response, HttpStatus.OK);
}

@GetMapping("/logout")
public ResponseEntity<LogOutRes> logOut(HttpServletRequest request) {
LogOutRes response = accountService.logout(request.getHeader(ACCESS_HEADER));
return new ResponseEntity<>(response, HttpStatus.OK);
}

@GetMapping("/profile")
public ResponseEntity<AccountRes> myAccountInfo(@AuthAccount Account account) {
AccountRes response = new AccountRes(account.getName(), account.getEmail());
Expand Down
16 changes: 16 additions & 0 deletions q-api/src/main/java/com/qcard/api/account/dto/LogOutRes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.qcard.api.account.dto;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class LogOutRes {
private String email;
private String message;

public LogOutRes(String email) {
this.email = email;
this.message = "성공적으로 로그아웃했습니다.";
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
package com.qcard.api.account.service;

import com.qcard.api.account.dto.SignInReq;
import com.qcard.api.account.dto.SignUpRes;
import com.qcard.api.account.dto.*;
import com.qcard.domains.account.entity.Account;
import com.qcard.domains.account.service.AccountDomainService;
import com.qcard.api.account.dto.AccountReq;
import com.qcard.api.account.dto.AccountRes;
import com.qcard.jwt.JwtService;
import com.qcard.jwt.TokenRes;
import com.qcard.redis.RedisService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Service
@Slf4j
@RequiredArgsConstructor
public class AccountService {
private final AccountDomainService accountDomainService;
private final JwtService jwtService;
private final RedisService redisService;

public SignUpRes signUp(AccountReq accountReq) {
if (!accountReq.isValid()) {
Expand All @@ -43,4 +44,27 @@ public TokenRes signIn(SignInReq signInReq) {
throw new IllegalArgumentException("비밀번호가 올바르지 않습니다.");
}
}

public TokenRes reissueToken(String refreshToken) {
String email = redisService.getValues(refreshToken);
if(email != null) {
Account account = accountDomainService.findAccountByEmail(email);
return jwtService.reissueJwt(account.getEmail(), account.getPassword(), refreshToken);
}
else {
throw new IllegalArgumentException("잘못된 refresh token입니다.");
}
}

public LogOutRes logout(String refreshToken) {
String email = redisService.getValues(refreshToken);
log.info("email: " + email);
if(email != null){
redisService.deleteValues(refreshToken);
return new LogOutRes(email);
}
else{
throw new IllegalArgumentException("잘못된 refresh token입니다.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,10 @@
import com.qcard.api.account.service.AccountService;
import com.qcard.api.answer.dto.*;
import com.qcard.api.answer.service.AnswerService;
import com.qcard.api.heart.dto.HeartReq;
import com.qcard.api.heart.dto.HeartRes;
import com.qcard.auth.AuthAccount;
import com.qcard.resolver.AuthAccount;
import com.qcard.domains.account.entity.Account;
import com.qcard.domains.question.entity.Answer;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
@NoArgsConstructor
public class AnswerMeRes {
private Question question;

private Long answerId;
private Type type;
private AccountRes account;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.qcard.api.heart.dto.HeartRes;
import com.qcard.api.heart.service.HeartService;
import com.qcard.auth.AuthAccount;
import com.qcard.resolver.AuthAccount;
import com.qcard.domains.account.entity.Account;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
package com.qcard.api.question.controller;

import com.qcard.api.answer.dto.AnswerRes;
import com.qcard.api.answer.service.AnswerService;
import com.qcard.api.question.dto.QuestionDetailRes;
import com.qcard.api.question.dto.QuestionMainRes;
import com.qcard.api.question.dto.QuestionRes;
import com.qcard.api.question.service.QuestionService;
import com.qcard.auth.AuthAccount;
import com.qcard.resolver.AuthAccount;
import com.qcard.common.enums.Category;
import com.qcard.domains.account.entity.Account;
import com.qcard.domains.question.entity.Answer;
import lombok.RequiredArgsConstructor;
import org.apache.coyote.Response;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,11 @@ public class Account {
@Column
private String password;

@Column
private Boolean isDeleted;

@Builder
public Account(String name, String email, String password) {
this.name = name;
this.email = email;
this.password = password;
this.isDeleted = Boolean.FALSE;
}

}
Loading

0 comments on commit 5197176

Please sign in to comment.