Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.example.solidconnection.auth.dto.ReissueRequest;
import com.example.solidconnection.auth.dto.ReissueResponse;
import com.example.solidconnection.auth.token.TokenBlackListService;
import com.example.solidconnection.common.exception.CustomException;
import com.example.solidconnection.siteuser.domain.SiteUser;
import lombok.RequiredArgsConstructor;
Expand All @@ -17,6 +18,7 @@
public class AuthService {

private final AuthTokenProvider authTokenProvider;
private final TokenBlackListService tokenBlackListService;

/*
* 로그아웃한다.
Expand All @@ -26,7 +28,7 @@ public class AuthService {
public void signOut(String token) {
AccessToken accessToken = authTokenProvider.toAccessToken(token);
authTokenProvider.deleteRefreshTokenByAccessToken(accessToken);
authTokenProvider.addToBlacklist(accessToken);
tokenBlackListService.addToBlacklist(accessToken);
}

/*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,49 +1,38 @@
package com.example.solidconnection.auth.service;

import com.example.solidconnection.auth.domain.TokenType;
import com.example.solidconnection.security.config.JwtProperties;
import com.example.solidconnection.siteuser.domain.SiteUser;
import com.example.solidconnection.util.JwtUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Objects;

@Component
public class AuthTokenProvider extends TokenProvider implements BlacklistChecker {
@RequiredArgsConstructor
public class AuthTokenProvider {

public AuthTokenProvider(JwtProperties jwtProperties, RedisTemplate<String, String> redisTemplate) {
super(jwtProperties, redisTemplate);
}
private final RedisTemplate<String, String> redisTemplate;
private final TokenProvider tokenProvider;

public AccessToken generateAccessToken(Subject subject) {
String token = generateToken(subject.value(), TokenType.ACCESS);
String token = tokenProvider.generateToken(subject.value(), TokenType.ACCESS);
return new AccessToken(subject, token);
}

public RefreshToken generateAndSaveRefreshToken(Subject subject) {
String token = generateToken(subject.value(), TokenType.REFRESH);
saveToken(token, TokenType.REFRESH);
String token = tokenProvider.generateToken(subject.value(), TokenType.REFRESH);
tokenProvider.saveToken(token, TokenType.REFRESH);
return new RefreshToken(subject, token);
}

/*
* 액세스 토큰을 블랙리스트에 저장한다.
* - key = BLACKLIST:{accessToken}
* - value = "signOut" -> key 의 존재만 확인하므로, value 에는 무슨 값이 들어가도 상관없다.
* */
public void addToBlacklist(AccessToken accessToken) {
String blackListKey = TokenType.BLACKLIST.addPrefix(accessToken.token());
redisTemplate.opsForValue().set(blackListKey, "signOut");
}

/*
* 유효한 리프레시 토큰인지 확인한다.
* - 요청된 토큰과 같은 subject 의 리프레시 토큰을 조회한다.
* - 조회된 리프레시 토큰과 요청된 토큰이 같은지 비교한다.
* */
* 유효한 리프레시 토큰인지 확인한다.
* - 요청된 토큰과 같은 subject 의 리프레시 토큰을 조회한다.
* - 조회된 리프레시 토큰과 요청된 토큰이 같은지 비교한다.
* */
public boolean isValidRefreshToken(String requestedRefreshToken) {
String subject = JwtUtils.parseSubject(requestedRefreshToken, jwtProperties.secret());
String subject = tokenProvider.parseSubject(requestedRefreshToken);
String refreshTokenKey = TokenType.REFRESH.addPrefix(subject);
String foundRefreshToken = redisTemplate.opsForValue().get(refreshTokenKey);
return Objects.equals(requestedRefreshToken, foundRefreshToken);
Expand All @@ -55,14 +44,8 @@ public void deleteRefreshTokenByAccessToken(AccessToken accessToken) {
redisTemplate.delete(refreshTokenKey);
}

@Override
public boolean isTokenBlacklisted(String accessToken) {
String blackListTokenKey = TokenType.BLACKLIST.addPrefix(accessToken);
return redisTemplate.hasKey(blackListTokenKey);
}

public Subject parseSubject(String token) {
String subject = JwtUtils.parseSubject(token, jwtProperties.secret());
String subject = tokenProvider.parseSubject(token);
return new Subject(subject);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package com.example.solidconnection.auth.service;

import com.example.solidconnection.common.exception.CustomException;
import com.example.solidconnection.security.config.JwtProperties;
import com.example.solidconnection.siteuser.domain.AuthType;
import com.example.solidconnection.util.JwtUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

Expand All @@ -14,11 +12,11 @@
@RequiredArgsConstructor
public class CommonSignUpTokenProvider {

private final JwtProperties jwtProperties;
private final TokenProvider tokenProvider;

public AuthType parseAuthType(String signUpToken) {
try {
String authTypeStr = JwtUtils.parseClaims(signUpToken, jwtProperties.secret()).get(AUTH_TYPE_CLAIM_KEY, String.class);
String authTypeStr = tokenProvider.parseClaims(signUpToken).get(AUTH_TYPE_CLAIM_KEY, String.class);
return AuthType.valueOf(authTypeStr);
} catch (Exception e) {
throw new CustomException(SIGN_UP_TOKEN_INVALID);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

import com.example.solidconnection.auth.domain.TokenType;
import com.example.solidconnection.auth.dto.EmailSignUpTokenRequest;
import com.example.solidconnection.auth.token.config.JwtProperties;
import com.example.solidconnection.common.exception.CustomException;
import com.example.solidconnection.security.config.JwtProperties;
import com.example.solidconnection.siteuser.domain.AuthType;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
Expand All @@ -19,22 +20,18 @@

import static com.example.solidconnection.common.exception.ErrorCode.SIGN_UP_TOKEN_INVALID;
import static com.example.solidconnection.common.exception.ErrorCode.SIGN_UP_TOKEN_NOT_ISSUED_BY_SERVER;
import static com.example.solidconnection.util.JwtUtils.parseClaims;
import static com.example.solidconnection.util.JwtUtils.parseSubject;

@Component
public class EmailSignUpTokenProvider extends TokenProvider {
@RequiredArgsConstructor
public class EmailSignUpTokenProvider {

static final String PASSWORD_CLAIM_KEY = "password";
static final String AUTH_TYPE_CLAIM_KEY = "authType";

private final PasswordEncoder passwordEncoder;

public EmailSignUpTokenProvider(JwtProperties jwtProperties, RedisTemplate<String, String> redisTemplate,
PasswordEncoder passwordEncoder) {
super(jwtProperties, redisTemplate);
this.passwordEncoder = passwordEncoder;
}
private final JwtProperties jwtProperties;
private final RedisTemplate<String, String> redisTemplate;
private final TokenProvider tokenProvider;

public String generateAndSaveSignUpToken(EmailSignUpTokenRequest request) {
String email = request.email();
Expand All @@ -54,7 +51,7 @@ public String generateAndSaveSignUpToken(EmailSignUpTokenRequest request) {
.setExpiration(expiredDate)
.signWith(SignatureAlgorithm.HS512, jwtProperties.secret())
.compact();
return saveToken(signUpToken, TokenType.SIGN_UP);
return tokenProvider.saveToken(signUpToken, TokenType.SIGN_UP);
}

public void validateSignUpToken(String token) {
Expand All @@ -65,7 +62,7 @@ public void validateSignUpToken(String token) {

private void validateFormatAndExpiration(String token) {
try {
Claims claims = parseClaims(token, jwtProperties.secret());
Claims claims = tokenProvider.parseClaims(token);
Objects.requireNonNull(claims.getSubject());
String encodedPassword = claims.get(PASSWORD_CLAIM_KEY, String.class);
Objects.requireNonNull(encodedPassword);
Expand All @@ -82,11 +79,11 @@ private void validateIssuedByServer(String email) {
}

public String parseEmail(String token) {
return parseSubject(token, jwtProperties.secret());
return tokenProvider.parseSubject(token);
}

public String parseEncodedPassword(String token) {
Claims claims = parseClaims(token, jwtProperties.secret());
Claims claims = tokenProvider.parseClaims(token);
return claims.get(PASSWORD_CLAIM_KEY, String.class);
}
}
Original file line number Diff line number Diff line change
@@ -1,47 +1,15 @@
package com.example.solidconnection.auth.service;

import com.example.solidconnection.auth.domain.TokenType;
import com.example.solidconnection.security.config.JwtProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Date;
import java.util.concurrent.TimeUnit;
public interface TokenProvider {

import static com.example.solidconnection.util.JwtUtils.parseSubject;
String generateToken(String string, TokenType tokenType);

public abstract class TokenProvider {
String saveToken(String token, TokenType tokenType);

protected final JwtProperties jwtProperties;
protected final RedisTemplate<String, String> redisTemplate;
String parseSubject(String token);

public TokenProvider(JwtProperties jwtProperties, RedisTemplate<String, String> redisTemplate) {
this.jwtProperties = jwtProperties;
this.redisTemplate = redisTemplate;
}

protected final String generateToken(String string, TokenType tokenType) {
Claims claims = Jwts.claims().setSubject(string);
Date now = new Date();
Date expiredDate = new Date(now.getTime() + tokenType.getExpireTime());
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(expiredDate)
.signWith(SignatureAlgorithm.HS512, jwtProperties.secret())
.compact();
}

protected final String saveToken(String token, TokenType tokenType) {
String subject = parseSubject(token, jwtProperties.secret());
redisTemplate.opsForValue().set(
tokenType.addPrefix(subject),
token,
tokenType.getExpireTime(),
TimeUnit.MILLISECONDS
);
return token;
}
Claims parseClaims(String token);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

import com.example.solidconnection.auth.domain.TokenType;
import com.example.solidconnection.auth.service.TokenProvider;
import com.example.solidconnection.auth.token.config.JwtProperties;
import com.example.solidconnection.common.exception.CustomException;
import com.example.solidconnection.security.config.JwtProperties;
import com.example.solidconnection.siteuser.domain.AuthType;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

Expand All @@ -18,17 +19,16 @@

import static com.example.solidconnection.common.exception.ErrorCode.SIGN_UP_TOKEN_INVALID;
import static com.example.solidconnection.common.exception.ErrorCode.SIGN_UP_TOKEN_NOT_ISSUED_BY_SERVER;
import static com.example.solidconnection.util.JwtUtils.parseClaims;
import static com.example.solidconnection.util.JwtUtils.parseSubject;

@Component
public class OAuthSignUpTokenProvider extends TokenProvider {
@RequiredArgsConstructor
public class OAuthSignUpTokenProvider {

static final String AUTH_TYPE_CLAIM_KEY = "authType";

public OAuthSignUpTokenProvider(JwtProperties jwtProperties, RedisTemplate<String, String> redisTemplate) {
super(jwtProperties, redisTemplate);
}
private final JwtProperties jwtProperties;
private final RedisTemplate<String, String> redisTemplate;
private final TokenProvider tokenProvider;

public String generateAndSaveSignUpToken(String email, AuthType authType) {
Map<String, Object> authTypeClaim = new HashMap<>(Map.of(AUTH_TYPE_CLAIM_KEY, authType));
Expand All @@ -42,7 +42,7 @@ public String generateAndSaveSignUpToken(String email, AuthType authType) {
.setExpiration(expiredDate)
.signWith(SignatureAlgorithm.HS512, jwtProperties.secret())
.compact();
return saveToken(signUpToken, TokenType.SIGN_UP);
return tokenProvider.saveToken(signUpToken, TokenType.SIGN_UP);
}

public void validateSignUpToken(String token) {
Expand All @@ -53,7 +53,7 @@ public void validateSignUpToken(String token) {

private void validateFormatAndExpiration(String token) {
try {
Claims claims = parseClaims(token, jwtProperties.secret());
Claims claims = tokenProvider.parseClaims(token);
Objects.requireNonNull(claims.getSubject());
String serializedAuthType = claims.get(AUTH_TYPE_CLAIM_KEY, String.class);
AuthType.valueOf(serializedAuthType);
Expand All @@ -70,11 +70,11 @@ private void validateIssuedByServer(String email) {
}

public String parseEmail(String token) {
return parseSubject(token, jwtProperties.secret());
return tokenProvider.parseSubject(token);
}

public AuthType parseAuthType(String token) {
Claims claims = parseClaims(token, jwtProperties.secret());
Claims claims = tokenProvider.parseClaims(token);
String authTypeStr = claims.get(AUTH_TYPE_CLAIM_KEY, String.class);
return AuthType.valueOf(authTypeStr);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.example.solidconnection.auth.token;

import com.example.solidconnection.auth.domain.TokenType;
import com.example.solidconnection.auth.service.TokenProvider;
import com.example.solidconnection.auth.token.config.JwtProperties;
import com.example.solidconnection.common.exception.CustomException;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.concurrent.TimeUnit;

import static com.example.solidconnection.common.exception.ErrorCode.INVALID_TOKEN;

@Component
@RequiredArgsConstructor
public class JwtTokenProvider implements TokenProvider {

private final JwtProperties jwtProperties;
private final RedisTemplate<String, String> redisTemplate;

@Override
public final String generateToken(String string, TokenType tokenType) {
Claims claims = Jwts.claims().setSubject(string);
Date now = new Date();
Date expiredDate = new Date(now.getTime() + tokenType.getExpireTime());
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(expiredDate)
.signWith(SignatureAlgorithm.HS512, jwtProperties.secret())
.compact();
}

@Override
public final String saveToken(String token, TokenType tokenType) {
String subject = parseSubject(token);
redisTemplate.opsForValue().set(
tokenType.addPrefix(subject),
token,
tokenType.getExpireTime(),
TimeUnit.MILLISECONDS
);
return token;
}

@Override
public String parseSubject(String token) {
return parseClaims(token).getSubject();
}

@Override
public Claims parseClaims(String token) {
try {
return Jwts.parser()
.setSigningKey(jwtProperties.secret())
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
throw new CustomException(INVALID_TOKEN);
}
}
}
Loading
Loading