web-dev-qa-db-ja.com

カスタムUserDetailsS​​erviceをSpringSecurityOAuth2アプリに追加します

以下のカスタムUserDetailsServiceこのSpring OAuth2サンプル に追加するにはどうすればよいですか?

デフォルトのuserとデフォルトのpasswordは、authserverアプリの application.properties ファイルで定義されています。

ただし、テスト目的で、次のカスタムUserDetailsServicedemoアプリのauthserverパッケージ に追加したいと思います。

package demo;

import Java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.stereotype.Service;

@Service
class Users implements UserDetailsManager {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        String password;
        List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER");
        if (username.equals("Samwise")) {
            auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_HOBBIT");
            password = "TheShire";
        }
        else if (username.equals("Frodo")){
            auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_HOBBIT");
            password = "MyRing";
        }
        else{throw new UsernameNotFoundException("Username was not found. ");}
        return new org.springframework.security.core.userdetails.User(username, password, auth);
    }

    @Override
    public void createUser(UserDetails user) {// TODO Auto-generated method stub
    }

    @Override
    public void updateUser(UserDetails user) {// TODO Auto-generated method stub
    }

    @Override
    public void deleteUser(String username) {// TODO Auto-generated method stub
    }

    @Override
    public void changePassword(String oldPassword, String newPassword) {
        // TODO Auto-generated method stub
    }

    @Override
    public boolean userExists(String username) {
        // TODO Auto-generated method stub
        return false;
    }
}

ご覧のとおり、このUserDetailsServiceはまだautowiredではなく、テスト目的でのみ設計されているため、意図的に安全でないパスワードを使用しています。

ユーザーがusername=Samwisepassword=TheShireとして、またはusername=Frodopassword=MyRingとしてログインできるように、 GitHubサンプルアプリ にどのような特定の変更を加える必要がありますか?AuthserverApplication.Java または他の場所に変更を加える必要がありますか?


提案:


Spring OAuth2開発者ガイド は、GlobalAuthenticationManagerConfigurerを使用してUserDetailsServiceをグローバルに構成するように指示しています。ただし、これらの名前をグーグルで検索しても、役立つ結果は得られません。

また、内部スプリングセキュリティを使用する別のアプリINSTEAD OF OAuthは次の構文を使用してUserDetailsServiceを接続しますが、構文を現在の構文に調整する方法がわかりませんOP:

@Order(Ordered.HIGHEST_PRECEDENCE)
@Configuration
protected static class AuthenticationSecurity extends GlobalAuthenticationConfigurerAdapter {

    @Autowired
    private Users users;

    @Override
    public void init(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(users);
    }
}

次のように、@Autowire内でOAuth2AuthorizationConfigを使用して、UsersAuthorizationServerEndpointsConfigurerに接続してみました。

@Autowired//THIS IS A TEST
private Users users;//THIS IS A TEST

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
   endpoints.authenticationManager(authenticationManager)
        .accessTokenConverter(jwtAccessTokenConverter())
        .userDetailsService(users)//DetailsService)//THIS LINE IS A TEST
        ;
}

ただし、Spring Bootログは、ユーザーSamwiseが見つからなかったことを示しています。これは、UserDetailsServiceが正常に接続されなかったことを意味します。 SpringBootログからの関連する抜粋は次のとおりです。

2016-04-20 15:34:39.998 DEBUG 5535 --- [nio-9999-exec-9] o.s.s.a.dao.DaoAuthenticationProvider    :  
        User 'Samwise' not found
2016-04-20 15:34:39.998 DEBUG 5535 --- [nio-9999-exec-9]   
        w.a.UsernamePasswordAuthenticationFilter :  
        Authentication request failed:  
        org.springframework.security.authentication.BadCredentialsException:  
        Bad credentials

他に何を試すことができますか?

8
CodeMed

SpringSecurityを使用してoauthサーバーを開発するときに、同様の問題が発生しました。更新トークンを認証するためにUserDetailsServiceを追加したかったので、状況は少し異なりましたが、私の解決策はあなたにも役立ちます。

あなたと同じように、私は最初にUserDetailsServiceを使用してAuthorizationServerEndpointsConfigurerを指定しようとしましたが、これは機能しません。これがバグなのか設計によるものなのかはわかりませんが、さまざまなoauth2クラスがそれを見つけるには、UserDetailsServiceAuthenticationManagerに設定する必要があります。これは私のために働いた:

@Configuration
@EnableWebSecurity 
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Autowired
  Users userDetailsService;

  @Autowired
  public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService);
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
    // other stuff to configure your security
  }

}

73行目から次のように変更した場合は、うまくいくと思います。

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.parentAuthenticationManager(authenticationManager)
        .userDetailsService(userDetailsService);
}

もちろん、WebSecurityConfigurerAdapterのどこかに@Autowired Users userDetailsService;を追加する必要もあります。

私が言及したかった他のこと:

  1. これはバージョン固有の可能性があります。私はspring-security-oauth22.0.12を使用しています。
  2. なぜこれがそうなのか、出典を引用することはできません。私の解決策が実際の解決策なのかハックなのかさえわかりません。
  3. ガイドで参照されているGlobalAuthenticationManagerConfigurerはほぼ間違いなくタイプミスであり、Springのソースコードのどこにもその文字列が見つかりません。
4
Manan Mehta

私は同じ問題に遭遇し、元々はMananMehtaが投稿したのと同じ解決策を持っていました。つい最近、SpringSecurityとSpringoauth2のバージョンの組み合わせにより、トークンを更新しようとすると、HTTP500エラーが発生してUserDetailsService is required私のログ。

関連するスタックトレースは次のようになります。

Java.lang.IllegalStateException: UserDetailsService is required.
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$UserDetailsServiceDelegator.loadUserByUsername(WebSecurityConfigurerAdapter.Java:463)
at org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper.loadUserDetails(UserDetailsByNameServiceWrapper.Java:68)
at org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider.authenticate(PreAuthenticatedAuthenticationProvider.Java:103)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.Java:174)
at org.springframework.security.oauth2.provider.token.DefaultTokenServices.refreshAccessToken(DefaultTokenServices.Java:150)

下部に、DefaultTokenServicesがトークンを更新しようとしていることがわかります。次に、AuthenticationManagerを呼び出して再認証します(ユーザーがアクセス許可を取り消した場合やユーザーが削除された場合など)が、ここですべてが解明されます。スタックトレースの一番上に、私の美しいUserDetailsServiceDelegatorではなくloadUserByUsernameUserDetailsServiceへの呼び出しを取得していることがわかります。 WebSecurityConfigurerAdapterの中にUserDetailsServiceを設定しましたが、他に2つのWebSecurityConfigurerAdapterがあります。 1つはResourceServerConfiguration用、もう1つはAuthorizationServerSecurityConfiguration用であり、これらの構成では、設定したUserDetailsServiceを取得できません。

Spring Securityをトレースして、何が起こっているのかをつなぎ合わせると、「ローカル」AuthenticationManagerBuilderと「グローバル」AuthenticationManagerBuilderがあり、それをに設定する必要があることがわかりました。この情報をこれらの他のビルダーコンテキストに渡すためのグローバルバージョン。

したがって、私が思いついた解決策は、他のコンテキストがグローバルバージョンを取得するのと同じ方法で「グローバル」バージョンを取得することでした。 WebSecurityConfigurerAdapterの中には、次のものがありました。

@Autowired
public void setApplicationContext(ApplicationContext context) {
    super.setApplicationContext(context);
    AuthenticationManagerBuilder globalAuthBuilder = context
            .getBean(AuthenticationManagerBuilder.class);
    try {
        globalAuthBuilder.userDetailsService(userDetailsService);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

そして、これはうまくいきました。他のコンテキストには私のUserDetailsServiceがありました。将来この地雷原に出くわす勇敢な兵士のために、これをここに残しておきます。

3
Michael Ressler

私の要件は、oauth2電子メール属性の後ろからデータベースオブジェクトを取得することでした。カスタムユーザー詳細サービスを作成する必要があると思ったので、この質問を見つけました。実際、OidcUserインターフェイスを実装して、そのプロセスにフックする必要があります。

最初はOAuth2UserServiceだと思っていましたが、オープンID接続になるようにAWSCognito認証プロバイダーを設定しました。

//inside WebSecurityConfigurerAdapter

http
.oauth2Login()
.userInfoEndpoint()
.oidcUserService(new CustomOidcUserServiceImpl());

...

public class CustomOidcUserServiceImpl implements OAuth2UserService<OidcUserRequest, OidcUser> {

    private OidcUserService oidcUserService = new OidcUserService();

    @Override
    public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
        OidcUser oidcUser = oidcUserService.loadUser(userRequest);
        return new CustomUserPrincipal(oidcUser);
    }
}

...

public class CustomUserPrincipal implements OidcUser {

    private OidcUser oidcUser;

    //forward all calls onto the included oidcUser
}

カスタムサービスは、特注のロジックを使用できる場所です。 UserDetailsCustomUserPrincipalインターフェースを実装して、自動UIテストを容易にするためにライブとテストに異なる認証メカニズムを使用できるようにする予定です。

0
James Render

誰のためにUserDetailsService is requiredリフレッシュトークンの実行中にエラーが発生し、すでにUserDetailsServiceBeanがあることを確認しました。

これを追加してみてください:

@Configuration
public class GlobalSecurityConfig extends GlobalAuthenticationConfigurerAdapter {
    private UserDetailsService userDetailsService;

    public GlobalSecurityConfig(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    public void init(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }
}

これは私の試行錯誤であり、おそらくあなたにとってはうまくいきません。

ちなみに、春までにBeanが選択する「推測」をあきらめると、AuthorizationServerConfigurerAdapterWebSecurityConfigurerAdapterを拡張して、すべてを自分で構成できますが、春の自動構成の能力が失われると思います。いくつかの構成をカスタマイズする必要があるのに、なぜすべてを構成する必要があるのですか?

0
user1686407