私はoAuth2を使用するAPIと、このAPIをバックエンドとして使用する自分のモバイルアプリを持っています。ユーザーは複数のデバイス(iPhone、iPad、AndroidタブレットまたはAndroid電話))を介して同時にログオンできるため、APIで区別する必要があります各接続間。私はこれを個別のアクセストークンを介して行いたい:各クライアントは個別のアクセストークンを取得します。
問題は、現在使用している実装(spring-security-oauth2)がclient_id、username、およびscopeに基づいて一意のキーを生成することです。つまり、基本的に、アクセストークンを取得すると、すべてのクライアントが同じユーザーの同じアクセストークンを取得します。これは、DefaultAuthenticationKeyGeneratorを使用して行われます。
認証キージェネレータを無視して、クライアントからのリクエストごとに新しいアクセストークンを作成しても安全ですか?
Springクラウドはすでにこの動作を提供しています。異なるクライアントを追加するだけです。 iosAppClientと同様に、AuthorizationServerConfigurationクラスのandroidAppClient。
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("androidAppclient")
.secret("clientsecret")
.autoApprove(true)
.accessTokenValiditySeconds(120)
.authorizedGrantTypes("password")
.resourceIds("accountservice")
.scopes("read", "write")
.and()
.withClient("iosappclient")
........
}
バックエンドでは、次のようにclientIDを取得できます
clientId = ((OAuth2Authentication) authentication).getOAuth2Request().getClientId();
clientIdに基づいて異なる動作を実装します。
1つの答えは、各アプリプラットフォームは異なるクライアントであるため、異なるクライアントIDを持つ必要があるということです。 1つはiOSアプリ用、もう1つはWebサイト用などです。
たとえばiPadとiPhoneの違いについては、OAuthシステムに依存しないことをお勧めします。
Spring BootとOAuth2を使用してバックエンドを開発しているときに、同じ問題に遭遇しました。私が遭遇した問題は、複数のデバイスが同じトークンを共有している場合、一方のデバイスがトークンを更新すると、もう一方のデバイスは無知であり、要するに、両方のデバイスがトークン更新狂乱に入るということでした。私の解決策は、デフォルトのAuthenticationKeyGenerator
を、DefaultAuthenticationKeyGenerator
をオーバーライドするカスタム実装で置き換え、新しいパラメーターclient_instance_id
をキージェネレーター混合物に追加することでした。モバイルクライアントは、アプリのインストール(iOSまたはAndroid)全体で一意である必要があるこのパラメーターを送信します。ほとんどのモバイルアプリはすでに何らかの形でアプリケーションインスタンスを追跡しているため、これは特別な要件ではありません。
public class EnhancedAuthenticationKeyGenerator extends DefaultAuthenticationKeyGenerator {
public static final String PARAM_CLIENT_INSTANCE_ID = "client_instance_id";
private static final String KEY_SUPER_KEY = "super_key";
private static final String KEY_CLIENT_INSTANCE_ID = PARAM_CLIENT_INSTANCE_ID;
@Override
public String extractKey(final OAuth2Authentication authentication) {
final String superKey = super.extractKey(authentication);
final OAuth2Request authorizationRequest = authentication.getOAuth2Request();
final Map<String, String> requestParameters = authorizationRequest.getRequestParameters();
final String clientInstanceId = requestParameters != null ? requestParameters.get(PARAM_CLIENT_INSTANCE_ID) : null;
if (clientInstanceId == null || clientInstanceId.length() == 0) {
return superKey;
}
final Map<String, String> values = new LinkedHashMap<>(2);
values.put(KEY_SUPER_KEY, superKey);
values.put(KEY_CLIENT_INSTANCE_ID, clientInstanceId);
return generateKey(values);
}
}
次に、同様の方法で注入します。
final JdbcTokenStore tokenStore = new JdbcTokenStore(mDataSource);
tokenStore.setAuthenticationKeyGenerator(new EnhancedAuthenticationKeyGenerator());
HTTPリクエストは次のようになります
POST /oauth/token HTTP/1.1
Host: {{Host}}
Authorization: Basic {{auth_client_basic}}
Content-Type: application/x-www-form-urlencoded
grant_type=password&username={{username}}&password={{password}}&client_instance_id={{instance_id}}
このアプローチを使用する利点は、クライアントがclient_instance_id
を送信しない場合、デフォルトのキーが生成され、インスタンスが提供されると、同じインスタンスに対して毎回同じキーが返されることです。また、キーはプラットフォームに依存しません。欠点は、MD5ダイジェスト(内部で使用される)が2回呼び出されることです。