Spring Security는 Java 애플리케이션에 인증과 권한 부여를 모두 제공하는 데 중점을 둔 프레임워크이다. 기본적으로 인증 절차 후 인가 절차를 진행하며, Principal(접근 주체)와 Credential(자격 증명)을 사용하여 절차가 진행된다.
Security와 관련된 Configuration은 WebSecurityConfigurerAdapter
에서 설정할 수 있다.
스프링의 인증은 AuthenticationManager
라는 API가 관리한다. 중간과정이 있긴 한데 축소시켜서 이야기하자면 인증 과정에 대해 AuthenticationProvider
를 AuthenticationManager
에 주입해 특정 종류의 인증을 진행하게 할 수 있다. 예컨대 DaoAuthenticationProvider
의 경우 사용자의 이름과 비밀번호를 기반한 인증을, JwtAuthenticationProvider
는 JWT 토큰에 기반한 인증을 진행한다.
AuthenticationManager
는 WebSecurityConfigurerAdapter
에서 configure
함수에서 AuthenticationManagerBuilder
를 통해 설정할 수 있다.
//인증 관리자
@Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider(userService));
}
DaoAuthenticationProvider
는 UserDetailsService
와 PasswordEncoder
를 이용해 인증을 진행한다. DaoAuthenticationProvider를 사용하기 위해서는 위의 두 필드를 설정해줘야한다. UserDetailsService
의 경우 UserDetails loadUserByUserName(String username)
만이 정의된 interface이다. 이를 implement한 서비스를 필드로 넘겨준다. 이 때 반드시 정의되어야 하는 loadUserByUserName
함수의 경우 UserDetails
타입 객체를 반환한다.
UserDetails 객체 interface에는 몇 가지 필드에 대한 Getter 함수가 정의되어있다. 즉, 해당 필드와 Getter 함수가 반드시 필요하다. 따라서 UserDetails를 implement하는 객체(우리가 사용할 User 객체)는 다음과 같은 형태로 작성할 수 있다.
UserDetails
@Data
public interface UserDetails {
private Collection<? extends GrantedAuthority> authorities;
private String password;
private String username;
private boolean accountNonExpired;
private boolean accountNonLocked;
private boolean credentialsNonExpired;
private boolean enabled;
}
User implements UserDetails
@Data
public class User implements UserDetails {
private List<UserGrantedAuthority> authorities;
// authorities는 List가 아니라 Map, Set 등 다른 Collection이 될 수도 있다.
// UserGrantedAuthority는 GrantedAuthority를 implement하는 사용자 정의 객체이다.
private String password;
private String username;
private boolean accountNonExpired;
private boolean accountNonLocked;
private boolean credentialsNonExpired;
private boolean enabled;
// 그 외의 사용자 정의 함수
}
다른 건 다 그렇다 치지면 여기 또 다른 객체 GrantedAuthority
가 있다. 의외로 간단히 authority의 Getter 함수만 정의된 interface이다. 따라서 해당 객체는 다음과 같은 형태로 작성할 수 있다.
@Data
public class UserGrantedAuthority implements GrantedAuthority {
String authority; // 권한
}
여기서 받게되는 authority가 나중에 접근 보안 설정 관리자에서 hasAuthority
에 해당하게 된다.
http.authorizeRequests().antMatchers("/home").hasAuthority("ADMIN")
// ADMIN 권한의 유저만 "/home" 에 접근가능
💡 Authority는 Role은 비슷하지만 조금 다르다. Role은 Admin, Manager와 같이 역할을 말하는 것이고, Authority는 그 역할이 가지는 권한, 예컨대 READ_ARTICLE, WRITE_ARTICLE 등을 말한다. Spring Security에서는 authority는 보통 Role로 혼용되어 사용된다.
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/home").hasAuthority("READ");
// List<UserGrantedAuthority> authorities에 "READ"가 포함된 사용자만 접근
http
.authorizeRequests()
.antMatchers("/home").hasRole("ADMIN");
// hasRole함수는 자동으로 앞에 "ROLE_"을 접두사로 붙여준다.
// List<UserGrantedAuthority> authorities에 "ROLE_ADMIN"이 포함된 사용자만 접근
}
관련 코드는 아래 깃헙을 참고해주세요.
Spring Security Github