Skip to content

Commit

Permalink
Refactor: 폼로그인 방식 -> 커스텀로그인 방식으로 변경 ssg-java3-240304#324
Browse files Browse the repository at this point in the history
  • Loading branch information
yeahjinjeong committed Jan 28, 2025
1 parent cda590b commit db63a16
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 85 deletions.
17 changes: 17 additions & 0 deletions src/main/java/com/readyauction/app/auth/config/WebConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.readyauction.app.auth.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("http://localhost:8080") // 허용할 출처 : 특정 도메인만 받을 수 있음
.allowedMethods("GET", "POST") // 허용할 HTTP method
.allowCredentials(true)
.exposedHeaders("Authorization");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
import com.readyauction.app.auth.config.jwt.JwtAuthenticationSuccessHandler;
import com.readyauction.app.auth.config.jwt.JwtProvider;
import com.readyauction.app.auth.filter.JwtAuthorizationFilter;
import com.readyauction.app.auth.service.AuthService;
import com.readyauction.app.user.entity.Authority;
import io.jsonwebtoken.Jwt;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
Expand All @@ -17,21 +18,21 @@
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;


@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@Slf4j
public class WebSecurityConfig {

private final JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler;
private final JwtProvider jwtProvider;

// public WebSecurityConfig(JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler) {
// this.jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler;
// }

// 정적파일에 대해서는 인증/인가 검사를 수행하지 않는다.
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().requestMatchers("/assets/**", "/css/**", "/js/**", "/images/**");
Expand All @@ -41,22 +42,29 @@ public WebSecurityCustomizer webSecurityCustomizer() {
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

http.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests((registry) -> {registry.requestMatchers("/auction-api/create", "/auction", "/", "/index", "/inquiry/faq").permitAll() // 누구나 허용
.requestMatchers( "/member/register").anonymous() // 인증하지 않은 사용자만 허용
.requestMatchers("/mypage/**", "/auction/**", "/cash/**", "/chat/**", "inquiry/register").authenticated() // 인증된 사용자만 허용
.requestMatchers("/admin/**").hasAnyAuthority(String.valueOf(Authority.ROLE_ADMIN)) // ROLE_ADMIN 권한이 있는 사용자만 허용
.anyRequest().authenticated();
})
.formLogin(configurer -> {
configurer.loginPage("/auth/login") // GET 로그인폼 요청 url (핸들러 작성 필요)
.loginProcessingUrl("/auth/login") // POST 로그인처리 url
.usernameParameter("email") // name="username"인 아닌 경우 작성필요
.passwordParameter("password") // name="password"가 아닌 경우 작성필요
.successHandler(jwtAuthenticationSuccessHandler)
// .defaultSuccessUrl("/auction", true) // 로그인 성공시 이동할 URL
.failureUrl("/auth/login") // 로그인 실패 시 리다이렉트할 URL을 설정
.permitAll(); // 누구나 허용
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.authorizeHttpRequests((registry) -> {registry.requestMatchers("/auction-api/create", "/auction", "/", "/auth/login", "/inquiry/faq").permitAll() // 누구나 허용
.requestMatchers( "/member/register").anonymous() // 인증하지 않은 사용자만 허용
.requestMatchers("/mypage/**", "/auction/**", "/cash/**", "/chat/**", "inquiry/register").authenticated() // 인증된 사용자만 허용
.requestMatchers("/admin/**").hasAnyAuthority(String.valueOf(Authority.ROLE_ADMIN)) // ROLE_ADMIN 권한이 있는 사용자만 허용
.anyRequest().authenticated();
})
// .authorizeHttpRequests((registry) -> {registry.requestMatchers("/auction-api/create", "/auction", "/", "/auth/login", "/inquiry/faq").permitAll() // 누구나 허용
// .requestMatchers( "/member/register").anonymous() // 인증하지 않은 사용자만 허용
// .requestMatchers("/mypage/**", "/auction/**", "/cash/**", "/chat/**", "inquiry/register").authenticated() // 인증된 사용자만 허용
// .requestMatchers("/admin/**").hasAnyAuthority(String.valueOf(Authority.ROLE_ADMIN)) // ROLE_ADMIN 권한이 있는 사용자만 허용
// .anyRequest().authenticated();
// })
.formLogin(AbstractHttpConfigurer::disable)
// .formLogin(configurer -> {
// configurer.loginPage("/auth/login") // GET 로그인폼 요청 url (핸들러 작성 필요)
// .loginProcessingUrl("/auth/login") // POST 로그인처리 url
// .usernameParameter("email") // name="username"인 아닌 경우 작성필요
// .passwordParameter("password") // name="password"가 아닌 경우 작성필요
// .successHandler(jwtAuthenticationSuccessHandler)
// .failureUrl("/auth/login") // 로그인 실패 시 리다이렉트할 URL을 설정
// .permitAll(); // 누구나 허용
// })
.logout(configurer -> {
configurer.logoutUrl("/auth/logout")
.logoutSuccessUrl("/auction"); // 로그아웃 후 리다이렉트 url (기본값은 로그인페이지)
Expand All @@ -65,6 +73,28 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti

return http.build();
}

@Bean
public CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOriginPattern("http://localhost:8080");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
config.setExposedHeaders(Arrays.asList("Authorization", "Authorization-refresh"));
source.registerCorsConfiguration("/**", config); // 모든 URL에 대해 CORS 설정 등록
return source; // CorsConfigurationSource 반환
}


@Bean
public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
// AuthenticationManagerBuilder를 사용해 AuthenticationManager를 구성
AuthenticationManagerBuilder authenticationManagerBuilder =
http.getSharedObject(AuthenticationManagerBuilder.class);
return authenticationManagerBuilder.build(); // AuthenticationManager를 빌드하여 반환
}

@Bean
public PasswordEncoder passwordEncoder() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,14 @@ public class JwtAuthenticationSuccessHandler extends SimpleUrlAuthenticationSucc
private final JwtProvider jwtProvider;
private final AuthService authService;

// public JwtAuthenticationSuccessHandler(JwtProvider jwtProvider, AuthService authService) {
// this.jwtProvider = jwtProvider;
// this.authService = authService;
// }

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {

AuthPrincipal principal = (AuthPrincipal) authentication.getPrincipal();
String accessToken = jwtProvider.createAccessToken(principal.getUser());

// 액세스 토큰을 Authorization 헤더에 추가
response.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);
response.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);

// 리프레시 토큰을 쿠키에 추가
ResponseCookie responseCookie = ResponseCookie.from(
Expand All @@ -60,9 +56,4 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo
super.onAuthenticationSuccess(request, response, authentication);
response.setStatus(HttpServletResponse.SC_OK);
}

public static String serialize(Object object) {
return Base64.getUrlEncoder()
.encodeToString(SerializationUtils.serialize(object));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import org.hibernate.type.SpecialOneToOneType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,34 @@
package com.readyauction.app.auth.controller;

import com.readyauction.app.auth.dto.req.CreateAccessTokenRequest;
import com.readyauction.app.auth.dto.res.CreateAccessTokenResponse;
import com.readyauction.app.auth.config.jwt.JwtProvider;
import com.readyauction.app.auth.dto.request.CreateAccessTokenRequest;
import com.readyauction.app.auth.dto.response.CreateAccessTokenResponse;
import com.readyauction.app.auth.principal.AuthPrincipal;
import com.readyauction.app.auth.service.AuthService;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseCookie;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.ui.Model;

@Controller
@RequestMapping("/auth")
@RequiredArgsConstructor
@Slf4j
public class AuthController {

private final JwtProvider jwtProvider;
private final AuthService authService;
private final AuthenticationManager authenticationManager;

@PostMapping("/token")
public ResponseEntity<CreateAccessTokenResponse> createNewAccessToken(
Expand All @@ -27,15 +39,69 @@ public ResponseEntity<CreateAccessTokenResponse> createNewAccessToken(
}

@GetMapping("/login")
public void login(@RequestParam(value = "success", required = false) Boolean success,
@RequestParam(value = "error", required = false) Boolean error,
Model model) {
if (success != null && success) {
model.addAttribute("loginSuccess", true);
}
public void login(
// @RequestParam(value = "success", required = false) Boolean success,
// @RequestParam(value = "error", required = false) Boolean error,
// Model model
) {
// if (success != null && success) {
// model.addAttribute("loginSuccess", true);
// }
//
// if (error != null && error) {
// model.addAttribute("loginFailure", true);
// }
}

if (error != null && error) {
model.addAttribute("loginFailure", true);
@PostMapping("/login")
public ResponseEntity<?> login(@RequestParam("email") String email,
@RequestParam("password") String password,
HttpSession session
) {
Authentication authentication = authenticateUser(email, password);

if (authentication == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("로그인 실패");
}

AuthPrincipal principal = (AuthPrincipal) authentication.getPrincipal();
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(authentication);

session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context);

String accessToken = jwtProvider.createAccessToken(principal.getUser());
ResponseCookie responseCookie = createResponseCookie(principal);

HttpHeaders headers = setHeaders(accessToken, responseCookie);

return ResponseEntity.ok()
.headers(headers)
.body("로그인 성공");
}

private Authentication authenticateUser(String email, String password) {
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(email, password);
return authenticationManager.authenticate(authenticationToken); // authenticationManager를 통해 인증 처리
}

private ResponseCookie createResponseCookie(AuthPrincipal principal) {
return ResponseCookie.from(
"refreshToken",
authService.findRefreshTokenByUserId(principal.getUser().getId())
)
.httpOnly(true)
.secure(true)
.path("/")
.maxAge(60 * 60 * 24 * 7) // 7일간 유효
.build();
}

private HttpHeaders setHeaders(String accessToken, ResponseCookie responseCookie) {
HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);
headers.set("Set-Cookie", responseCookie.toString());
return headers;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.readyauction.app.auth.dto.req;
package com.readyauction.app.auth.dto.request;

import lombok.Data;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.readyauction.app.auth.dto.res;
package com.readyauction.app.auth.dto.response;

import lombok.AllArgsConstructor;
import lombok.Data;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,9 @@
public class JwtAuthorizationFilter extends OncePerRequestFilter {

private final JwtProvider jwtProvider;
// private final AuthService authService;
private final static String HEADER_AUTHORIZATION = "Authorization";
private final static String TOKEN_PREFIX = "Bearer ";

// public JwtAuthorizationFilter(JwtProvider jwtProvider, AuthService authService) {
// this.jwtProvider = jwtProvider;
// this.authService = authService;
// }

@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
Expand Down
Loading

0 comments on commit db63a16

Please sign in to comment.