web-dev-qa-db-ja.com

Spring SecurityOAuth2用のWebおよびモバイルクライアント

私はOAuth2とSpringSecurity OAuth、特にOAuthプロバイダーサービスに頭を悩ませようとしています。次のことを実装しようとしています。

  1. OAuthプロバイダー
  2. リソースサーバー(OAuthプロバイダー(1))を使用して保護する必要があるRESTful Webサービス)
  3. Webクライアント(Spring Securityを使用して保護されているが、OAuthプロバイダー(1)を使用してユーザーを認証する必要があるWebクライアントアプリケーション)
  4. 認証にOAuthプロバイダー(1))を使用する必要があるネイティブモバイルクライアント(AndroidおよびiOS)

これらのモジュールはすべて互いに独立しています。つまり、異なるプロジェクトに分離されており、(1) http://oauth.web.com 、( 2) http://rest.web.com 、(3) http://web.com

私の2つの質問は次のとおりです。

A. Webクライアントプロジェクトを実装して、ユーザーが保護されたページにログインするか、[ログイン]ボタンをクリックすると、OAuthプロバイダーのURLにリダイレクトされ、ログインして、で認証されるようにするにはどうすればよいですか?すべてのユーザーロールを持つWebクライアントであり、どのクライアントが使用されたかを知る必要もあります。@EnableResourceServer(Resource Serverの実装と同じ方法。以下のコードを参照)このプロジェクトでユーザーの詳細を取得しますか?アクセストークンを管理し、常にリソースサーバーへの呼び出しに含める必要がありますか、それとも何らかの方法で自動的に行うことができますか?

B.開発するモバイルアプリにセキュリティを実装するための最良の方法は何ですか。この認証にはパスワードグランドを使用する必要があります。アプリは自分で作成し、ユーザー名とパスワードをネイティブ画面に表示してから、SSLを介した基本認証としてサーバーに送信するためです。 Spring Security OAuthとユーザーの詳細を返すことについての話を見ることができるサンプルはありますか。

OAuthプロジェクト(1)とリソースプロジェクト(2)の実装は次のとおりです。

1. OAuthプロバイダー

OAuth2サーバー構成(ほとんどのコードは [〜#〜]ここ[〜#〜] から取得されました)

@Configuration
@EnableAuthorizationServer
public class OAuth2ServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;

    @Autowired
    DataSource dataSource;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .tokenStore(tokenStore())
                .approvalStore(approvalStore())
                .authorizationCodeServices(authorizationCodeServices())
        ;
    }

    @Bean
    public JdbcClientDetailsService clientDetailsService() {
        return new JdbcClientDetailsService(dataSource);
    }

    @Bean
    public TokenStore tokenStore() {
        return new JdbcTokenStore(dataSource);
    }

    @Bean
    public ApprovalStore approvalStore() {
        return new JdbcApprovalStore(dataSource);
    }

    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        return new JdbcAuthorizationCodeServices(dataSource);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetailsService());
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {

        oauthServer.checkTokenAccess("permitAll()");
    }
}

Webセキュリティ構成

@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable(); // TODO. Enable this!!!

        http.authorizeRequests()
                .and()
                .formLogin()
//                .loginPage("/login") // manually defining page to login
//                .failureUrl("/login?error") // manually defining page for login error
                .usernameParameter("email")
                .permitAll()

                .and()
                .logout()
//                .logoutUrl("/logout")
                .logoutSuccessUrl("/")
                .permitAll();
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(customUserDetailsService)
                .passwordEncoder(new BCryptPasswordEncoder());
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

UserDetailsS​​ervice(customUserDetailsS​​ervice)

@Service
public class CustomUserDetailsService implements UserDetailsService{

    private final UserService userService;

    @Autowired
    public CustomUserDetailsService(UserService userService) {
        this.userService = userService;
    }

    public Authority loadUserByUsername(String email) throws UsernameNotFoundException {
        User user = userService.getByEmail(email)
                .orElseThrow(() -> new UsernameNotFoundException(String.format("User with email=%s was not found", email)));
        return new Authority(user);
    }
}

2.リソースサーバー(RESTful WS)

構成(スケルトンコードのほとんどは [〜#〜] this [〜#〜] の例から取得されました)

@Configuration
@EnableResourceServer
public class OAuth2ResourceConfig extends ResourceServerConfigurerAdapter{

    @Autowired
    DataSource dataSource;

    String RESOURCE_ID = "data_resource";

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        TokenStore tokenStore = new JdbcTokenStore(dataSource);
        resources
                .resourceId(RESOURCE_ID)
                .tokenStore(tokenStore);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                // For some reason we cant just "permitAll" OPTIONS requests which are needed for CORS support. Spring Security
                // will respond with an HTTP 401 nonetheless.
                // So we just put all other requests types under OAuth control and exclude OPTIONS.
                .authorizeRequests()
                .antMatchers(HttpMethod.GET, "/**").access("#oauth2.hasScope('read')")
                .antMatchers(HttpMethod.POST, "/**").access("#oauth2.hasScope('write')")
                .antMatchers(HttpMethod.PATCH, "/**").access("#oauth2.hasScope('write')")
                .antMatchers(HttpMethod.PUT, "/**").access("#oauth2.hasScope('write')")
                .antMatchers(HttpMethod.DELETE, "/**").access("#oauth2.hasScope('write')")
                .and()

                // Add headers required for CORS requests.
                .headers().addHeaderWriter((request, response) -> {
            response.addHeader("Access-Control-Allow-Origin", "*");

            if (request.getMethod().equals("OPTIONS")) {
                response.setHeader("Access-Control-Allow-Methods", request.getHeader("Access-Control-Request-Method"));
                response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
            }
        });    
    }
}

WSコントローラー:

@RestController
@RequestMapping(value = "/todos")
public class TodoController {

    @Autowired
    private TodoRepository todoRepository;

    @RequestMapping(method = RequestMethod.GET)
    public List<Todo> todos() {
        return todoRepository.findAll();
    }

   // other methods
}
17
Maksim

ユーザーが保護されたページにログインするか、[ログイン]ボタンをクリックすると、OAuthプロバイダーのURLにリダイレクトされ、ログインして、Webで認証されるように、Webクライアントプロジェクトを実装するにはどうすればよいですかすべてのユーザーロールを持ち、どのクライアントが使用されたかを知る必要があるクライアント

OAuthをSSOとして使用します。

オプション1、Spring Cloudを使用 https://spring.io/blog/2015/02/03/sso-with-oauth2-angular-js-and-spring-security-part-v

オプション2、SSOプロセスを手動で処理します。

webクライアントで、認証付与を使用してログインページをOAuthサーバーに構成します。

protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable(); // TODO. Enable this!!!
    http.authorizeRequests()
    .and()
    .formLogin()
    .loginPage("http://oauth.web.com/oauth/authorize?response_type=code&client_id=webclient&redirect_uri=http://web.com") // manually defining page to login
    //.failureUrl("/login?error") // manually defining page for login error
    .usernameParameter("email")
    .permitAll()   
    .and()
    .logout()
    //.logoutUrl("/logout")
    .logoutSuccessUrl("/")
    .permitAll();
}

認証と認証のプロセスが完了すると、認証コードhttp://web.com/?code=jYWioIでWebクライアントにリダイレクトされます。 Webクライアントは、このコードをoauthサーバー上のトークンアクセスと交換する必要があります。oauthサーバー上で、ユーザー情報を取得するためのエンドポイントを作成します

@RestController
public class UserRestService {

  @RequestMapping("/user")
  public Principal user(Principal user) {
    // you can also return User object with it's roles
    // {"details":...,"principal":{"username":"user",...},"name":"user"}
    return user;
  }

}

次に、Webクライアントは、上記のRESTエンドポイントへのトークンアクセスを使用してリクエストを送信することでユーザーの詳細情報にアクセスし、応答に基づいてユーザーを認証できます。

アクセストークンを管理し、常にリソースサーバーへの呼び出しに含める必要がありますか、それとも何らかの方法で自動的に行うことができますか?

すべてのリクエストにはトークンアクセスが含まれている必要があります。自動的に実行したい場合、springはOauth 2 client http://projects.spring.io/spring-security-oauth/docs/oauth2.html ==

私が開発するモバイルアプリにセキュリティを実装するための最良の方法は何ですか。この認証にはパスワードグランドを使用する必要があります。アプリは自分で作成し、ユーザー名とパスワードをネイティブ画面に表示してから、SSLを介した基本認証としてサーバーに送信するためです。

ネイティブ画面を使用しているため、パスワードの付与で十分ですが、更新トークンを保存できます。これにより、認証プロセスを繰り返さずにトークンアクセスをリクエストできます。

Spring Security OAuthとユーザーの詳細を返すことについての話を見ることができるサンプルはありますか。

上記のサンプルコードを参照するか、これをご覧ください https://spring.io/blog/2015/02/03/sso-with-oauth2-angular-js-and-spring-security-part-v

8
MangEngkus