인프런/스프링 시큐리티 완전 정복 [6.x 개정판]

12)스프링 MVC 로그인 구현

backend dev 2024. 10. 21.

스프링 MVC 로그인 구현

 

스프링 시큐리티 필터에 의존하는 대신 수동으로 사용자를 인증하는 경우

스프링 MVC 컨트롤러 엔드포인트를 사용할 수 있다

 

• 요청 간에 인증을 저장하고 싶다면 HttpSessionSecurityContextRepository 를 사용하여 인증 상태를 저장 할 수있다

 

 

 

 

 

@RestController
@RequiredArgsConstructor
public class LoginController {

    private final AuthenticationManager authenticationManager;
    private final HttpSessionSecurityContextRepository securityContextRepository = new HttpSessionSecurityContextRepository();


    @PostMapping("/login")
    public Authentication login(@RequestBody LoginRequest login, HttpServletRequest request, HttpServletResponse response) {
//        UsernamePasswordAuthenticationToken token =
//                new UsernamePasswordAuthenticationToken(login.getUsername(),login.getPassword()) -> 이런식으로 해도 되고,아래처럼 해도된다.
        UsernamePasswordAuthenticationToken token =
                UsernamePasswordAuthenticationToken.unauthenticated(login.getUsername(), login.getPassword()); // 인증 객체생성

        Authentication authentication = authenticationManager.authenticate(token); // 인증 성공시 새로운 인증객체[인증된 상태의 객체] 반환, 실패시 예외 발생

        SecurityContext securityContext = SecurityContextHolder.getContextHolderStrategy().createEmptyContext(); // SecurityContext 객체생성

        securityContext.setAuthentication(authentication);// SecurityContext에 인증객체 저장

        SecurityContextHolder.getContextHolderStrategy().setContext(securityContext);
        // ThreadLocal에 SecurityContext 저장, 해당 요청에서는 ThreadLocal에서 인증객체를 꺼내서 처리하는 로직이 없어 이 코드가 필요하지는 않지만
        // 추가적으로 로직상 인증객체를 꺼내서 사용해야한다면 이렇게 저장해서 사용한다.

        securityContextRepository.saveContext(securityContext,request,response);
        // 인증상태를 유지하기위해 securityContextRepository를 이용하여 세션에 SecurityContext를 저장한다.

        return authentication;
    }
}

Spring Security는 기본적으로 인증 정보를 세션에 자동으로 저장하므로 따로 저장하는 코드를 사용할 필요는 없다.

[자동으로 저장하지않도록 설정할 수도 있다.]

@Data
public class LoginRequest {

    private String username;
    private String password;
}

 

@EnableWebSecurity
@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {


        http.authorizeHttpRequests(auth -> auth
                        .requestMatchers("/login").permitAll()
                        .anyRequest().authenticated())
//            .formLogin(Customizer.withDefaults());
                .csrf(csrf -> csrf.disable());
        return http.build();

    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
        return configuration.getAuthenticationManager();
    }



    @Bean
    public UserDetailsManager userDetailsManager() {
        UserDetails user = User.withUsername("user")
                .password("{noop}1234")
                .authorities("ROLE_USER")
                .build();
        UserDetails user2 = User.withUsername("user2")
                .password("{noop}1234")
                .authorities("ROLE_USER")
                .build();
        UserDetails user3 = User.withUsername("user3")
                .password("{noop}1234")
                .authorities("ROLE_USER")
                .build();
        return new InMemoryUserDetailsManager(user, user2, user3);
    }

}

.formLogin()을 주석처리하지않는다면 /login의 request를 필터에서 formLogin이 가로채기때문에 login API가 동작하지않을것이다.

login api의 엔드포인트를 바꾸거나 formLogin을 주석처리하던가 해야한다.

인텔리제이에서 HttpRequest 라는것도 제공해준다.

포스트맨으로 요청해서 테스트해도 되지만 다음과 같이 인텔리제이에서도 요청가능하다.

https://ksh-coding.tistory.com/97

옆에 Examples를 이용하여 요청 샘플을 받아 사용하면된다. 

 

login.http

POST https://localhost:8080/login
Content-Type: application/json

{
  "username": "user",
  "password": "1111"
}

인증되고나면 password는 null로 채워진다. -> 보안을 위해

다음과 같은 결과를 확인할 수 있다. 

 

댓글