hasRole("ADMIN")hasAuthority("ADMIN")은 같은 동작을 할까요? ROLE_ADMINADMIN은 어떻게 다를까요?

Spring Security에서 Role과 Authority는 모두 GrantedAuthority 인터페이스로 표현되지만, ROLE_ 접두사 규칙 때문에 사용 방식이 다릅니다. 이 차이를 모르면 권한 체크가 의도대로 동작하지 않는 상황을 겪게 됩니다.

개념 정의

Authority(권한) 는 사용자가 수행할 수 있는 개별 동작을 나타내는 문자열입니다. Spring Security에서 모든 권한은 GrantedAuthority 인터페이스로 표현됩니다.

Role(역할) 은 Authority의 특수한 형태로, ROLE_ 접두사가 붙은 Authority입니다. Spring Security는 Role을 다룰 때 내부적으로 ROLE_ 접두사를 자동으로 관리합니다.

PLAINTEXT
Authority:  READ_PRIVILEGE, WRITE_PRIVILEGE, DELETE_PRIVILEGE
Role:       ROLE_USER, ROLE_ADMIN, ROLE_MANAGER

hasRole()과 hasAuthority()의 차이

핵심 차이는 ROLE_ 접두사 자동 추가 여부 입니다.

메서드전달값내부 비교값
hasRole("ADMIN")"ADMIN""ROLE_ADMIN" (자동 추가)
hasAuthority("ADMIN")"ADMIN""ADMIN" (그대로)
hasAuthority("ROLE_ADMIN")"ROLE_ADMIN""ROLE_ADMIN" (그대로)

hasRole("ADMIN")은 내부적으로 ROLE_ADMIN을 찾습니다. DB에 ADMIN으로 저장했다면 hasRole("ADMIN")은 실패합니다.

URL 기반 인가 설정

JAVA
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests(auth -> auth
        .requestMatchers("/admin/**").hasRole("ADMIN")       // ROLE_ADMIN 확인
        .requestMatchers("/api/posts/**").hasAuthority("WRITE_POST")  // WRITE_POST 확인
        .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
        .anyRequest().authenticated()
    );
    return http.build();
}

메서드 기반 인가 설정

@EnableMethodSecurity를 활성화하면 메서드 레벨에서도 권한을 체크할 수 있습니다.

JAVA
@Configuration
@EnableMethodSecurity
public class MethodSecurityConfig { }
JAVA
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long userId) { ... }

@PreAuthorize("hasAuthority('WRITE_POST') and #authorId == authentication.name")
public void editPost(Long postId, String authorId) { ... }

@PreAuthorize는 SpEL(Spring Expression Language)을 지원하므로, 역할 체크와 파라미터 기반 조건을 조합할 수 있습니다.

Role과 Authority를 함께 사용하는 구조

실무에서는 Role(역할)로 큰 범위를 나누고, Authority(세부 권한)로 세밀한 접근 제어를 합니다.

PLAINTEXT
ROLE_ADMIN  → READ_POST, WRITE_POST, DELETE_POST, MANAGE_USER
ROLE_USER   → READ_POST, WRITE_POST
ROLE_GUEST  → READ_POST

UserDetails에서 이 구조를 반영하려면 getAuthorities()에서 Role과 Authority를 모두 반환합니다.

JAVA
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    List<GrantedAuthority> authorities = new ArrayList<>();
    // Role 추가
    authorities.add(new SimpleGrantedAuthority("ROLE_" + role.name()));
    // 세부 권한 추가
    role.getPermissions().forEach(permission ->
        authorities.add(new SimpleGrantedAuthority(permission))
    );
    return authorities;
}

주의할 점

hasRole()은 자동으로 ROLE_ 접두사를 붙인다

hasRole("ADMIN")은 내부적으로 ROLE_ADMIN을 찾습니다. DB에 ADMIN으로만 저장했다면 hasAuthority("ADMIN")을 사용하거나, 저장할 때 ROLE_ADMIN으로 넣어야 합니다. 팀 내에서 "Role 저장 시 ROLE_ 접두사를 포함하는지 안 하는지"를 명확하게 규약으로 정해야 합니다.

@RolesAllowed와 @PreAuthorize의 차이

@RolesAllowed는 JSR-250 표준이고 SpEL을 지원하지 않습니다. @PreAuthorize는 SpEL로 복잡한 조건을 표현할 수 있습니다. 한 프로젝트에서 둘을 혼용하면 가독성이 떨어지므로, 팀 내에서 하나만 선택해 일관성을 유지해야 합니다.

roles()와 authorities()를 User.builder()에서 혼용하면 안 된다

JAVA
// roles()는 자동으로 ROLE_ 접두사를 붙인다
User.builder().roles("USER")           // → ROLE_USER
// authorities()는 전달값 그대로 사용
User.builder().authorities("ROLE_USER") // → ROLE_USER
// 둘을 같이 호출하면 나중 호출이 이전 값을 덮어쓴다

User.builder()에서 roles()authorities()를 동시에 호출하면, 마지막 호출만 반영됩니다. 하나만 선택해서 사용해야 합니다.


정리

항목설명
AuthorityGrantedAuthority로 표현되는 개별 권한 문자열
RoleROLE_ 접두사가 붙은 특수한 Authority
hasRole("ADMIN")내부적으로 ROLE_ADMIN을 찾음 (접두사 자동 추가)
hasAuthority("ADMIN")ADMIN을 그대로 찾음 (접두사 추가 안 함)
실무 패턴Role로 큰 범위 구분, Authority로 세밀한 접근 제어
메서드 인가@PreAuthorize로 SpEL 기반 조건 체크 가능
댓글 로딩 중...