인프런 커뮤니티 질문&답변

은빛갈기님의 프로필 이미지

작성한 질문수

스프링 시큐리티 OAuth2

RSA 검증 기능 구현 - PublicKey.txt 에 의한 검증

RSA 검증 기능 구현 - PublicKey.txt 에 의한 검증 46:20

22.10.27 15:28 작성

·

458

0

/api/user get 요청시

JwtAuthorizationRsaPublicKeyFilter#doFilterInternal

코드를 블록에서 에러가 발생합니다.

Jwt jwt = jwtDecoder.decode(getToken(request));
String username = jwt.getClaimAsString("username");

디버깅으로 따라가봤습니다.

JwtAuthenticationFilter#getToken 리턴값으로

(토큰값에서 Bearer를 없애고 리턴한 값)

Jwt jwt = jwtDecoder.decode(getToken(request));

위 코드를 실행했었어야 했는데 자꾸 프로그램이 종료가 됩니다?

원인을 알 수 가없습니다.

스크린샷 2022-10-27 오후 3.16.26.png스크린샷 2022-10-27 오후 3.16.44.png

깃 클론 링크입니다.

git@github.com:InSuChoe/spring-security-oauth2.git

답변 3

0

정수원님의 프로필 이미지
정수원
지식공유자

2022. 10. 28. 13:11

깃헙에 올려주신 소스와 해당 강의 챕터에서 제공하는 강의의 소스와 일부 다른 부분이 있어 정확한 테스트가 어려운 점이 있습니다.

정확한 원인을 발견하기 위해서 가급적 소스를 바로 실행할 수 있는 상태로 올려 주시기 부탁드립니다.

일단 제가 소스를 원 상태로 정리한 후에 실행한 결과입니다.

오류는 발생하지 않았습니다.

다만 모든 검증이 완료되고 인증완료 후 IndexController 의 /api/user 로 가게 되는데 인증객체타입이 맞지 않아서 오류가 나고 있습니다.

이 부분을 제외하면 정상적으로 동작하는 것 같습니다.

제가 정리한 소스를 참고해 주시기 바랍니다.

OAuth2ResourceServer.java

package io.security.oauth2.springsecurityoauth2.config;

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.jwk.RSAKey;
import io.security.oauth2.springsecurityoauth2.filter.authentication.JwtAuthenticationFilter;
import io.security.oauth2.springsecurityoauth2.filter.authorization.JwtAuthorizationRsaPublicKeyFilter;
import io.security.oauth2.springsecurityoauth2.signature.RSAPublicKeySecuritySigner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
public class Oauth2ResourceServer {
    /*@Bean
    public SecurityFilterChain securityFilterChain1(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.antMatcher("/photos/1")
                .authorizeRequests(req -> req.antMatchers("/photos/1")
                        .hasAuthority("SCOPE_photo")
                        .anyRequest().authenticated());
        httpSecurity.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
        return httpSecurity.build();
    }
    @Bean
    public SecurityFilterChain securityFilterChain2(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.antMatcher("/photos/2")
                .authorizeRequests(req -> req.antMatchers("/photos/2")
                        .permitAll()
                        .anyRequest().authenticated());
        httpSecurity.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
        return httpSecurity.build();
    }*/
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {

        httpSecurity.csrf().disable();
        httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        httpSecurity.authorizeRequests(req -> req.antMatchers("/").permitAll()
                .anyRequest().authenticated());
        httpSecurity.userDetailsService(userDetailsService());
//        httpSecurity.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
        httpSecurity.addFilterBefore(jwtAuthenticationFilter(null, null), UsernamePasswordAuthenticationFilter.class);
        httpSecurity.addFilterBefore(jwtAuthorizationRsaPublicKeyFilter(null),UsernamePasswordAuthenticationFilter.class);
        return httpSecurity.build();
    }

@Bean public JwtAuthorizationRsaPublicKeyFilter jwtAuthorizationRsaPublicKeyFilter(JwtDecoder jwtDecoder) throws JOSEException {
    return new JwtAuthorizationRsaPublicKeyFilter(jwtDecoder);
}
//    @Bean
//    public JwtAuthorizationRsaFilter jwtAuthorizationRsaFilter(RSAKey rsaKey) throws JOSEException {
//        return new JwtAuthorizationRsaFilter(new RSASSAVerifier(rsaKey.toRSAPublicKey()));
//    }


//    @Bean
//    public JwtAuthorizationMacFilter jwtAuthorizationMacFilter(OctetSequenceKey octetSequenceKey) throws JOSEException {
//        return new JwtAuthorizationMacFilter(new MACVerifier(octetSequenceKey.toSecretKey()));
//    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter(RSAPublicKeySecuritySigner rsaPublicSecuritySigner, RSAKey rsaKey) throws Exception {
        JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(rsaPublicSecuritySigner, rsaKey);
        jwtAuthenticationFilter.setAuthenticationManager(authenticationManager(null));
        return jwtAuthenticationFilter;

    }


    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails user = User.withUsername("user")
                .password("1234")
                .authorities("ROLE_USER")
                .build();
        return new InMemoryUserDetailsManager(user);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
}

 

application.yml

server:
  port: 8081

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
#          jwk-set-uri: http://localhost:8080/realms/oauth2/protocol/openid-connect/certs
#          jws-algorithms: RS256
          public-key-location: classpath:certs/publicKey.txt
  main:
    allow-bean-definition-overriding: true

IndexController.java

인증이 완료되면 UsernamePasswordAuthenticationToken 타입으로 인증객체가 생성됩니다.
JwtAuthenticationToken 은 시큐리티의 내장 필터인 BearerTokenAuthenticationFilter 사용했을 때 검증 이후 생성되는 객체입니다.
해당 강의 소스에서는 JwtAuthorizationRsaPublicKeyFilter 에서 검증처리 및 인증객체를 생성 하고 있습니다.

package io.security.oauth2.springsecurityoauth2.controller;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.net.URI;
import java.net.URISyntaxException;

@RestController
public class IndexController {
    @GetMapping("/index")
    public String index(Authentication authentication){
        return "index";
    }
    @GetMapping("/api/user")
    public Authentication apiUser(Authentication authentication, @AuthenticationPrincipal Jwt principal) throws URISyntaxException {
        if(authentication instanceof JwtAuthenticationToken) {
            JwtAuthenticationToken authenticationToken = (JwtAuthenticationToken) authentication;
            String sub = (String) authenticationToken.getTokenAttributes().get("sub");
            String email = (String) authenticationToken.getTokenAttributes().get("email");
            String scope = (String) authenticationToken.getTokenAttributes().get("scope");

            String sub1 = principal.getClaim("sub");
            String token = principal.getTokenValue();

            RestTemplate restTemplate = new RestTemplate();
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.add("Authorization", "Bearer " + token);
            RequestEntity<String> reqest = new RequestEntity<>(httpHeaders, HttpMethod.GET, new URI("http://localhost:8082"));
//        ResponseEntity<String> response = restTemplate.exchange(reqest, String.class);
//        String body = response.getBody();
        }

        return authentication;
    }
}

0

은빛갈기님의 프로필 이미지
은빛갈기
질문자

2022. 10. 27. 19:19

다시 올렸습니다.

0

정수원님의 프로필 이미지
정수원
지식공유자

2022. 10. 27. 17:46

깃헙 소스가 정상 동작하지 않는 것 같습니다.

JwtAuthorizationRsaPublicKeyFilter

도 내용이 비어 있습니다.

확인 부탁드립니다.

은빛갈기님의 프로필 이미지
은빛갈기
질문자

2022. 10. 28. 09:30

다시 올렸습니다