Skip to content

Commit

Permalink
Merge pull request #38 from gooiman/feat/logout
Browse files Browse the repository at this point in the history
[Feat] logout 추가
  • Loading branch information
koosco authored Sep 28, 2024
2 parents 832400d + 58f8644 commit 8597db2
Show file tree
Hide file tree
Showing 14 changed files with 117 additions and 25 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.+")
implementation("com.fasterxml.jackson.core:jackson-databind:2.17.+")
implementation("org.springframework.boot:spring-boot-starter-data-redis")
compileOnly("org.projectlombok:lombok")
developmentOnly("org.springframework.boot:spring-boot-devtools")
developmentOnly("org.springframework.boot:spring-boot-docker-compose")
Expand Down
10 changes: 6 additions & 4 deletions docker-compose.bootrun.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ services:
timeout: 10s
ports:
- "3306:3306"
# redis:
# image: redis:alpine
# command: redis-server --port 6379
# container_name: redis.gooiman.internal
redis:
image: redis:alpine
command: redis-server
container_name: redis.gooiman.internal
ports:
- "6379:6379"
sqlpad:
image: sqlpad/sqlpad
ports:
Expand Down
4 changes: 3 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ services:
timeout: 10s
redis:
image: redis:alpine
command: redis-server --port 6379
command: redis-server
ports:
- "6379:6379"
container_name: redis.gooiman.internal
api:
build:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package dev.gooiman.server.auth.application;

import dev.gooiman.server.page.application.PageService;
import dev.gooiman.server.page.repository.entity.Page;
import dev.gooiman.server.auth.application.domain.CustomUserDetails;
import dev.gooiman.server.auth.application.dto.JwtResponseDto;
import dev.gooiman.server.auth.application.dto.LoginRequestDto;
import dev.gooiman.server.config.security.provider.CustomAuthenticationProvider;
import dev.gooiman.server.auth.application.domain.CustomUserDetails;
import dev.gooiman.server.auth.repository.UserRepository;
import dev.gooiman.server.auth.repository.entity.User;
import dev.gooiman.server.common.dto.CommonSuccessDto;
import dev.gooiman.server.config.security.provider.CustomAuthenticationProvider;
import dev.gooiman.server.page.application.PageService;
import dev.gooiman.server.page.repository.entity.Page;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
Expand All @@ -21,10 +22,11 @@

@Service
@RequiredArgsConstructor
public class CustomAuthenticationService {
public class AuthService {

private final JwtAuthenticationService jwtAuthenticationService;
private final JwtService jwtAuthenticationService;
private final CustomAuthenticationProvider authenticationProvider;
private final BlackListService blackListService;
private final PageService pageService;
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
Expand Down Expand Up @@ -55,4 +57,9 @@ public Authentication signup(UUID pageId, String name, String password) {
return new UsernamePasswordAuthenticationToken(new CustomUserDetails(saveUser), null,
List.of(new SimpleGrantedAuthority("ROLE_USER")));
}

@Transactional
public CommonSuccessDto signout(String token) {
return blackListService.saveBlackList(token);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package dev.gooiman.server.auth.application;

import dev.gooiman.server.auth.repository.BlackListRepository;
import dev.gooiman.server.auth.repository.entity.BlackList;
import dev.gooiman.server.common.dto.CommonSuccessDto;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class BlackListService {

private final BlackListRepository blackListRepository;

public boolean isExists(String id) {
String bearerToken = "Bearer " + id;
Optional<BlackList> token = blackListRepository.findById(bearerToken);
return token.isPresent();
}

public CommonSuccessDto saveBlackList(String token) {
BlackList entity = new BlackList(token);
blackListRepository.save(entity);
return CommonSuccessDto.fromEntity(true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

@Service
@RequiredArgsConstructor
public class JwtAuthenticationService {
public class JwtService {

private static SecretKey key;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package dev.gooiman.server.auth.controller;

import dev.gooiman.server.auth.application.CustomAuthenticationService;
import dev.gooiman.server.auth.application.AuthService;
import dev.gooiman.server.auth.application.dto.JwtResponseDto;
import dev.gooiman.server.auth.application.dto.LoginRequestDto;
import dev.gooiman.server.common.dto.CommonSuccessDto;
import dev.gooiman.server.common.dto.ResponseDto;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirements;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PathVariable;
Expand All @@ -21,13 +23,19 @@
@Tag(name = "Authentication", description = "인증 처리 API")
public class AuthController {

private final CustomAuthenticationService authenticationService;
private final AuthService authService;

@PostMapping("/login/{page_id}")
@Operation(summary = "로그인", description = "로그인을 수행합니다. 만약 한번도 로그인 한 적 없는 name으로 로그인을 시도할 경우 회원가입을 수행합니다.")
@SecurityRequirements
public ResponseDto<JwtResponseDto> signIn(@PathVariable("page_id") UUID pageId,
@RequestBody LoginRequestDto dto) {
return ResponseDto.ok(authenticationService.login(pageId, dto));
return ResponseDto.ok(authService.login(pageId, dto));
}

@PostMapping("/logout")
public ResponseDto<CommonSuccessDto> signout(HttpServletRequest request) {
String token = request.getHeader("Authorization");
return ResponseDto.ok(authService.signout(token));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package dev.gooiman.server.auth.repository;

import dev.gooiman.server.auth.repository.entity.BlackList;
import org.springframework.data.repository.CrudRepository;

public interface BlackListRepository extends CrudRepository<BlackList, String> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package dev.gooiman.server.auth.repository.entity;

import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;
import org.springframework.data.redis.core.TimeToLive;

@Getter
@RedisHash(value = "token")
public class BlackList {

@Id
private String token;

@TimeToLive
@Value("${spring.security.blacklist-validity-time}")
private Long expiration;

public BlackList(String token) {
this.token = token;
}
}
12 changes: 9 additions & 3 deletions src/main/java/dev/gooiman/server/config/security/JwtConfig.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package dev.gooiman.server.config.security;

import dev.gooiman.server.config.security.fliter.JwtFilter;
import dev.gooiman.server.config.security.provider.JwtAuthenticationProvider;
import dev.gooiman.server.config.security.provider.JwtProvider;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
Expand All @@ -10,9 +12,13 @@
public class JwtConfig extends
SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

private final JwtAuthenticationProvider jwtAuthenticationProvider;
@Getter
@Value("${spring.security.blacklist-validity-time")
private Long blacklistValidityTime;

public JwtConfig(JwtAuthenticationProvider jwtAuthenticationProvider) {
private final JwtProvider jwtAuthenticationProvider;

public JwtConfig(JwtProvider jwtAuthenticationProvider) {
this.jwtAuthenticationProvider = jwtAuthenticationProvider;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import dev.gooiman.server.config.security.entrypoint.JwtAuthenticationEntryPoint;
import dev.gooiman.server.config.security.handler.JwtAccessDeniedHandler;
import dev.gooiman.server.config.security.provider.JwtAuthenticationProvider;
import dev.gooiman.server.config.security.provider.JwtProvider;
import dev.gooiman.server.config.web.CorsConfig;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -24,7 +24,7 @@
public class SecurityConfig {

private final CorsConfig corsConfig;
private final JwtAuthenticationProvider jwtAuthenticationProvider;
private final JwtProvider jwtAuthenticationProvider;
private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
private final JwtAccessDeniedHandler jwtAccessDeniedHandler;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import static org.springframework.http.HttpHeaders.AUTHORIZATION;
import static org.springframework.util.StringUtils.hasText;

import dev.gooiman.server.config.security.provider.JwtAuthenticationProvider;
import dev.gooiman.server.config.security.provider.JwtProvider;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
Expand All @@ -18,9 +18,9 @@
@Slf4j
public class JwtFilter extends OncePerRequestFilter {

private final JwtAuthenticationProvider jwtAuthenticationProvider;
private final JwtProvider jwtAuthenticationProvider;

public JwtFilter(JwtAuthenticationProvider jwtAuthenticationProvider) {
public JwtFilter(JwtProvider jwtAuthenticationProvider) {
this.jwtAuthenticationProvider = jwtAuthenticationProvider;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
import static dev.gooiman.server.common.exception.ErrorCode.INVALID_TOKEN_ERROR;
import static dev.gooiman.server.common.exception.ErrorCode.TOKEN_UNSUPPORTED_ERROR;

import dev.gooiman.server.common.exception.CommonException;
import dev.gooiman.server.auth.application.BlackListService;
import dev.gooiman.server.auth.application.CustomUserDetailsService;
import dev.gooiman.server.auth.application.domain.CustomUserDetails;
import dev.gooiman.server.common.exception.CommonException;
import dev.gooiman.server.common.exception.ErrorCode;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
Expand All @@ -31,14 +33,15 @@
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtAuthenticationProvider implements AuthenticationProvider {
public class JwtProvider implements AuthenticationProvider {

private static SecretKey key;

@Value("${spring.security.secret}")
private String secret;

private final CustomUserDetailsService customUserDetailsService;
private final BlackListService blackListService;

@PostConstruct
public void init() {
Expand All @@ -58,6 +61,10 @@ public Authentication authenticate(Authentication authentication)
.build()
.parseSignedClaims(token);

if (blackListService.isExists(token)) {
throw new CommonException(ErrorCode.INVALID_TOKEN_ERROR);
}

Claims claims = parsedToken.getPayload();
String userId = claims.getSubject();

Expand Down
1 change: 1 addition & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ spring:
security:
secret: ${JWT_SECRET}
token-validity-time: ${JWT_TOKEN_VALIDITY_TIME}
blacklist-validity-time: ${BLACKLIST_VALIDITY_TIME}

springdoc:
packages-to-scan: dev.gooiman.server
Expand Down

0 comments on commit 8597db2

Please sign in to comment.