티스토리 뷰

Spring

Spring Security 적용

Shmoon 2019. 10. 29. 00:56

버전 정보

Spring-Boot 2.2.1.BUILD-SNAPSHOT

Java 1.8

Maven 4.0.0

Spring-security-Oauth2 2.3.7.RELEASE

Spring-security-jwt 1.0.11.RELEASE

 

사용 이유

문서가 오래간만에 업데이트...?

 

모바일 환경에서 stateless 하게 사용할 수 있는 인증인  JWT 사용하기로 결정

이전에는 Github 에서 샘플로 올려둔 프로젝트를 기반으로 변경했는데 좀 자세히 기술 해봐야 할 것 같아서 글로 남긴다

 

읽기전

@Configuration
public class WebFilterConfig implements WebMvcConfigurer {

    @Bean
    public FilterRegistrationBean<Filter> myFilter(){
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new TokenFilter());
        bean.setUrlPatterns(Arrays.asList("/test/*")); 
        return bean;
    }

}

진짜 간단히 JWT만 사용할거면 WebMvcCnofigurer 상속받아서 필터 등록하고 빈으로 내보내면 된다.

고민하지 말고 소규모면 더이상 읽지 말고 필터에서 JWT토큰 parse 메소드 호출해서 그냥 하면 속편하다.

얼마 전 면접에서 시큐리티를 난 쫌 안다고 생각했는데 너무 털려서 회사에 적용해보는 것이다...

 

시작.

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-oauth2-resource-server</artifactId>
	<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-oauth2-jose</artifactId>
	<version>5.2.0.RELEASE</version>
</dependency>

스프링 시큐리티 적용 성공!

이제 런 하면 오류가 빠바박 뜬다...

 

Correct the classpath of your application so that it contains a single, 
compatible version of 
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration

파파고 형님의 해석은

응용프로그램의 클래스 경로를 수정하여 호환되는 단일 org.springframework.boot.autoconfigure 버전을 포함하도록 하십시오.웹.서블릿WebMvcAutoConfiguration$EnableWebMvc 구성 하란다. 

 

버전이 안맞는구나... 해당 원인은

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-autoconfigure</artifactId>
	<version>2.2.0.RELEASE</version>  <!-- 원래는 2.1.8.RELEASE 였다 -->
</dependency>

다음과 같다.

2.1.8 버전은 예전에 쓰던 스프링 2.0.0RELEASE 버전에서 사용하던  Autoconfigure 라서 버전 호환 오류가 났던 것이다.

즉 버전이 호환 오류가 날 경우 스프링 부트의 autoconfigure  버전이 현재 spring-boot 버전을 지원 하는지를 봐야한다.

 

이후 localhost 접속을 하면 

아주 이쁘게 변한 기본 로그인 화면이 나온다.

2.0.0.RELEASE 때는 html 6시간 배운 사람이 만든 페이지처럼 나오더니...

 

난 세션 안쓰고 jwt 토큰으로 할껀댕... 아무리봐도 로그인 후 스프링시큐리티 세션으로 처리하는 화면같단 말이야..

 

자 일단 oauth2 인증과정을 -> jwt 로 전환하면 되는거라

토큰 발급, 토큰 인증을 커스터마이징 한다는 목표를 가지고 시작해보자.

 

https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#servlet

 

Spring Security Reference

The authenticator is also responsible for retrieving any required user attributes. This is because the permissions on the attributes may depend on the type of authentication being used. For example, if binding as the user, it may be necessary to read them

docs.spring.io

여기 보면 스프링 5.2버전의 시큐리티 문서가 나와있다.

우선 스프링 부트 2.2.1.BUILD-SNAPSHOT 은 스프링 시큐리티 5.2를 기반으로 한다.

<spring-security.version>5.2.0.RELEASE</spring-security.version>

POM에 부모쪽을 잘 찾아가보면 나온다.

참고로

<tomcat.version>9.0.27</tomcat.version>
<undertow.version>2.0.27.Final</undertow.version>

이렇다.

 

각설하고 해당 문서에.

Servlet, WebFlux 두가지 종류가 있다고 한다. 

Servlet은 우리가 계속 써왔던 것이고

WebFlux는 2.0.0 버전에서 추가된 스프링 프레임워크의 새로운 모듈이다. 

비동기로 연속적인 요청을 처리하여 효율을 높이는 구조라던가 뭔가.. 기억이 잘 안난다. 2.0.0 버전에서 사용하려고 했을 때에는

WebSocket 라이브러리가 기존 사용하던 것과 매우 다르고 일정이 촉박하여 Servlet 으로 사용했었다. 지금도 마찬가지고. 다음 프로젝트에 적용 예정이다. 

 

내가 봐야 할 태그는 

https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#oauth2

 

Spring Security Reference

The authenticator is also responsible for retrieving any required user attributes. This is because the permissions on the attributes may depend on the type of authentication being used. For example, if binding as the user, it may be necessary to read them

docs.spring.io

이 섹션이다.

 

우선 나는 이미 PHP에서 구동중인 서버에 JWT 로그인이 되어있기 때문에 지금 만드는 기능은 해당 토큰이 올바른 토큰인지 아닌지만 판별하면 된다. 문서의 셋팅에는 해당 토큰의 유효성을 검사하는 외부 서버 주소를 넣는 프로퍼티도 있었지만 php에 손대기엔 부담스러워서 자체 검증으로 해야 할 것 같다.

 

외부 JWT 인증 url을 사용하는 것과

내부에서 공개키로 해당 키를 검증하는 셋팅 두가지가 문서에 나와있다.

 

@EnableWebSecurity
public class DirectlyConfiguredJwkSetUri extends WebSecurityConfigurerAdapter {
    protected void configure(HttpSecurity http) {
        http
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .oauth2ResourceServer()
                .jwt()
                    .jwkSetUri("https://idp.example.com/.well-known/jwks.json");
    }
}

이게 외부 인증서버를 따로 발급 했을 시 해당 토큰이 유효한지 확인하는 주소를 넣는 방식이고

@EnableWebSecurity
public class DirectlyConfiguredJwtDecoder extends WebSecurityConfigurerAdapter {
    protected void configure(HttpSecurity http) {
        http
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .oauth2ResourceServer()
                .jwt()
                    .decoder(myCustomDecoder());
    }
}

이 방식이 디코더에 공개키로 값을 확인하는 방법이다.

난 php 소스를 건들기 힘드니 decoder 를 빈으로 띄워서 넣겠다.

문서에서는 NimbusJwtDecoder 를 쓰라고 나와있는데 내가 버전이 낮은건지.. 문서가 업데이트 안된건지..

@Bean
JwtDecoder jwtDecoder() {
    return NimbusJwtDecoder.fromJwkSetUri(this.jwkSetUri)
            .jwsAlgorithm(RS512).build();
}

해당 디코더를 만들 수가 없더라. fromJwkSetUri 메소드도 없고..

NimbusJwtDecoder 클래스에 가보니 이너 클래스로 Builder 클래스들이 엄청 많더라..

@Override
protected void configure(HttpSecurity httpSecurity) throws Exception{
    httpSecurity
            .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            .authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .oauth2ResourceServer()
            .jwt().decoder(jwtDecoder());
}

jwt().decoder에 디코더를 넣어야 하는대 오류가 많이났다.

PHP에서 생성한 토큰을 

NimbusJwtDecoder.withSecretKey(SECRET_KEY).build()

org.springframework.security.oauth2.jwt.NimbusJwtDecoder 의 withSecretKey 를 사용하여 JwtDecoder를 만들면

자꾸 오류가 났다.

 

그래서 decoder jwtDecoder 를 상속받은 커스텀 디코더를 만들기로 결정.

이 판단을 한 이유는 io.jsonwebtoken.Jwts 에서는 파싱이 잘 되는데 Oauth2 Jwt 라이브러리로 파싱하면 계속 파싱 오류가 나기 때문이었다.

 

@Bean
    public JwtDecoder jwtDecoder() {
        return new JwtDecoderImpl();
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception{
        httpSecurity
                .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .oauth2ResourceServer()
                .jwt().decoder(jwtDecoder());
    }

로 셋팅을 정상적으로 마무리 지었다.

 

PHP 생성 한 토큰으로 정상적인 요청이 오나 확인

정상동작

 

토큰값 변경 후 401 떨어지는지 확인 완료

 

일단.. 퇴근하고 이번에 Security 5.2 를 봐야겠다.

Jwt파싱이 왜 안되는지

 

후에 PHP 기능이 Spring으로 전부 이관될 때 토큰생성하는 기능을 추가 예정이다

'Spring' 카테고리의 다른 글

Docker 적용하기 (Spring Boot)  (0) 2019.11.27
Docker 적용하기 (Mariadb)  (0) 2019.11.26
Project Module  (0) 2019.11.21
Kafka 적용일기 1..  (0) 2019.11.08
Java Builder Pattern  (0) 2019.11.05
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함