문득 점프 투 스프링부트 교재를 공부하다 시큐리티는 인증과 권한을 담당한다는 문구를 보고
관리자같은 권한을 따로 부여하는 방법이 궁금해졌다.
해당 글은 스프링 시큐리티 - 점프 투 스프링부트(게시판 만들기) 편과 연계된다.
만들어 놓았던 SecurityConfig를 살펴보자.
package com.example.board.practice;
(... 생략 ...)
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests().requestMatchers(
new AntPathRequestMatcher("/**")).permitAll()
.and()
.csrf().ignoringRequestMatchers(
new AntPathRequestMatcher("/h2-console/**"))
.and()
.headers()
.addHeaderWriter(new XFrameOptionsHeaderWriter(
XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN))
.and()
.formLogin()
.loginPage("/user/login")
.defaultSuccessUrl("/")
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/user/logout"))
.logoutSuccessUrl("/")
.invalidateHttpSession(true)
;
return http.build();
}
(... 생략 ...)
}
http.authorizeHttpRequests().requestMatchers(new AntPathRequestMatcher("/**")).permitAll();
해당 문장은 [ localhost:8080/~~ ] URL이하 어떠한 주소가 달려도 모두에게 접근권한을 준다는 의미였다.
맞게 이해한 건지 확인을 위해 **을 빼고 들어가자 로그인창이 떳다.
관리자일 때만 해당 페이지에 들어가지게 만들고 싶었다.
(... 생략 ...)
http.authorizeHttpRequests().requestMatchers(
new AntPathRequestMatcher("/")).permitAll()
.antMatchers("/question/detail/**").hasRole("ADMIN")
.and()
.csrf().ignoringRequestMatchers(
new AntPathRequestMatcher("/h2-console/**"))
(... 생략 ...)
antMachers("해당 주소").hasRole("ADMIN") 코드를 작성하였다
로그인을 했음에도 ADMIN이 아니기 때문에 페이지에 접근 불가능한 모습이다.
403 Forbidden
이 에러는 서버 자체 또는 서버에 있는 파일에 접근할 권한이 없을 경우에 발생한다. 서버에는 외부 접근을 제어하기 위한 수많은 권한 설정이 있고, 서버에서 설정해 둔 권한과 맞지 않는 접속 요청이 들어오면 접근을 거부하고 접근거부 코드를 반환하는데, 이 때 뜨는 것이 바로 403 Forbidden 에러다.
권한 설정 및 로그인, 로그아웃 그리고 403 Forbidden의 핸들링 처리를 보통 아래와 같이 한다고 한다.
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 페이지 권한 설정
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/myinfo").hasRole("MEMBER")
.antMatchers("/**").permitAll()
.and() // 로그인 설정
.formLogin()
.loginPage("/user/login")
.defaultSuccessUrl("/user/login/result")
.permitAll()
.and() // 로그아웃 설정
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/user/logout"))
.logoutSuccessUrl("/user/logout/result")
.invalidateHttpSession(true)
.and()
// 403 예외처리 핸들링
.exceptionHandling().accessDeniedPage("/user/denied");
}
}
뷰단에서(템플릿) 이런식으로의 활용도 가능하다고 한다.
<a href="members/login" sec:authorize="isAnonymous()">로그인</a>
<a href="members/new" sec:authorize="isAnonymous()">회원가입</a>
<a href="members/logout" sec:authorize="isAuthenticated()">로그아웃</a>
<a href="util/find" sec:authorize = "hasRole('USER')">찾아보기</a>
<a href="util/register" sec:authorize = "hasRole('ADMIN')">등록하기</a>
해당 앵커 태그들은 권한에 따라 이동하는 링크가 다르게 셋팅되어있다.
시큐리티 표현식
스프링 시큐리티에서의 표현식은 아래와 같다. 하나 유의할 점은 isAnonymous()의 경우 anonynous 인증 객체를 가진 상태이기 때문에, 일반 유저로 로그인하여 Role_USER 권한을 가진 경우 접근이 불가하다. 모든 사용자가 접근이 가능하도록 만들고 싶다면, permitAll을 해야한다.
표현식 | 설명 |
hasRole(String role) | 해당 롤을 가지고 있는 경우 true |
hasAnyRole(String… roles) | 해당 롤 중에 하나를 가지고 있는 경우 true |
isAnonymous() | 익명 사용자인 경우 true |
isRememberMe() | Remember Me 인증을 통해 로그인한 경우 true |
isAuthenticated() | 이미 인증된 사용자인 경우 true |
isFullyAuthenticated() | Remember Me가 아닌 일반적인 인증 방법으로 로그인한 경우 true |
permitAll | 항상 true |
denyAll | 항상 false |
principal | 인증된 사용자의 사용자 정보(UserDetails 구현한 클래스의 객체) 반환 |
authentication | 인증된 사용자의 인증 정보**(Authentication** 구현한 클래스의 객체) 반환 |
참조
https://whitepro.tistory.com/481?category=1058993
2023.04 ~ 백엔드 개발자의 기록
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!