Spring-JWT(JSON Web Token) - 3. Spring Security 적용하기

3. 로그인

목차

의존성 추가

Spring Boot에 Security를 적용하기 위해 Spring Security 의존성과 JWT를 사용하기위해 jjwt 의존성을 추가해주도록 한다.

build.gradle

dependencies {
....
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
// https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt
implementation group: 'io.jsonwebtoken', name: 'jjwt', version: '0.9.1'
....
}

Security 설정하기

Spring Security는 인증에 필요한 사용자 정보를 저장할 때 비밀번호는 PasswordEncoder 객체를 이용해 암호화된 Password로 저장돼야 한다. 현재 프로젝트에서는 PasswordEncoder를 구현한 BcryptPasswordEncoder 객체를 이용해 암호화를 할 것이다.

정적자원에 대한 접근은 Spring Filter를 거치지 않도록 설정을 추가한다.

SecurityConfig.java

@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {

private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/static/"
, "classpath:/public/"
, "classpath:/"
, "classpath:/resources/"
, "classpath:/META-INF/resources/"
, "classpath:/META-INF/resources/webjars/" };

@Override
public void configure(WebSecurity web) {
web.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations());
}

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/api/user/signup").permitAll()
.anyRequest().authenticated()
.and()
// 토큰을 활용하면 세션이 필요 없으므로 STATELESS로 설정하여 Session을 사용하지 않는다.
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
// form 기반의 로그인에 대해 비활성화 한다.
.formLogin().disable();
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

회원 가입 Service에 암호화 적용하기

PasswordEncoder 객체를 이용해 암호화된 Password를 갖는 Account 객체를 생성하도록 변경한다.

AccountService.java

@Service
@RequiredArgsConstructor
public class AccountService {

private final AccountRepository accountRepository;
private final PasswordEncoder passwordEncoder;

public Account saveAccount(SignUpRequest signUpRequest) {
Account newAccount = Account.builder()
.username(signUpRequest.getUsername())
.password(passwordEncoder.encode(signUpRequest.getPassword()))
.role(Role.USER)
.build();

return accountRepository.save(newAccount);
}
}

username을 이용한 Account 객체 조회 조건 추가하기

AccountRepository.java

@Repository
public interface AccountRepository extends JpaRepository<Account, String> {
Optional<Account> findByUsername(String username);
}

인증에 필요한 User 객체 생성하기

CustomUserDetailsService는 저장된 Account객체를 가져와 UserDetails객체로 변환해주는 역할을 한다.

CustomUserDetailsService.java

@Service
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {
private final AccountRepository accountRepository;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Account account = accountRepository
.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException(username + "사용자 없음"));

return new User(account.getUsername(),
account.getPassword(),
AuthorityUtils.createAuthorityList(account.getRole().getValue()));
}
}
Share