1. 서론
- 오늘은 userDetailsService를 이용하여 DB에서 정보를 가져와 인증하는 방식을 구현해보고자 합니다.
- 이 글을 이해하면 로그인 기능을 구현할 수 있습니다.
- 질문은 댓글로 언제든 환영합니다!
2. 본론
SecurityConfig.java
- 시작은 SecurityConfig 클래스에서부터 시작합니다.
package com.example.springsecurity.security;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity // 이거 안해줘서 그동안 계속 안됬었음
@Slf4j
@AllArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
// String password = passwordEncoder().encode("1111");
//
// auth.inMemoryAuthentication().withUser("user").password(password).roles("USER");
// auth.inMemoryAuthentication().withUser("manager").password(password).roles("MANAGER");
// auth.inMemoryAuthentication().withUser("admin").password(password).roles("ADMIN");
auth.userDetailsService(userDetailsService);
}
@Override
// js, css, image 설정은 보안 설정의 영향 밖에 있도록 만들어주는 설정.
public void configure(WebSecurity web) throws Exception {
web.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations());
}
@Bean
// BCryptPasswordEncoder는 Spring Security에서 제공하는 비밀번호 암호화 객체입니다.
// Service에서 비밀번호를 암호화할 수 있도록 Bean으로 등록합니다.
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/","/loginUser").permitAll()
.antMatchers("/user").hasRole("USER")
.antMatchers("/manager").hasRole("MANAGER")
.antMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin();
}
}
- 기존 inMemoryAuthentication을 이용하여 정적으로 박혀있던 정보를 주석 처리하였습니다.
- 인메모리 방식을 보고 싶으시다면 다음 링크로 이동해주세요!!
- 그 후, userDetailsService 객체를 이용하여 구현합니다.
- 중요한 것은 이 객체가 가지고 있는 메서드입니다. 스프링 부트 시큐리티에서 구현된 것(제가 구현한 것이 아닙니다)인데, loadUserByUsername이라는 메서드를 가지고 있으므로, 이를 extends 해서 구현하는 작업이 필요합니다.
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.security.core.userdetails;
public interface UserDetailsService {
UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}
CustomUserDetailsService.java
- 여기에서 UserDetailsService를 implement 해주어 구현합니다.
package com.example.springsecurity.security;
import com.example.springsecurity.domain.Account;
import com.example.springsecurity.repository.AccountRepository;
import lombok.AllArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
// DB에 연동되게끔 하는 곳
@AllArgsConstructor
@Service("userDetailsService") // 빈 등록하기
public class CustomUserDetailsService implements UserDetailsService {
private final AccountRepository accountRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Account account = accountRepository.findByUsername(username);
if(account == null){
throw new UsernameNotFoundException("UsernameNotFoundException");
}
List<GrantedAuthority> roles = new ArrayList<>();
roles.add(new SimpleGrantedAuthority(account.getRole()));
AccountContext accountContext = new AccountContext(account,roles);
return accountContext;
}
}
- 유저 이름으로 계정을 찾은 후, 권한을 SimpleGrantedAuthority로 부여해줍니다.
- 중요한 점은 AccountContext라는 곳인데 스프링 기본 User를 가져와서 구현해줍니다.
AccountContext.java
package com.example.springsecurity.security;
import com.example.springsecurity.domain.Account;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import java.util.Collection;
public class AccountContext extends User {
private final Account account;
public AccountContext(Account account, Collection<? extends GrantedAuthority> authorities) {
super(account.getUsername(), account.getPassword(), authorities);
this.account = account;
}
public Account getAccount() {
return account;
}
}
3. 결론
현재 테스트를 위해 회원가입을 각 권한에 맞게 시켜보았습니다.
등록된 아이디(username)로 로그인을 하면 권한에 맞게 로그인 처리가 되는 것을 볼 수 있습니다!
4. 마무리
- UserDetailsService를 이용하여 DB 정보를 가져와서 로그인 처리하는 과정을 공부해보았습니다.
'Dev > SpringBoot' 카테고리의 다른 글
9. [springboot] 간단 커스텀 로그인 예제 (2) | 2020.06.11 |
---|---|
8. [springboot] Spring Security 간단 권한관리 예제 - AuthenticationProvider 방식 (1) | 2020.06.09 |
6. [springboot] Spring boot 기초 회원가입 예제 (5) | 2020.06.09 |
5. [springboot] Spring Security 간단 권한관리 예제 (4) | 2020.06.08 |
4. [springboot] hibernate.hbm2ddl.auto 속성 정리 (0) | 2020.03.26 |