web-dev-qa-db-ja.com

Spring Boot-JWT、OAuth、および個別のリソースサーバーと認証サーバーの使用

JWTトークンとOAuth2プロトコルを使用するSpringアプリケーションを構築しようとしています。 このチュートリアル のおかげで認証サーバーを実行しています。ただし、リソースサーバーを正しく機能させるのに苦労しています。記事をフォローすることから、そして 前の質問 への応答のおかげで、これは私の現在の試みです:

リソースサーバーのセキュリティ構成:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${security.signing-key}")
    private String signingKey;

    @Value("${security.encoding-strength}")
    private Integer clientID;

    @Value("${security.security-realm}")
    private String securityRealm;

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setVerifierKey(signingKey);
        return converter;
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean ResourceServerTokenServices tokenService() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    }
    @Override
    public AuthenticationManager authenticationManager() throws Exception {
        OAuth2AuthenticationManager authManager = new OAuth2AuthenticationManager();
        authManager.setTokenServices(tokenService());
        return authManager;
    }

}

リソースサーバーの構成:

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Autowired
    private ResourceServerTokenServices tokenServices;

@Value("${security.jwt.resource-ids}")
private String resourceIds;

@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
    resources.resourceId(resourceIds).tokenServices(tokenServices);
}

@Override
public void configure(HttpSecurity http) throws Exception {
    http.requestMatchers().and().authorizeRequests().antMatchers("/actuator/**", "/api-docs/**").permitAll()
            .antMatchers("/**").authenticated();
}

}

Authorization Serverのセキュリティ構成(上記のチュートリアルから):

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${security.signing-key}")
    private String signingKey;

    @Value("${security.encoding-strength}")
    private Integer encodingStrength;

    @Value("${security.security-realm}")
    private String securityRealm;

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
                .passwordEncoder(new ShaPasswordEncoder(encodingStrength));
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .httpBasic()
                .realmName(securityRealm)
                .and()
                .csrf()
                .disable();

    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(signingKey);
        return converter;
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    @Primary //Making this primary to avoid any accidental duplication with another token service instance of the same name
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    }
}

これで、リソースサーバーにリクエストを送信しようとすると、次のようなエラーが表示されます。

{"error":"invalid_token","error_description":"Invalid access token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidGVzdGp3dHJlc291cmNlaWQiXSwidXNlcl9uYW1lIjoiam9obi5kb2UiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXh wIjoxNTE1MTE3NTU4LCJhdXRob3JpdGllcyI6WyJTVEFOREFSRF"}

いくつか質問があります。

  • 私の理解では、この問題はトークンストアに関連していることがよくあります。 JwtTokenStoreが使用されている場合、サーバーの分離をどのように処理しますか?
  • 次に、現在、私のリソースアプリはキーへのアクセスに依存しています。 JWTと0Authの仕様を理解するために、これは必要ないはずです。むしろ、検証を認証サーバー自体に委任できるはずです。 Springのドキュメントから、次のプロパティが適用できると思いましたsecurity.oauth2.resource.token-info-uri=http://localhost:8080/oauth/check_token。ただし、Resource Serverでキーに依存しないようにしようとすると、ResourceServerTokenServiceの設定で問題が発生します。サービスはトークンストアを想定しており、JWTTokenStoreはJwtAccessTokenConverterを使用し、コンバーターはキーを使用します(キーを削除すると同じinvalid token以前に経験したエラー)。

AuthサーバーとResourceサーバーを分離する方法を示す記事を見つけるのに本当に苦労しています。何かアドバイスをいただければ幸いです。

編集:実際には、リソースサーバーのコードは次のメッセージでコンパイルに失敗しています:

Caused by: Java.lang.IllegalStateException: For MAC signing you do not need to specify the verifier key separately, and if you do it must match the signing key

5
KellyM

Spring oauthを試しましたが、同じエラーが発生しました:

Caused by: Java.lang.IllegalStateException: For MAC signing you do not need to specify the verifier key separately, and if you do it must match the signing key

私の間違いは、私の公開証明書が次のとおりだったということでした:

-----BEGIN PUBLIC KEY-----
tadadada
-----END PUBLIC KEY-----
-----BEGIN CERTIFICATE-----
tadadada
-----END CERTIFICATE-----

そして、これは許可されていません。証明書を削除します。このファイルに公開鍵を入れてください。

-----BEGIN PUBLIC KEY-----
tadadada
-----END PUBLIC KEY-----

そして、起動エラーは消えます。

あなたの2番目の質問については、それが私が理解していることです:

  • 認証サーバーは、ユーザーのすべてのアクセス許可を含む暗号化されたトークン(秘密鍵で暗号化された)を提供します。

  • リソースサーバーは公開鍵を使用してトークンを復号化し、トークンに含まれるアクセス許可がTRUEであると想定します。

この助けを願っています。

4
Oreste Viron

公開鍵が次のようにフォーマットされているときに、この問題が発生しました。

"-----BEGIN RSA PUBLIC KEY-----\n${encoder.encodeToString(keyPair.public.encoded)}\n-----END RSA PUBLIC KEY-----\n"

これを次のように変更したとき:

"-----BEGIN PUBLIC KEY-----\n${encoder.encodeToString(keyPair.public.encoded)}\n-----END PUBLIC KEY-----\n"

キーが受け入れられました。

0
Bas Kuis