본문 바로가기
Dev/Spring Boot

[스프링 부트 개념과 활용] 스프링 시큐리티 2부

by dev_jsk 2020. 8. 31.
728x90
반응형

시큐리티 설정 커스터마이징

기본 프로젝트 구조와 의존성은 스프링 시큐리티 1부 참고

 

페이지 별 로그인 설정 예제

 

1. HelloControllerindex.html, hello.html, my.html 추가

// HelloController.java

@Controller
public class HelloController {
    @GetMapping(value="/hello")
    public String hello() {
        return "hello";
    }

    @GetMapping(value="/my")
    public String my() {
        return "my";
    }
}
// index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset='utf-8'>
    <title>Page Title</title>
</head>
<body>
    <h1>Hello Spring Boot Security</h1>
    <a href="/hello">Hello</a>
    <a href="/my">My</a>
</body>
</html>

// hello.html

...
<body>
    <h1>Hello</h1>
</body>
</html>

// my.html

...
<body>
    <h1>My</h1>
</body>
</html>

2. SecurityConfig 추가

// SecurityConfig.java

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    // 스프링 부트가 제공하는 SecurityAutoConfigutaion은 미사용
    // WebSecurityConfigurerAdapter 타입의 Bean을 등록했기 때문에

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/", "/hello").permitAll() // antMatcher로 root, hello 요청은 접근 가능 처리
            .anyRequest().authenticated()   // 그 외 요청은 인증정보 필요
            .and()
            .formLogin()    // 인증이 안되어있으니까 걸림, accept header 에 html이 존재해서
            .and()
            .httpBasic()    // html이 없는 경우
        ;
    }
}

결과 확인

1. index.html 접근 시

2. hello.html 접근 시

3. my.html 접근 시

SecurityConfig 에서 root(/), /hello 만 접근 허가를 하였기 때문에 /my 접근 시 스프링 시큐리티에서 제공하는 로그인 페이지 표시

 

User 정보 커스터마이징

공통

Account, AccountService, AccountRepositoryJPA, H2 의존성 추가

// pom.xml

<!-- JPA -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!-- H2 -->
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
</dependency>
// Account.java

@Entity
public class Account {
    @Id @GeneratedValue
    private Long id;
    private String username;
    private String password;

    getter/setter
}

// AccountService.java

@Service
public class AccountService {}

// AccountRepository.java

public interface AccountRepository extends JpaRepository<Account, Long> {}

UserDetailsServce 구현

1. AccountService 수정

// AccountService.java

@Service
public class AccountService implements UserDetailsService {
    // 보통 Service 계층에 UserDetailsService 구현
    // 별개로 다른 클래스를 만들어서 UserDetailsService 인터페이스를 구현해도 상관 없다.
    // UserDetailsService 타입의 Bean이 등록이 되어있어야 스프링 부트가 만들어주는 User가 더 이상 생성되지 않는다.
    // 커스텀 User 사용 가능하다.

    @Autowired
    private AccountRepository accountRepository;

    public Account createAccount(String username, String password) {
        Account account = new Account();
        account.setUsername(username);
        account.setPassword(password);
        return accountRepository.save(account);
    }

    // 로그인 처리 시 호출
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Optional<Account> byUsername = accountRepository.findByUsername(username);
        Account account = byUsername.orElseThrow(() -> new UsernameNotFoundException(username));

        return new User(account.getUsername(), account.getPassword(), authorites());
    }

    // Authority
    private Collection<? extends GrantedAuthority> authorites() {
        return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
    }
}

2. AccountRepository 수정

// AccountRepository.java

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

3. User 객체 생성을 위해 AccountRunner 생성

// AccountRunner.java

@Component
public class AccountRunner implements ApplicationRunner {

    @Autowired
    AccountService accountService;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        Account jinseo = accountService.createAccount("jinseo", "1234");
        System.out.println(jinseo.getUsername() + " / " + jinseo.getPassword());
    }
}

결과 확인

jinseo / 1234 로 로그인 했을 경우 PasswordEncoder가 정의되지 않아 오류 발생

 

PasswordEncoder 설정 및 사용

1. SecurityConfig 수정

// SecurityConfig.java

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/", "/hello").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .and()
            .httpBasic()
        ;
    }

    // PasswordEncoder 를 사용하지 않는 방법
    // 절대 사용 X
    // @Bean
    // public PasswordEncoder passwordEncoder() {
    //     return NoOpPasswordEncoder.getInstance();
    // }

    // 권장하는 PasswordEncoder 사용 방법
    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
}

2. AccountService 수정

// AccountService.java

@Service
public class AccountService implements UserDetailsService {

    @Autowired
    private AccountRepository accountRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    public Account createAccount(String username, String password) {
        Account account = new Account();
        account.setUsername(username);
        account.setPassword(passwordEncoder.encode(password));	// Password Encoding
        return accountRepository.save(account);
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Optional<Account> byUsername = accountRepository.findByUsername(username);
        Account account = byUsername.orElseThrow(() -> new UsernameNotFoundException(username));

        return new User(account.getUsername(), account.getPassword(), authorites());
    }

    private Collection<? extends GrantedAuthority> authorites() {
        return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
    }
}

결과 확인

로그인 시 my.html 접근 가능

참고

https://docs.spring.io/spring-security/site/docs/current/reference/html5/

 

Spring Security Reference

In Spring Security 3.0, the codebase was sub-divided into separate jars which more clearly separate different functionality areas and third-party dependencies. If you use Maven to build your project, these are the modules you should add to your pom.xml. Ev

docs.spring.io

 

728x90
반응형

댓글