Spring 安全 OAuth2 的 Web 和移动客户端

Web and Mobile Clients for Spring Security OAuth2

我正在努力研究 OAuth2 和 Spring Security OAuth,尤其是 OAuth Provider 服务。我正在尝试执行以下操作:

  1. OAuth 提供商
  2. 资源服务器(RESTful 应使用 OAuth 提供程序保护的网络服务 (1))
  3. Web 客户端(使用 Spring 安全保护的 Web 客户端应用程序,但应使用 OAuth Provider (1) 对用户进行身份验证
  4. 本机移动客户端(Android 和 iOS)也应使用 OAuth 提供程序 (1) 进行身份验证

所有这些模块都是相互独立的,即在不同的项目中分离,将托管在不同的域中,例如 (1) http://oauth.web.com, (2) http://rest.web.com, (3) http://web.com

我的两个问题是:

一个。我如何实现 Web 客户端项目,以便当用户在受保护页面上登录或单击“登录”按钮时,被重定向到 OAuth Provider url、登录,并在 Web 客户端上以所有用户角色进行身份验证并且还需要知道使用了哪个客户端。 @EnableResourceServer(与资源服务器的实现方式相同;请参阅下面的代码)在此项目中获取用户的详细信息?我是否必须管理访问令牌并始终将其包含在对资源服务器的调用中,或者它可以以某种方式自动完成?

乙。在我将要开发的移动应用程序上实现安全性的最佳方式是什么。我是否应该为此身份验证使用密码 grand,因为应用程序将由我构建,我将在本机屏幕中显示用户名和密码,然后作为通过 SSL 的基本身份验证发送到服务器?有没有我可以看一下与 Spring Security OAuth 和 return 用户详细信息的对话的示例。

这是我的 OAuth 项目 (1) 和资源项目 (2) 的实现:

1。 OAuth 提供商

OAuth2 服务器配置(大部分代码取自 HERE

@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()");
    }
}

网络安全配置

@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();
    }
}

用户详细信息服务(自定义用户详细信息服务)

@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
}

How do I implement a Web Client project so that when user is logging in on the protected page or clicks on the Login button, be redirected to OAuth Provider url, login, and be authenticated on the Web Client with all user roles and also as well need to know which client was used

您想将 OAuth 用作 SSO。

选项 1,使用 spring 云 https://spring.io/blog/2015/02/03/sso-with-oauth2-angular-js-and-spring-security-part-v

方案二,手动处理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 端点发送带有令牌访问权限的请求来访问用户详细信息,并根据响应对用户进行身份验证。

Do I have to manage Access Token and always include it in the call to the Resource Server or it can be done somehow automatically?

每个请求都必须包含令牌访问。如果您想自动完成,spring 已提供 Oauth 2 客户端 http://projects.spring.io/spring-security-oauth/docs/oauth2.html

What is the best way to implement security on the mobile apps that I'll be developing. Should I be using password grand for this authentication, since the apps will be build by me where I'll have a user name and password be in the native screen and then be send to the server as a basic authentication over SSL?

由于您使用的是原生屏幕,授予密码就足够了,但您可以存储刷新令牌,这将使您无需重复身份验证过程即可请求令牌访问。

Are there any samples that I can take a look at that talk to Spring Security OAuth and return user details.

请参阅上面的示例代码或查看此 https://spring.io/blog/2015/02/03/sso-with-oauth2-angular-js-and-spring-security-part-v