티스토리 뷰

Spring Security Flow Example

Spring Security에 대한 의존성을 추가하고 WebSecurityConfigurerAdapter 추상 클래스를 extends해서 작성하면 Request에 대해서 Spring Security의 Filter Chain을 거쳐서 인증, 권한에 대한 Default작업을 수행하게 된다. 이때 Spring Security에서 인증과정에서 사용하는 Interface 들을 구현함으로써 Customizing 해서 사용하도록 의도 되어 진 것으로 보인다.

1. UserDetailsService Interface를 구현하는 UserDetailsServiceImpl 클래스 작성

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    AccountRepository accountRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Account account = accountRepository.findByUsername(username);
        if (account == null) {
            throw new UsernameNotFoundException(username);
        }
        return new UserDetailsImpl(account);
    }
}
  • UserDetailsService 의 loadUserByUsername method를 구현하는데 이는 Database에 접근해서 사용자 정보를 가져오는 역할을 한다.
  • 이 때, Spring Security에서 제공하는 사용자 Interface로 UserDetails 가 있는데 이를 구현해 놓은 구현체로 User Class가 있다. UserDetails Interface 를 직접 구현하거나 User Class를 확장해서 사용하면 된다.

2. User Class를 확장한 UserDetailsImpl Class

public class UserDetailsImpl extends User {

    public UserDetailsImpl(Account account) {
        super(account.getUsername(), account.getPassword(), authorities(account));
    }

    private static Collection<? extends GrantedAuthority> authorities(Account account) {
        List<GrantedAuthority> authorities = new ArrayList<>();
        if (account.isAdmin()) {
            authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
        } else {
            authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
        }
        return authorities;
    }
}
  • 생성자에서 super Class인 User의 생성자에 username, password, 권한정보(Collection)를 넣는다.
  • GrantedAuthority 란?

User Class의 생성자

public User(String username, String password,
            Collection<? extends GrantedAuthority> authorities) {
    this(username, password, true, true, true, true, authorities);
}

3. Config 에서 UserDetailsService 의존성을 주입받아서 사용한다.

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    PasswordEncoder passwordEncoder;

    @Autowired
    UserDetailsService userDetailsService;

    // TODO Security 설정 사용자의 유저네임과 패스워드가 맞는지 검증해준다.
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
    }
}
  • AuthenticationManagerBuilder 란?
  • auth.userDetailsService(userDetailsService) 부분에서 하는 일은?
    • DaoAuthenticationConfigurer 는 무엇인가?

userDetailsService method in AuthenticationManagerBuilder

public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T> userDetailsService(
            T userDetailsService) throws Exception {
        this.defaultUserDetailsService = userDetailsService;
        return apply(new DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T>(
                userDetailsService));
    }

Concept of words

1. UserDetailsService

44.3.6 What is a UserDetailsService and do I need one?

UserDetailsService is a DAO interface for loading data that is specific to a user account. It has no other function other to load that data for use by other components within the framework. It is not responsible for authenticating the user. Authenticating a user with a username/password combination is most commonly performed by the DaoAuthenticationProvider, which is injected with a UserDetailsService to allow it to load the password (and other data) for a user in order to compare it with the submitted value. Note that if you are using LDAP, this approach may not work.

If you want to customize the authentication process then you should implement AuthenticationProvider yourself. See this blog article for an example integrating Spring Security authentication with Google App Engine.

9.2.2 The UserDetailsService

Another item to note from the above code fragment is that you can obtain a principal from the Authentication object. The principal is just an Object. Most of the time this can be cast into a UserDetails object. UserDetails is a core interface in Spring Security. It represents a principal, but in an extensible and application-specific way. Think of UserDetails as the adapter between your own user database and what Spring Security needs inside the SecurityContextHolder. Being a representation of something from your own user database, quite often you will cast the UserDetails to the original object that your application provided, so you can call business-specific methods (like getEmail(), getEmployeeNumber() and so on).

By now you’re probably wondering, so when do I provide a UserDetails object? How do I do that? I thought you said this thing was declarative and I didn’t need to write any Java code - what gives? The short answer is that there is a special interface called UserDetailsService. The only method on this interface accepts a String-based username argument and returns a UserDetails:

UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
This is the most common approach to loading information for a user within Spring Security and you will see it used throughout the framework whenever information on a user is required.

On successful authentication, UserDetails is used to build the Authentication object that is stored in the SecurityContextHolder (more on this below). The good news is that we provide a number of UserDetailsService implementations, including one that uses an in-memory map (InMemoryDaoImpl) and another that uses JDBC (JdbcDaoImpl). Most users tend to write their own, though, with their implementations often simply sitting on top of an existing Data Access Object (DAO) that represents their employees, customers, or other users of the application. Remember the advantage that whatever your UserDetailsService returns can always be obtained from the SecurityContextHolder using the above code fragment.

[Note]
There is often some confusion about UserDetailsService. It is purely a DAO for user data and performs no other function other than to supply that data to other components within the framework. In particular, it does not authenticate the user, which is done by the AuthenticationManager. In many cases it makes more sense to implement AuthenticationProvider directly if you require a custom authentication process.

  • You can define custom authentication by exposing a custom UserDetailsService as a bean.
댓글
댓글쓰기 폼