인프런 스프링부트 시큐리티 & JWT 강의를 수강하며 작성하는 내용입니다.
소스코드
SecurityConfig
loginProcessingUrl
: login 주소가 호출이 되면 시큐리티가 낚아채서 대신 로그인 진행해주기 때문에 login api가 필요없어졌다.defaultSuccessUrl
: login이 완료되면 이동하는 Url을 적어준다.@Configuration
@EnableWebSecurity // 스프링 시큐리티 필터가 스프링 필터체인에 등록됨
class SecurityConfig {
@Bean
fun encodePwd(): BCryptPasswordEncoder {
return BCryptPasswordEncoder()
}
@Bean
@Throws(Exception::class)
fun filterChain(http: HttpSecurity): SecurityFilterChain? {
http
.authorizeRequests { authz ->
authz
.antMatchers("/user/**").authenticated()
.antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')")
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/loginForm")
// .usernameParameter("userNickname") -> username 으로 파라미터 안받으려면 설정 필요
.loginProcessingUrl("/login")
.defaultSuccessUrl("/")
}
.httpBasic(withDefaults())
.cors().and()
.csrf().disable()
return http.build()
}
}
loginForm
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>로그인 페이지</title>
</head>
<body>
<h1>로그인 페이지</h1>
<hr/>
<form action="/login" method="POST">
<input type="text" name="username" placeholder="Username"> <br/>
<input type="password" name="password" placeholder="Password"> <br/>
<button>로그인</button>
</form>
<a href="/joinForm">회원가입을 아직 하지 않으셨나요?</a>
</body>
</html>
PrincipalDetails
class PrincipalDetails(
private val user: User // 콤포지션
) : UserDetails {
// 해당 User 의 권한을 리턴하는 곳
override fun getAuthorities(): MutableCollection<out GrantedAuthority> {
val authorities: MutableList<GrantedAuthority> = ArrayList()
authorities.add(SimpleGrantedAuthority(user.role))
return authorities
}
override fun getPassword(): String {
return user.password.toString()
}
override fun getUsername(): String {
return user.username.toString()
}
override fun isAccountNonExpired(): Boolean {
return true
}
override fun isAccountNonLocked(): Boolean {
return true
}
override fun isCredentialsNonExpired(): Boolean {
return true
}
override fun isEnabled(): Boolean {
return true
}
}
PrincipalDetailsService
loginProcessingUrl("/login")
을 해놓았기 때문에, /login 요청이 오면 자동으로 UserDetailsService 타입으로 IoC되어 있는 loadUserByUsername
이 실행된다.@Service
class PrincipalDetailsService(
private val userRepository: UserRepository
) : UserDetailsService {
@Throws(UsernameNotFoundException::class)
override fun loadUserByUsername(username: String): UserDetails? {
println("username: $username")
val userEntity = userRepository.findByUsername(username)
if (userEntity != null) {
return PrincipalDetails(userEntity)
}
return null
}
}
실행결과
.antMatchers("/user/**").authenticated()
설정에 의해