简体   繁体   中英

Verifying access token from Google using spring security

I'm trying to authenticate API-calls with my spring-boot backend by giving it an access token that I've gotten from Google.

From what I understand of the documentation it should be enough to just declare

security.oauth2.resource.jwk.key-set-uri=https://www.googleapis.com/oauth2/v3/certs

in the application.properties file along with enabling resource server and web security.

The token is being sent in in the header on the form

'Authorization': 'Bearer ya29.ImCQBz5-600zVNsB[...]ka-x5kC[...]hvw-BGf3m5Bck-HF[...]44'

When I try to authenticate I get a 401 Unauthorized error with the following console error:

OAuth2AuthenticationProcessingFilter: Authentication request failed: error="invalid_token", error_description="An I/O error occurred while reading the JWT: Invalid UTF-8 start byte 0xad at [Source: (byte[])"??"; line: 1, column: 3]

I'm hoping to use most of what I can of the spring security libraries, but I've tried to write my own simple beans for token management.

@Configuration
@EnableResourceServer
@EnableWebSecurity
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().hasRole("USER");
    }

    @Bean
    public TokenStore tokenStore() {
        return new jwkTokenStore("https://www.googleapis.com/oauth2/v3/certs");
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        return defaultTokenServices;
    }
    @Override
        public void configure(ResourceServerSecurityConfigurer config) {
        config.tokenServices(tokenServices());
    }
}

I expect to authenticate the token and be able to display information.

Do I need to write my own functions to handle this?

Maybe you must implement the WebSecurityConfigurerAdapter

@Configuration

@RequiredArgsConstructor
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class RestSecurityConfig extends WebSecurityConfigurerAdapter {

    private final AADAppRoleStatelessAuthenticationFilter appRoleAuthFilter;

    private final RestAuthenticationEntryPoint unauthorizedHandler;

    private final RestAccessDeniedHandler accessDeniedHandler;

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

        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.authorizeRequests()
                .antMatchers("/actuator/refresh").hasRole("Admin")
                .antMatchers("/actuator/health").permitAll()
                .anyRequest().fullyAuthenticated();

        http.addFilterBefore(appRoleAuthFilter, UsernamePasswordAuthenticationFilter.class);

        http.exceptionHandling()
                .accessDeniedHandler(accessDeniedHandler)
                .authenticationEntryPoint(unauthorizedHandler);

    }
}

I had the same problem. It turns out that you need to import specific jwt dependencies, the default oauth2 dependencies will not work with key-set-uri.

My dependecies that I use:

        'org.springframework.cloud:spring-cloud-starter-security',

        'org.springframework.security:spring-security-oauth2-jose',
        'org.springframework.security:spring-security-oauth2-resource-server',

The second one is the most important. Now, you will have the JwtDecoder and NimbusJwtDecoderJwkSupport on your classpath you can configure SpringBoot. This is my setup:

@NoArgsConstructor
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
class WebAppConfig extends WebSecurityConfigurerAdapter {

  @Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
  private String issuer;

  @Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}")
  private String jwkSetUri;

  @Override
  public void configure(HttpSecurity http) throws Exception {
    http
        .csrf().disable()

        ...

        .and()
        .oauth2ResourceServer()
        .jwt()
        .decoder(decoder());
  }

  private JwtDecoder decoder() {
    List<OAuth2TokenValidator<Jwt>> validators = new ArrayList<>();
    validators.add(new JwtTimestampValidator());
    validators.add(new JwtIssuerValidator(issuer));
    validators.add(new TokenSupplierValidator(List.of(StringUtils.split(androidClientId,","))));

    NimbusJwtDecoderJwkSupport decoder = new NimbusJwtDecoderJwkSupport(jwkSetUri)
    decoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>(validators));
    return decoder;
  }
}

If you need only default JWT token validation, you can go with the default validation (no need for setting up the custom validator).

Hope this helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM