강의/Java Spring Boot

Spring Security를 이용한 로그인 구현

studylida 2025. 1. 26. 03:19

이전까지 우리는 애플리케이션 제작 방법과 과정을 이해하기 위해 직접 로그인 논리를 작성했다.

 

그때 중간에 실제로는 이렇게 하고 프레임워크에서 제공하는 기능을 이용하여 구현한다고 말한 적이 있을 것이다.

 

이제 그렇게 것이다. Spring Security라는 이용할 건데, 이걸 위해서 우리가 그동안 코드를 어느정도 정리해야 한다. 일단 LoginController 가보자.

 

```

@Controller

@SessionAttributes("name")

public class LoginController {

 

AuthenticationService authenticationService;

 

public LoginController(AuthenticationService authenticationService) {

super();

this.authenticationService = authenticationService;

 

}

 

@RequestMapping(value="login", method=RequestMethod.GET)

public String gotoLoginPage() {

return "login";

}

 

@RequestMapping(value="login", method=RequestMethod.POST)

public String gotoWelcomePage(@RequestParam String name, @RequestParam String password, ModelMap model) {

 

if(authenticationService.authenticate(name, password)) {

model.put("name", name);

return "welcome";

 

}

 

model.put("errorMessage", "Invalid Credential. try again.");

return "login";

}

 

}

```

아까 말했지만, 우리가 직접 로그인 논리를 작성할 필요가 없다. 해서 관련된 코드를 지우고, gotoLoginPage() 메서드만 남긴다. 그리고 메서드가 "localhost:8080" 대응되도록 바꾸고, login 페이지가 아니라 welcome 페이지를 반환하도록 하자. 그리고 겸사겸사 welcome 페이지에서 필요로 하는 변수를 넘겨줄 있도록 매개변수로 model 넘겨주고, 이름도 바꿔주자.

 

```

@Controller

@SessionAttributes("name")

public class LoginController {

 

@RequestMapping(value="/", method=RequestMethod.GET)

public String gotoWelcomePage(ModelMap model) {

model.put("name", "in28minutes");

return "welcome";

}

 

}

```

 

이제 login.jsp 쓸모가 없다. 지우자.

 

이제 Spring Security 대한 작성할 건데, 전에 pom.xml 가서 의존성을 추가하자. 추가해야 코드는 아래와 같다.

 

```

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-security</artifactId>

</dependency>

```

 

상태로, 하지 말고 프로그램을 실행해보자. 그리고 우리가 만든 페이지 아무 것에나 접속하려 해보자.

 

그러면 놀랍게도 로그인 페이지가 뜬다! 회원가입을 적도 없는데 로그인을 어떻게 하나 싶겠지만, 로그를 보면 개발전용 비밀번호를 확인할 있다. default username user.

 

여러 실행할 때마다 비밀번호가 바뀌는 확인할 있는데, 이건 곤란하다. 그러니까 이걸 설정하려고 하는데, 이번에는 application.properties 바로 가지 않는다. SpringSecurityConfiguration이라는 이름의 새로운 루트 패키지를 만들어 것이다.

 

경로는 `com.in28minutes.springboot.myfirstwebapp.security`.

 

여기서 우리는 직접 여러 Bean 만들고 반환하도록 해볼 것이다.

 

보통 아이디와 비밀번호는 저장되어야 하고, 이를 위해서 LDAP 데이터베이스를 사용한다.

 

우리는 간단하게 인메모리 설정자를 사용할 것이다. 코드를 먼저 보자.

 

```

@Configuration

public class SpringSecurityConfiguration{

 

@Bean

public InMemoryUserDetailsManager createUserDetailsManager() {

 

UserDetails userDetails = User.withDefaultPasswordEncoder()

.username("in28minutes")

.password("dummy")

.roles("USER, ADMIN")

.build();

 

return new InMemoryUserDetailsManager(userDetails);

}

}

```

 

코드를 따라서 작성해보면 알겠지만, `User.withDefaultPasswordEncoder()` 취소선이 그어져있는 확인할 있다. 아마 deprecated 녀석인데, 이유는 해싱을 하지 않고 평문으로 저장하기 때문이다. 실제로 이걸 쓰면 유출될 사용자정보가 그대로 유출되므로 위험해서 쓰지 않지만 지금은 예제니까 일단 쓰고 나중에 BCrypt 사용할 것이다.

 

위의 코드처럼 그냥 username, password, roles 정해주고, 마지막에 build() 적어주면 정보 입력 완성이다.

 

이렇게 하고 로그인, 로그아웃을 하면 작동하는 확인할 있다.

 

이제 비밀번호 인코더를 바꿔보자. BCryptPasswordEncoder 인스턴스를 반환하는 Bean 만든 , 그걸 passwordEncoder 매개변수로 넘겨줄 것이다.

 

```

@Configuration

public class SpringSecurityConfiguration{

 

@Bean

public InMemoryUserDetailsManager createUserDetailsManager() {

 

Function<String, String> passwordEncoder = input -> passwordEncoder().encode(input);

 

UserDetails userDetails = User.build()

.passwordEncoder(passwordEncoder())

.username("in28minutes")

.password("dummy")

.roles("USER, ADMIN")

.build();

 

return new InMemoryUserDetailsManager(userDetails);

}

}

```

 

1. **Function<String, String>**: `Function` Java의 함수형 인터페이스로, 입력값을 받아서 출력값을 반환하는 기능을 제공합니다. 여기서는 입력값과 출력값 모두 `String` 타입입니다.

 

2. **passwordEncoder**: 이 변수는 비밀번호를 인코딩하는 함수를 참조합니다. , 이 변수에 할당된 람다 표현식이 비밀번호 인코딩 로직을 수행합니다.

 

3. **input -> ...**: 이 부분은 람다 표현식입니다. `input`은 이 함수가 입력받는 매개변수입니다.

 

4. **passwordEncoder().encode(input)**: 이 부분은 `passwordEncoder()` 메서드를 호출하여 비밀번호를 인코딩합니다. 여기서 `passwordEncoder()`는 비밀번호 인코딩을 위한 객체를 반환하는 메서드로, 일반적으로 BCryptPasswordEncoder와 같은 인코딩 클래스를 사용합니다.

 

 

이렇게 해서 인코딩도 안전한 걸로 바꿨으니 Spring Security 관련된 여기서 마무리한다.