Dev/SpringBoot

7. [springboot] Spring Security 간단 권한관리 예제 - UserDetailsService 방식

VIPeveloper 2020. 6. 9. 14:40
728x90
반응형

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을 이용하여 정적으로 박혀있던 정보를 주석 처리하였습니다.

- 인메모리 방식을 보고 싶으시다면 다음 링크로 이동해주세요!!

https://dkyou.tistory.com/15

 

5. [springboot] Spring Security 간단 권한관리 예제

1. 서론 - 스프링 시큐리티 공부하던 도중 간단하게 권한부여하는 방법을 예제로 구현해보고자 포스팅하게 되었습니다. - 이 포스팅을 잘 보게 될 경우 간단한 권한관리를 알 수 있게 됩니다. 2. �

dkyou.tistory.com

- 그 후, 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. 결론

home.html
h2 Database

현재 테스트를 위해 회원가입을 각 권한에 맞게 시켜보았습니다.

 

 

등록된 아이디(username)로 로그인을 하면 권한에 맞게 로그인 처리가 되는 것을 볼 수 있습니다!

 

매니저페이지
어드민페이지, 권한403 에러

4. 마무리

 

- UserDetailsService를 이용하여 DB 정보를 가져와서 로그인 처리하는 과정을 공부해보았습니다.

728x90
반응형