본문 바로가기
  • Where there is a will there is a way.
개발/spring

스프링 시큐리티에 대한 이해

by 소확행개발자 2019. 1. 15.

스프링 시큐리티에 대한 이해


스프링 기반 어플리케이션의 보안( 인증과 권한 )을 담당하는 프레임워크이다.


스프링 시큐리티는 보안과 관련해서 체계적으로 많은 옵션들로 이를 지원해준다.


spring security는 filter 기반으로 동작하기 때문에 spring MVC 와 분리되어 관리 및 동작한다.




스프링 시큐리티에 대한 키워드


접근 주체 ( Principal ) : 보호된 대상에 접근하는 클라이언트


인증 ( Authentication ) : 현재 유저가 누구인지 확인 / 애플리케이션의 작업을 수행할 수 있는 주체임을 증명하는 과정


ex ) HTTP Basic Authentication 폼 로그인


인가 ( Authorize ) : 현재 유저가 어떤 서비스, 페이지에 접근할 수 있는 권한이 있는지 검사


권한 ( Authorization ) : 인증된 주체가 애플리케이션의 동작을 수행할 수 있도록 허락되있는지를 결정




Spring security structure



스프링 시큐리티 시작하기


gradle 설정

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


스프링 시큐리티는 사용자 정보를 UserDetails 구현체로 사용한다. 그래서 스프링 스큐리티는 User라는 클래스를 제공한다

하지만 이름과 패스워드 권한들에 대한 필드만 있기 때문에 이메일 정보나 프로필 이미지 경로 등과 같은 부가적인 정보는 담을 수 없다.

따라서 UserDetails 구현체를 직접 만들어야 한다. 


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


AuthenticationManagerBuilder.userDetailsService().passwordEncoder() 를 통해 패스워드 암호화에 사용될 

PasswordEncoder 구현체를 지정할 수 있다.


@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();
}

UserDetailsService 구현체는 스프링 시큐리티 인증 시에 사용됩낟. UserRepository를 통해 영속성으로 저장된 인증정보를 검색 예제

@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);
	}

}


'개발 > spring' 카테고리의 다른 글

13장 웹 어플리케이션과 영속성 관리  (0) 2019.04.18
spring oauth2.0 jwt 토큰 방식  (5) 2019.01.21
spring OAuth server 구성  (0) 2019.01.10
spring 이란 무엇인가  (0) 2018.12.09
rabbitmq microservice 스프링 부트  (0) 2018.11.28

댓글