Java/spring

spring security를 알아보자 - 10

티코딩 2024. 4. 15. 17:12

ㅇ 인증과 인가의 차이

인증(Authentication)은 사용자의 신원을 확인하는 과정. 사용자가 누구인지를 식별하고 검증하는 것. 대표적인 예시로 로그인이 있다.

인가(Authorization)은 인증된 사용자가 수행할 수 있는 행동을 결정하는 과정.

인증이 실패하면 401, 인가가 실패하면 403. 인증이 됐지만 이 유저는 접근할 수 없다는 뜻.

애플리케이션 내에는 다양한 역할이 존재하고 역할마다 접근할 수 있는 서비스는 다를 것이다. 그래서 우리는 인가 설정을 잘 해야한다.

 

ㅇ 어디에 인가정보가 저장될까?

Spring Security에서는 이 권한 또는 특권을 권한(Authorities), 역할(role)로 구분해서 부른다.

저장되는 방식은 비슷한다. 먼저 권한을 보자.

GrantedAuthority라는 인터페이스가 있다.

이 인터페이스를 구현한 SimpleGrantedAuthority라는 클래스를 제공한다.

여기선 역할을 파라미터로 입력받는다. 이 클래스로 객체를 만들면 setter가 없기 때문에 이 객체는 수정이 불가능하다. 

 

ㅇ 권한을 활용하는법

DB에서 한 유저에 대해 하나의 권한이나 역할만 저장할 수 있다. 하지만 Spring Security는 여러개의 역할을 부여할 수 있게 해준다. 

이렇게 하기 위해 우리는 Authorities라는 테이블을 새로 생성하고 그안에 유저가 가질 수 있는 권한의 종류를 정의할 수 있음. 외래키를 사용해 다른테이블에서 우리가 만든 권한들을 로드 할 수 있다. DB의 스크립트를 수정해서 Authorities 테이블을 만들고 Spring Data JPA의 코드도 바꿔야한다. Authorities테이블과 Customer 테이블과 연관관계를 맺는다. Authorities에선 @ManyToOne, Customer에선 

@OneToMany 로 연관관계를 맺었다.

 

아래는 Customer 엔티티에 추가한코드.

@JsonIgnore
@OneToMany(mappedBy="customer",fetch=FetchType.EAGER)
private Set<Authority> authorities;

 

@JsonIgnore은  뭐 응답값으로 Json을 보내는데 거기에 포함시키지 않겠다는 뜻이다. 왜냐하면 민감한 정보기 때문.

 

그다음 AuthenticationProvider의 구현체에 아래 코드도 추가해줬다.

private List<GrantedAuthority> getGrantedAuthorities(Set<Authority> authorities) {
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        for (Authority authority : authorities) {
            grantedAuthorities.add(new SimpleGrantedAuthority(authority.getName()));
        }
        return grantedAuthorities;
    }

그리고 기존 authenticate 메서드의 리턴값중 권한을 getGrantedAuthorities(customer.get(0).getAuthorities())로 바꿔줬다.

 

ㅇ 웹 애플리케이션 내의 권한 설정

Spring Security내에 인가를 실행할 수 있는 메서드가 뭐가 있는지 알아보자.

 

ㅁ hasAuthority() : 현재 인증된 사용자가 특정 권한을 가지고 있는지 확인하는데 사용된다. 이 메서드는 단일 권한 문자열을 인자로 받으며, 해당 권한이 현재 사용자의 권한 목록에 있는 경우에만 true를 반환함. 이를 통해 특정 권한을 가진 사용자만이 특정 경로나 기능에 접근할 수 있도록 할 수 있음.

.antMatchers("/admin/**").access("hasAuthority('ROLE_ADMIN')")

ㅁ hasAnyAuthority() : 현재 인증된 사용자가 주어진 권한 목록 중 하나라도 가지고 있는지를 확인한다. 여러 권한을 쉼표로 구분하여 인자로 전달할 수 있으며, 해당 권한 중 하나라도 사용자의 권한 목록에 있으면 true를 반환함. 이는 사용자가 여러 역할 중 하나를 가질 경우 유용하게 사용된다.

.antMatchers("/user/**").access("hasAnyAuthority('ROLE_USER', 'ROLE_ADMIN')")

ㅁ access() : 가장 유연한 접근 제어를 제공하는메서드. 이 메서드는 보안 표현식을 문자열로 받아 처리하며, 내부적으로 SpEL (Spring Expression Language)을 사용하여 복잡한 접근 제어 로직을 구현할 수 있음.

.antMatchers("/api/**").access("hasRole('ROLE_USER') and hasIpAddress('192.168.1.0/24')")

 

 

ㅇ 웹 애플리케이션 내의 역할 권한 설정

ㅁ hasRole() : 현재 인증된 사용자가 특정 역할을 가지고 있는지 확인함. 이 메서드는 하나의 역할 이름을 인자로 받으며, 해당 역할이 인증된 사용자의 권한 목록에 포함되어 있을 때 true를 반환함. Spring Security는 내부적으로 역할 이름 앞에 "ROLE_" 접두사를 추가합니다. 따라서, 설정 시 "ROLE_" 접두사를 생략하고 역할 이름만 지정해야 한다.

.antMatchers("/admin/**").hasRole("ADMIN")

ㅁ hasAnyRole() : 인증된 사용자가 주어진 역할 목록 중 하나 이상을 가지고 있는지 확인함. 여러 역할을 인자로 전달할 수 있으며, 전달된 역할 중 하나라도 사용자의 권한 목록에 포함되어 있으면 true를 반환한다.

.antMatchers("/user/**").hasAnyRole("USER", "ADMIN")

ㅁ access() : 가장 유연한 접근 제어 기능을 제공함. 복잡한 보안 요구 사항을 SpEL(Spring Expression Language)을 사용하여 표현할 수 있음. 이 메서드는 문자열로 된 보안 표현식을 받아, 그 표현식을 평가한 결과가 true일 때 접근을 허용한다.

.antMatchers("/api/**").access("hasRole('USER') and hasIpAddress('192.168.1.0/24')")