Spring Security를 사용한 인가

김가빈·2023년 8월 23일
0

springsecurity

목록 보기
11/23

인가

  • 인가란 인증 후, 로그인 한 유저에게 권한을 부여하는 것을 의미한다.
  • 참고로 인증에 실패하게 되면 401에러를, 인가에 실패하게 되면 403에러를 반환한다.
    • 인증에는 성공했으나, 해당 유저의 권한 상 해당 페이지에 접근이 불가능 한 경우 403에러를 반환한다.

Spring Security에서 인가 정보가 저장되는 방법

  • spring security에서 유저들의 role은 GrantedAuthority interface에 저장된다.
    • GrantedAuthority interface는 getter 메소드 하나만 존재한다.(수정불가능)
    • 이를 구현한 대표적인 class로는 SimpleGrantedAuthority가 있고, 생성자가 호출될 때 전달된 role을 객체에 저장한다.


  • 다음과 같이 로그인 시도 시에 db로 부터 유저의 role을 가져와서 해당 객체에 집어넣는다.

인가 정보를 통해 접근 제어하기

  1. hasAuthority
    • 특정 권한을 가지고 있는 경우에만 접근 허가
  2. hasAuthority
    • 에러 권한 중 하나만 만족해도 접근 허용
  3. access
    • 특정 조건을 기반으로 접근을 제한
http.authorizeRequests()
    .antMatchers("/admin").hasAnyAuthority("ROLE_ADMIN", "ROLE_SUPER_ADMIN")
    .antMatchers("/user").hasAuthority("ROLE_USER")
    .anyRequest().authenticated();

http.authorizeRequests()
    .antMatchers("/public").permitAll()
    .antMatchers("/admin").access("hasRole('ROLE_ADMIN')")
    .antMatchers("/user").access("hasRole('ROLE_USER')")
    .anyRequest().authenticated();

실습

테이블 생성

  • 인가 정보를 저장한 테이블을 생성한다.
CREATE TABLE `authorities` (
  `id` int NOT NULL AUTO_INCREMENT,
  `customer_id` int NOT NULL,
  `name` varchar(50) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `customer_id` (`customer_id`),
  CONSTRAINT `authorities_ibfk_1` FOREIGN KEY (`customer_id`) REFERENCES `customer` (`customer_id`)
);

INSERT INTO `authorities` (`customer_id`, `name`)
 VALUES (1, 'VIEWACCOUNT');

INSERT INTO `authorities` (`customer_id`, `name`)
 VALUES (1, 'VIEWCARDS');

 INSERT INTO `authorities` (`customer_id`, `name`)
  VALUES (1, 'VIEWLOANS');

 INSERT INTO `authorities` (`customer_id`, `name`)
   VALUES (1, 'VIEWBALANCE');

DTO생성

package com.eazybytes.model;

import org.hibernate.annotations.GenericGenerator;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;

@Entity
@Table(name = "authorities")
public class Authority {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO , generator = "native")
	@GenericGenerator(name = "native", strategy = "native")
	private Long id;
	
	private String name;
	
	@ManyToOne
	@JoinColumn(name = "customer_id")
	private Customer customer;
	
	
}
  • 참고로 이렇게 작성하면 customer_id라는 식별자 값을 통해서 JPA가 자동으로 Customer테이블에서 customer_id를 사용해 객체를 가지고와서 해당 DTO에 넣어준다.

  • 기존의 Customer테이블에 다음과 같은 코드를 추가해 준다.
    • @JsonIgnore는 JSON직렬화 시 이 필드를 무시하고 결과에 포함되지 않도록 함으로써, 노출되지 않아야 하는 정보를 숨길 수 있다.
    • mappedBy는 맵핑되는 DTO인 Authority에서 관계를 맺는 엔티티 필드의 명을 적는다.(앞선 Authority필드의 customer필드)
    • fetch속성은 데이터를 로딩하는 방식을 의미하고, 이를 통해 Customer 엔티티가 로딩될 때 관련된 Authority 엔티티들도 함께 로딩된다.
    • 참고로 EAGER는 즉시 로딩하는 옵션이다.
  • 참고로 @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)옵션은 유저한테 pwd를 읽어오지만, 다시 유저들에게 반환하지 않도록 해준다.

  • 기존에 생성해놓은 AuthenticationProvider 구현 class에 해당 로직을 추가한다.

  • 이렇게 UsernamePasswordAuthenticationToken에 해당 유저의 인가 정보들이 저장되어 이는, security context에 저장된다.

  • 인가를 실행하기 위해 security config class에 다음과 같이 추가한다

  • 다음과 같이 권한이 있는 페이지에는 접근이 가능하다

  • 다음과 같이 인가 권한을 수정한 경우 페이지 접근이 불가능하다.

role vs authority

  • authority의 경우 위에서 보는 것과 같이 개별 페이지에 대한 접근 권한을 제어한다
  • role의 경우 사용자의 권한을 나누어서, 좀 더 편하게 그룹으로 접근 권한을 제어할 수 있다.
  • 참고로 role을 정의할 때는 ROLE_라는 접두사를 붙이고 DB에 저장하는게 규칙이다.
    • db에서 검색할 때는 spring security가 자동으로 ROLE_을 붙여서 검사하므로, hasRole메소드에는 접두사를 쓰지않는다

role 관련 메소드

  1. hasRole
    • 특정 role을 가지고 있는 경우에만 접근 허가
  2. hasAnyRole
    • 여러 role 중 하나만 만족해도 접근 허용
  3. access
    • 특정 조건을 기반으로 접근을 제한

실습

  • 실습을 위해 기존 권한을 제거하고 새롭게 권한을 추가한다
 DELETE FROM `authorities`;

 INSERT INTO `authorities` (`customer_id`, `name`)
  VALUES (1, 'ROLE_USER');

 INSERT INTO `authorities` (`customer_id`, `name`)
  VALUES (1, 'ROLE_ADMIN');
  • security config 클래스를 다음과 같이 수정한다

  • 다음과 같이 권한이 있는 페이지에서 접근이 가능함을 확인할 수 있다.
  • 귀찮아서 캡쳐를 안했지만, 권한이 없는 페이지에서는 접근이 불가능하다.
profile
신입 웹개발자입니다.

0개의 댓글