스프링 시큐리티에 대한 이해
스프링 시큐리티에 대한 이해
스프링 기반 어플리케이션의 보안( 인증과 권한 )을 담당하는 프레임워크이다.
스프링 시큐리티는 보안과 관련해서 체계적으로 많은 옵션들로 이를 지원해준다.
spring security는 filter 기반으로 동작하기 때문에 spring MVC 와 분리되어 관리 및 동작한다.
스프링 시큐리티에 대한 키워드
접근 주체 ( Principal ) : 보호된 대상에 접근하는 클라이언트
인증 ( Authentication ) : 현재 유저가 누구인지 확인 / 애플리케이션의 작업을 수행할 수 있는 주체임을 증명하는 과정
ex ) HTTP Basic Authentication 폼 로그인
인가 ( Authorize ) : 현재 유저가 어떤 서비스, 페이지에 접근할 수 있는 권한이 있는지 검사
권한 ( Authorization ) : 인증된 주체가 애플리케이션의 동작을 수행할 수 있도록 허락되있는지를 결정
Spring security structure
스프링 시큐리티 시작하기
dependencies {
compile 'org.springframework.security:spring-security-web:4.2.2.RELEASE'
compile 'org.springframework.security:spring-security-config:4.2.2.RELEASE'
}
java configuration
스프링 시큐리티 xml 기반 설정에서는 springSecurityFilterChain 을 등록하는 것으로 시작하지만
자바기반 설정에서는 WebSecurityConfigurerAdapter를 상속받은 클래스에
@EnableWebSecurity 어노테이션을 명시하는 것만으로도 springSecurityFilterChain가 자동으로 포함되어 진다.
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
}
HttpSecurity
configure 메소드를 통해서 커스텀 인증 매커니즘을 구성할 수 있다.
public class SecurityConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
}
.authorizeRequests() : 요청에 대한 권한을 지정할 수 있다.
.anyRequest().authenticated() : 인증이 되어야 한다는 이야기이다.
.anonymous() : 인증되지 않은 사용자도 접근할 수 있다.
.fullyAuthenticated() : 완전히 인증된 사용자만 접근할 수 있다.
.hasRole() or hasAnyRole() : 특정 권한을 가지는 사용자만 접근할 수 있다.
.hasAuthority() or hasAnyAuthority() : 특정 권한을 가지는 사용자만 접근할 수 있다.
.hasIpAddress() : 특정 아이피 주소를 가지는 사용자만 접근할 수 있다.
.access() : SpEL? 표현식에 의한 결과에 따라 접근할 수 있다.
.not() : 접근 제한 기능을 해제
.permitAll() or denyAll() : 접근을 전부 허용하거나 제한한다.
.rememberMe() : 로그인한 사용자만 접근할 수 있다. 리멤버기능
Method Security
스프링 스큐리티는 다음과 같이 인터페이스에 시큐리티를 걸어 줄수도 있다.
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig {
// ...
}
public interface BankService {
@PreAuthorize("isAnonymous()")
public Account readAccount(Long id);
@PreAuthorize("isAnonymous()")
public Account[] findAccounts();
@PreAuthorize("hasAuthority('ROLE_TELLER')")
public Account post(Account account, double amount);
}
Remember-Me
UserDetails & UserDetailsService
public class UserDetails extends org.springframework.security.core.userdetails.User {
private static final long serialVersionUID = -4855890427225819382L;
private Long id;
private String nickname;
private String email;
private Date createdAt;
public UserDetails(String username, String password, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
// TODO Auto-generated constructor stub
}
public UserDetails(User user){
super(user.getEmail(), user.getPassword(), user.isAccountNonExpired(), user.isAccountNonLocked(), user.isCredentialsNonExpired(), user.isEnabled(), authorities(user));
this.id = user.getId();
this.nickname = user.getNickname();
this.email = user.getEmail();
this.createdAt = user.getCreatedAt();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
private static Collection<? extends GrantedAuthority> authorities(User user) {
// TODO Auto-generated method stub
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
user.getAuthorities().forEach(a -> {
authorities.add(new SimpleGrantedAuthority(a.getAuthority()));
});
return authorities;
}
public UserDetails(String username, String password, Collection<? extends GrantedAuthority> authorities,
String nickname) {
super(username, password, authorities);
this.nickname = nickname;
this.email = username;
}
public UserDetails(String username, String password, boolean enabled, boolean accountNonExpired,
boolean credentialsNonExpired, boolean accountNonLocked,
Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
// TODO Auto-generated constructor stub
}
}
Password Encoding
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// TODO Auto-generated method stub
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Service
public class UserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
// TODO Auto-generated method stub
User user = userRepository.findByEmail(email);
if(user == null){
throw new UsernameNotFoundException(email);
}
com.kdev.app.security.userdetails.UserDetails userDetails = new com.kdev.app.security.userdetails.UserDetails(user);
return userDetails;
}
}
AuthenticationSuccessHandler & AuthenticationFailureHandler
클라이언트가 인증과정을 성공했을때와 실패했을 경우 handler 를 implementaion 해서 custom 할 수 있다.
public class AuthenticationSuccessHandlerImpl extends SavedRequestAwareAuthenticationSuccessHandler {
private static final Logger logger = LoggerFactory.getLogger(AuthenticationSuccessHandlerImpl.class);
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
logger.info("Login Success... - {}", authentication.getPrincipal());
response.sendRedirect("/?login");
}
}
public class AuthenticationFailureHandlerImpl extends SimpleUrlAuthenticationFailureHandler {
private static final Logger logger = LoggerFactory.getLogger(AuthenticationFailureHandlerImpl.class);
public AuthenticationFailureHandlerImpl() {
this.setDefaultFailureUrl("/login?error");
}
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
logger.info("Login Failed... - {}",request.getParameter("email"));
super.onAuthenticationFailure(request, response, exception);
}
}