Programming/Spring

Spring - Spring Security (Method Security 적용 및 인증 적용 확인)

잇(IT) 2023. 10. 13. 20:46
- Method Security

- Method Secuity는 애플리케이션의 메소드 레벨에서 보안 규칙을 정의하고 적용할 수 있게 해주는 기능이다.

 

 

- SecurityConfig.java

@Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
        return http
                .authorizeHttpRequests()
//                    .requestMatchers(HttpMethod.POST,"/auth/login").permitAll()
//                    .requestMatchers(HttpMethod.POST,"/auth/signup").permitAll()
                //위의 방법을 사용하면 무한 페이지 리다이렉트 될 수 있다...?
//                    .requestMatchers("/auth/login").permitAll()
//                    .requestMatchers("/auth/signup").permitAll()
                .anyRequest().permitAll()
                .and()
                .addFilterBefore(emailPasswordAuthFilter(), UsernamePasswordAuthenticationFilter.class)
                .exceptionHandling(e -> {
                    e.accessDeniedHandler(new Http403Handler(objectMapper));
                    e.authenticationEntryPoint(new Http401Handler(objectMapper));
                            //로그인이 필요한 페이지인데 로그인이 안된 상태에서 접근 했을 때
                })
                .rememberMe(new Customizer<RememberMeConfigurer<HttpSecurity>>() {
                                @Override
                                public void customize(RememberMeConfigurer<HttpSecurity> rm) {
                                    rm.rememberMeParameter("remember")
                                            .alwaysRemember(false)
                                            .tokenValiditySeconds(2592000);
                                }
                            }
                        )
                .csrf(AbstractHttpConfigurer::disable)
                .build();
    }

1. SecurityFilterChain을 보게 되면, 프로젝트가 커지고 컨트롤러가 많아지면 filter의 코드가 길어지면 복잡해질 수 있기 때문에 Controller에 직접 Security를 적용 시킬 수 있다.

@Slf4j
@Configuration
@EnableWebSecurity(debug = true)
@RequiredArgsConstructor
@EnableMethodSecurity
//이것만 달아주면 메서드 시큐리티가 가능하다.
// debug 달면 log가 더 잘뜬다. 운영환경에선 사용하면 안된다.
public class SecurityConfig {

    private final ObjectMapper objectMapper;
    private final UserRepository userRepository;

2. 클래스 상단에 @EnableMethodSecurity를 달아주게 되면, Controller에서 Spring EL을 통해 해당 경로에 대한 인증을 수행할 수 있다.

 

- PostController.java

@Slf4j
@RestController
@RequiredArgsConstructor
public class PostController {

    private final PostService postService;

    @PreAuthorize("hasRole('ROLE_ADMIN')")
    @PostMapping("/posts")
    public void post(@RequestBody @Valid PostCreate request) {
        request.validate();
        postService.write(request);
    }

    @GetMapping("/posts/{postId}")
    public PostResponse get(@PathVariable Long postId) {
        return postService.get(postId);
    }

    @GetMapping("/posts")
    public List<PostResponse> getList(@ModelAttribute PostSearch postSearch) {
        return postService.getList(postSearch);
    }

    @PreAuthorize("hasRole('ROLE_ADMIN')")
    @PatchMapping("/posts/{postId}")
    public void edit(@PathVariable Long postId, @RequestBody @Valid PostEdit request) {
        postService.edit(postId, request);
    }

    @PreAuthorize("hasRole('ROLE_ADMIN')")
    @DeleteMapping("/posts/{postId}")
    public void delete(@PathVariable Long postId) {
        postService.delete(postId);
    }
}

3. PreAuthorize, PostAuthorize를 통해 메소드 호출 전 후에 사용자에 대한 권한을 검사할 수 있다. 

@PreAuthorize("hasRole('ROLE_ADMIN')")

4. 현재 위 Controller에서는 PreAuthorize를 사용하였고, 해당 경로에 접근하기 이전에 사용자에 대한 권한을 검사하게 된다. hasRole을 통해 해당 경로에 접근 가능한 역할을 지정한다.

 

- UserPrincipal.java

public UserPrincipal(com.hodolog.api.domain.User user) {
        super(user.getEmail(), user.getPassword(),
                List.of(new SimpleGrantedAuthority("ROLE_ADMIN")
                ));
        this.userId = user.getId();
    }

5. 현재 UserPrincipal 클래스에 의해 현재 로그인되는 사용자에 대해서 ADMIN 역할을 부여하게 된다.


- post.http

### 회원가입

POST http://localhost:8080/auth/signup
Content-Type: application/json

{
  "name": "백인수",
  "email": "saymay10@naver.com",
  "password": "1234"

}

### 로그인

POST http://localhost:8080/auth/login
#Content-Type: application/x-www-form-urlencoded
# 폼으로 해당 경로로 POST 요청을 Spring에서 알아서 전달한다.
Content-Type: application/json

{
  "email": "saymay10@naver.com",
  "password": "1234"
}

### 메인 페이지

GET http://localhost:8080

### 사용자 페이지

GET http://localhost:8080/user

### 관리자 페이지

GET http://localhost:8080/admin

### 게시글 작성

POST http://localhost:8080/posts
Content-Type: application/json

{
  "title": "제목입니다.",
  "content": "내용입니다."
}

6. SecurityFilterChain에 요청에 대한 역할 부여 코드가 사라진 대신 Controller에 설정하여도 똑같이 요청 경로에 대한 사용자 인증이 똑같이 이루어지는 것을 알 수 있다.

 

- PostController.java

@PreAuthorize("hasRole('ROLE_USER')")
    @PostMapping("/posts")
    public void post(@RequestBody @Valid PostCreate request) {
        request.validate();
        postService.write(request);
    }

- 만약 hasRole을 USER로 변환한 다음 (UserPrincipal에 의해 ROLE은 ADMIN으로 설정된다.) /posts 경로로 POST 요청을 하게 되면

현재 사용자의 ROLE은 ADMIN인데 /posts 경로의 허용 역할은 USER이기 때문에 위와 같이 예외가 발생하는 것을 확인 할 수 있다.

@PreAuthorize("hasRole('ROLE_ADMIN')")
    @PostMapping("/posts")
    public void post(@RequestBody @Valid PostCreate request) {
        request.validate();
        postService.write(request);
    }

- /posts 경로의 허용 역할을 ADMIN으로 바꾸면

- 위와 같이 정상적으로 해당 경로로 접근이 가능한 것을 확인 할 수 있다.

728x90