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

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

작성한 질문수

스프링 시큐리티 OAuth2

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

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

작성

·

462

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

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

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

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

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

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

다만 모든 검증이 완료되고 인증완료 후 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

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

다시 올렸습니다.

0

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

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

JwtAuthorizationRsaPublicKeyFilter

도 내용이 비어 있습니다.

확인 부탁드립니다.

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

다시 올렸습니다