Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: CI / CD

on:
push:
branches: [main]
branches: [feat/#85-logout]

jobs:
CI:
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/com/opendata/docs/LogoutControllerDocs.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.opendata.docs;



import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.ResponseEntity;

@Tag(name = "로그아웃 API")
public interface LogoutControllerDocs {
@Operation(summary = "로그아웃")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "로그아웃 성공"),
@ApiResponse(responseCode = "500", description = "서버 에러")
})
ResponseEntity<String> doLogout();
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.opendata.domain.user.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import com.opendata.docs.LogoutControllerDocs;

import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
public class LogoutController implements LogoutControllerDocs {

@PostMapping("/logout")
public ResponseEntity<String> doLogout(){
return ResponseEntity.ok().build();
}
}
7 changes: 5 additions & 2 deletions src/main/java/com/opendata/global/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.opendata.domain.oauth2.service.CustomOAuth2UserService;
import com.opendata.domain.user.repository.UserRepository;
import com.opendata.global.jwt.CookieUtil;
import com.opendata.global.jwt.CustomLogoutFilter;
import com.opendata.global.jwt.JwtFilter;
import com.opendata.global.jwt.JwtUtil;
import com.opendata.global.jwt.LoginFilter;
Expand All @@ -29,6 +30,7 @@
import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
Expand Down Expand Up @@ -111,8 +113,9 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.successHandler(customSuccessHandler)
);

http
.addFilterBefore(new JwtFilter(jwtUtil, userDetailsService), UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(new JwtFilter(jwtUtil, userDetailsService), UsernamePasswordAuthenticationFilter.class);

http.addFilterBefore(new CustomLogoutFilter(jwtUtil), LogoutFilter.class);
// .addFilterBefore(oAuth2RedirectUriCookieFilter, OAuth2AuthorizationRequestRedirectFilter.class);
// .addFilterAfter(new JwtFilter(jwtUtil,userDetailsService), OAuth2LoginAuthenticationFilter.class);
// .addFilterAt(new LoginFilter(authenticationManager(authenticationConfiguration), jwtUtil, cookieUtil, userRepository), UsernamePasswordAuthenticationFilter.class);
Expand Down
70 changes: 70 additions & 0 deletions src/main/java/com/opendata/global/jwt/CustomLogoutFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.opendata.global.jwt;

import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.filter.GenericFilterBean;

import java.io.IOException;

@RequiredArgsConstructor
public class CustomLogoutFilter extends GenericFilterBean {

private final JwtUtil jwtUtil;

@Override
public void doFilter(
jakarta.servlet.ServletRequest servletRequest,
jakarta.servlet.ServletResponse servletResponse,
FilterChain filterChain
) throws IOException, ServletException {
doFilter((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse, filterChain);
}

private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {

String requestUri = request.getRequestURI();
if (!requestUri.equals("/logout") && !requestUri.equals("/api/logout")) {
chain.doFilter(request, response);
return;
}

String requestMethod = request.getMethod();
if (!requestMethod.equals("POST")) {
chain.doFilter(request, response);
return;
}

String refresh = CookieUtil.getRefreshTokenFromRequest(request).orElse(null);

if (refresh == null) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return;
}

try {
jwtUtil.isExpired(refresh);
} catch (ExpiredJwtException e) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return;
}

String category = jwtUtil.getCategory(refresh);
if (!"refresh".equals(category)) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return;
}

String domain = ".yourse-seoul.com";
response.addHeader("Set-Cookie",
"refresh=; Max-Age=0; Path=/; Domain=" + domain + "; HttpOnly; Secure; SameSite=Strict");
response.addHeader("Set-Cookie",
"access=; Max-Age=0; Path=/; Domain=" + domain + "; HttpOnly; Secure; SameSite=Strict");

response.setStatus(HttpServletResponse.SC_OK);
}
}
4 changes: 4 additions & 0 deletions src/main/java/com/opendata/global/jwt/JwtUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ public String getRole(String token) {
return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload()
.get("role", String.class);
}
public String getCategory(String token) {
return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload()
.get("category", String.class);
}



Expand Down