스프링 시큐리티 (Spring Security) - 인증, 인가 절차

kwak woojong·2022년 7월 26일
0

코드스테이츠

목록 보기
30/36
post-thumbnail

인증 처리

인증 처리 흐름

  1. 사용차 요청

  2. AuthenticationFilter (UsernamePasswordAuthenticationFilter)
    에서 전달 받은 Username과 password를 가지고 Authentication을 생성함.

  3. 생성된 인증 Authentication을 인증매니저 (AuthenticationManger)에 전달한다.

  4. 인증매니저는 받아온 인증을 Provider에 전달한다. 이 때 이 Provider는 유효성 검증을 하기 위해 UserDetailsService에 전달한다.

  5. UserDetailsService는 실제 저장 계층(DB)를 확인하여 유저가 있는지, 비번이 맞는지 확인하고, 검증이 성공적으로 이루어지게 되면, UserDetails를 생성한다.

  6. 생성된 UserDetails를 역순으로 다시 전달한다.

  7. 최종 Filter까지 도달하면 인증 정보를 담고 있는 Authentication을 시큐리티컨텍스트 SecurityContext에 저장한다.

이러면 로그인 인증이 완료됨.

흐름 자체는 MVC쪽 보다 복잡하거나 그러지 않으니 이해 자체는 편할 것.


Authentication

인증 정보를 가지고 있는 인터페이스

구성 요소

principal

사용자를 식별하는 정보. UserDetails 인터페이스의 구현체다.

credentials

사용자의 암호. 인증이 이루어 진 후에 지워진다.

Authorities

AuthenticationManager에 의해 부여된 인가 정보다.
부여된 권한은 GrantedAuthority로 추상화 한다. 일반적인 구현체는
SimpleGrantedAuthority다.

UsernamePasswordAuthenticationToken

일반적으로 Authentication의 구현체는 이걸 이용한다.
아직 안뜯어봤는데 Username과 Password만 가지고 돌아다니는 친구일 것으로 예상됨.


인가 처리

인가 처리 흐름

  1. 인증 완료된 (로그인 한) 친구가 Http 요청을 보낸다.

  2. FilterSecurityInterCeptor은 인증 완료된 친구들이 저장된 SecurityContextHolder에서 인증정보를 가져온다 (Authentication을)

  3. 가져오고 FilterInvocation을 생성함.

  1. 생성된 FilterInvocation은 SecurityMetadataSource에서 ConfigAttributes를 획득하고 온다.
    이 친구는 체크할 대상을 가지고 있다. 이걸 AccessDecisionManager 로 전달해서 체크를 시작한다.

  2. AccessDecisionManager는 AccessDecisionVoter에 처리를 위임한다. 이 때 이 친구는 여러개일 수 있다. 일종의 위원회라 생각하자.

  3. 권한이 존재하지 않을 경우엔 AccessDenieException을 뱉는다.

  4. 존재하는 경우 ACCESS_GRANTED를 매니저에 전달하고, 역순으로 인터셉터까지 간 뒤에 filter를 끝내고 요청을 허가해준다.

Configuration으로 인가 설정

        http.authorizeRequests()
                .antMatchers("/user/**").authenticated()
                .antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')")
                .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
                .anyRequest().permitAll()
                .and()
                .formLogin()
                .loginPage("/login");

user는 인증된 모든 사용자에게 접근허용,
manager는 ROLE_ADMIN 혹은 ROLE_MANAGER에게 허용
admin은 ROLE_ADMIN에게만 허용 된다.

member 객체에 role을 설정해두고 이런 식으로 인가를 활용할 수 있다.
hasRole, authentication 같은 친구들을 표현식 기반 접근 제어라고 한다.


표현식 기반 접근 제어

스프링 시큐리티 3.0부터 구현할 수 있다.

표현식 설명
hasRole(Stirng role) - 현재 보안 주체(principal)가 지정된 역할을 갖고 있는지 여부를 확인하고 가지고 있다면 true를 리턴한다.
- hasRole(’admin’)처럼 파라미터로 넘긴 role이 ROLE_ 로 시작하지 않으면 기본적으로 추가한다. (DefaultWebSecurityExpressionHandler의 defaultRolePrefix를 수정하면 커스텀할 수 있다.)
hasAnyRole(String… roles) - 현재 보안 주체가 지정한 역할 중 1개라도 가지고 있으면 true를 리턴한다. (문자열 리스트를 콤마로 구분해서 전달한다.)
- ex) hasAnyRole(’admin’, ‘user’)
hasAuthority(String authority) - 현재 보안 주체가 지정한 권한을 갖고 있는지 여부를 확인하고 가지고 있다면 true를 리턴한다.
- ex) hasAuthority(’read’)
hasAnyAuthority(String… authorities) - 현재 보안 주체가 지정한 권한 중 하나라도 있으면 true를 리턴한다.
- ex) hasAnyAuthority(’read’, ‘write’)
principal - 현재 사용자를 나타내는 principal 객체에 직접 접근할 수 있다.
authentication - SecurityContext로 조회할 수 있는 현재 Authentication 객체에 직접 접근할 수 있다.0
permitAll - 항상 true로 평가한다.
denyAll - 항상 false로 평가한다.
isAnonymous() - 현재 보안 주체가 익명 사용자면 true를 리턴한다.
isRememberMe() - 현재 보안 주체가 remember-me 사용자면 true를 리턴한다.
isAuthenticated() - 사용자가 익명이 아닌 경우 true를 리턴한다.
isFullyAuthenticated() - 사용자가 익명 사용자나 remember-me 사용자가 아니면 true를 리턴한다.
hasPermission(Object target, Object permission) - 사용자가 target에 해당 permission 권한이 있으면 true를 리턴한다.
-ex) hasPermission(domainObject, ‘read’)
hasPermission(Object targetId, String targetType, Object permission) - 사용자가 target에 해당 permission 권한이 있으면 true를 리턴한다.
- ex) hasPermission(1, ‘com.example.domain.Message’, ‘read’)

왜케 길어


메서드 보안 표현식

Configuration이 아닌 어노테이션을 이용한 방법이 있다.

그냥 권한 설정이 필요한 곳에다가 추가해 주면 권한별로 통제하게 된다.

처음 시큐리티는 @Secured나 @PreAuthorize 실행이 false로 되어 있으므로 Configuration에서 이것을 바꿔주긴 해야한다.

@Configuration
@EnableGlobalMethodSecurity(
  prePostEnabled = true, 
  securedEnabled = true, 
  jsr250Enabled = true)
public class MethodSecurityConfig 
  extends GlobalMethodSecurityConfiguration {
}

이런 식으로 어노테이션으로 허영해주면 됨.

@Secured : 특정 권한을 가진 사람만 들어올 수 있게 한다. (메서드 권한)

@Secured("ROLE_ADMIN")

이런 식으로 메서드에 붙여주면 작동한다. 다만 복잡한 조건이 있을 경우 표현하기 좀 어렵다.

@PreAuthorize : 메서드 실행 전 (요청전)에 권한을 확인 한다. 메서드를 호출할 권한이 있는지 확인하다고 생각하면 편함. 좀 더 복잡한 조건을 달 수 있다.

@PreAuthorize("isAuthenticated() and (( #user.name == principal.name ) or hasRole('ROLE_ADMIN'))")

@PostAuthorize : 얘는 메서드 호출 이후

profile
https://crazyleader.notion.site/Crazykwak-36c7ffc9d32e4e83b325da26ed8d1728?pvs=4<-- 포트폴리오

0개의 댓글