IT

스프링시큐리티 7장

프로개발러 2023. 7. 28. 13:01
반응형

https://github.com/wikibook/spring-security/tree/main/ssia-ch7-ex1

 

 

pom.xml에

 

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <scope>test</scope>
</dependency>

 

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

 

추가

 

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.httpBasic();


    http.authorizeRequests().anyRequest().access("hasAuthority('WRITE')");
}

 

WebSecurityConfigurerAdapter를 상속받은 클래스에서 write권한이 있는 사람만 접근가능하도록 설정

 

//http.authorizeRequests().anyRequest().permitAll();//모든 요청에 대해 엑세스 허용

 

그냥 호출하면 401에러 떨어짐

 

C:\Users\user>curl http://localhost:8080/hello
{"timestamp":"2023-07-28T03:54:27.140+00:00","status":401,"error":"Unauthorized","message":"","path":"/hello"}

 

 

정상적인 경우

C:\Users\user>curl -u jane:12345 http://localhost:8080/hello
Hello!

 

 

참고로 jane은 write권한이 있음

 

var user2 = User.withUsername("jane")
                .password("12345")
                .authorities("WRITE")
                .build();

manager.createUser(user1);
manager.createUser(user2);

 

 

john은 권한이 없기 때문에 접근금지

 

C:\Users\user>curl -u john:12345 http://localhost:8080/hello
{"timestamp":"2023-07-28T03:56:47.035+00:00","status":403,"error":"Forbidden","message":"","path":"/hello"}

 

var user1 = User.withUsername("john")
                .password("12345")
                .authorities("READ")
                .build();

 

참고로 서버 시작시에

 

가장 먼저 시작되는 것이 메인클래스 그 다음이

 

@SpringBootApplication
public class Main {

    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }

 

 

configuration 부분

@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {

    @Override
    @Bean
    public UserDetailsService userDetailsService() {
        var manager = new InMemoryUserDetailsManager();

 

 

그리고 hasAnyAuthority 사용하면 write 혹은 read만 있더라도 접근 허용 가능

http.authorizeRequests().anyRequest().hasAnyAuthority("WRITE", "READ");

 

expression으로 권한 관리를 할 수 있는건 처음 알음...

String expression = "hasAuthority('READ') and !hasAuthority('DELETE')";
http.authorizeRequests().anyRequest().access(expression);

 

@Override
@Bean
public UserDetailsService userDetailsService() {
    var manager = new InMemoryUserDetailsManager();

    var user1 = User.withUsername("john")
                    .password("12345")
                    .authorities("READ","WRITE")
                    .build();

    var user2 = User.withUsername("jane")
                    .password("12345")
                    .authorities("READ","DELETE")
                    .build();

    manager.createUser(user1);
    manager.createUser(user2);

    return manager;
}

 

이런식으로 주고

 

 

C:\Users\user>curl -u john:12345 http://localhost:8080/hello
Hello!

 


C:\Users\user>curl -u jane:12345 http://localhost:8080/hello
{"timestamp":"2023-07-28T04:06:31.867+00:00","status":403,"error":"Forbidden","message":"","path":"/hello"}

각각 존과 제인을 호출했을 경우

제인은 forbidden.

 

 

그리고 스프링부트 테스트도 잘만듬.

 

@SpringBootTest
@AutoConfigureMockMvc
public class MainTests {

    @Autowired
    private MockMvc mvc;

    @Test
    @DisplayName("The endpoint cannot be called unauthenticated")
    public void testFailedAuthentication() throws Exception {
        mvc.perform(get("/hello"))
                .andExpect(unauthenticated());
    }

    @Test
    @DisplayName("A user without privileges can authenticate but is not authorized")
    @WithUserDetails("john")
    public void testSuccessfulAuthentication() throws Exception {
        mvc.perform(get("/hello"))
             .andExpect(authenticated())
             .andExpect(status().isForbidden());
    }

    @Test
    @DisplayName("A user with privileges can authenticate and is authorized")
    @WithUserDetails("jane")
    public void testSuccessfulAuthorization() throws Exception {
        mvc.perform(get("/hello"))
                .andExpect(authenticated())
                .andExpect(status().isOk());
    }
}

 

.andExpect(unauthenticated());를 써서 만약 인증을 통과하지 못했을 경우를 가정

 

 

그리고 아래는 인증 통과하고 정상적으로 권한획득한 경우를 가정해서 테스트.

.andExpect(authenticated())
.andExpect(status().isOk());

 

 

그다음 .role()부분 설명이 있는데 이 부분은 .authorities와 크게 차이가 없으므로 패스

 

그다음 denyAll()에 대한 설명이 나오는데

 

denyAll()은 말 그대로 전부 허용 거부.

 

테스트를 위해 hello2 메소드를 컨트롤러에 추가한다.

 

@GetMapping("/hello")
public String hello() {
    return "Hello!";
}

@GetMapping("/hello2")
public String hello2() {
    return "Hello2!";
}

 

 

그후에 아래 환경설정으로 테스트하면

String expression = "hasAuthority('READ') and !hasAuthority('DELETE')";
http.authorizeRequests().anyRequest().access(expression);

 

john : read/write 권한보유

jane : read/delete 권한보유

 

john은 hello / hello2 uri 모두 통과

jane은 모두 실패

 

근데 아래처럼 테스트하면

 

String expression = "hasAuthority('READ') and !hasAuthority('DELETE')";
http.authorizeRequests().antMatchers("/hello").access(expression).antMatchers("/**").denyAll();

 

john은 hello 에 대해서만 통과 / hello2는 denyall로 실패

jane은 모두 실패

 

원래 생각은 어차피 특정 url만 접근하도록 권한을 주면 굳이 denyall을 안써도 될 것 같다고 생각해서 테스트.

근데 보면 알겠지만 denyall을 걸어버리면 hello만 접근되고 hello2는 접근불가됨.

 

반응형