java - 具有多个客户端的中央身份验证服务器,使用资源所有者密码凭据 oauth 流程

标签 java spring spring-security oauth-2.0

我进行了以下设置:

  • 使用 Spring Boot 编写的中央身份验证服务器当前正在运行(我可以 curl 并接收访问 token 、jdbc token 存储等)
  • 同一开发者拥有多个应用程序,在不同域共享相同的客户群。 IE:app1 的 John Doe 与 app2 的 John Doe 相同。

我有一个现有的应用程序(上面的app1),它是jsf 2.2,配置了spring security用于登录目的。该应用程序现在可以独立运行,拥有自己的用户群。

这是我试图获得的流程: Resource Owner Password Credential OAuth Flow

所以我们想要:

  1. 用户转到 app1
  2. 用户在 app1 登录页面输入用户名和密码
  3. 用户点击“登录”
  4. Spring 中的某种配置将接受 loginByUsername 请求,从中央 oauth 服务器获取访问 token
  5. 我们现在拥有 app1 访问权限 - 用户可以拥有三个角色之一(管理员、用户、 super 用户)。
  6. 当他们转到(例如)app1/views/createEntry.xhtml 时,我们将确认我们当前拥有的访问 token 在身份验证服务器上仍然处于 Activity 状态。
  7. 从技术上讲,资源服务器是 app1 服务器上的资源(对吗?)

我对这个 oauth2.0 流程很陌生(而且真的是 spring),但我认为这就是我想要的流程。我如何使用 Spring Security 进行设置?我看到了一个名为 oauth2login() 的安全设置,我认为这是我们可能想要的,但我认为这是更多的授权代码流程。

我还没有找到使用密码流程的很好的示例。

我确实信任此过程中的每个应用程序,因此也信任密码流程。我们控制维护身份验证服务器和其他应用程序之间流量的网络。

编辑:由于要求和我们的客户群,SSO 不是一个选项。这些应用程序非常独特,没有任何意义,但用户应该能够使用这些凭据登录我们的任何应用程序。

编辑 2:抱歉进行第二次编辑。我想补充一点,我在 app1 上添加了一个资源配置,它实际上似乎有效 - 我已经保护了任何/views/* ,当我尝试访问它们时,我收到了预期的“需要完全身份验证”消息。

编辑 3:我认为我正在取得一些进展 -

首先,我创建了一个实现 AuthenticationProvider 的 spring 组件,然后覆盖了authenticate 方法,以便我创建了一个包含所有属性(客户端 ID、客户端 key 、授权类型、范围等)的 ResourceOwnerPasswordResourceDetails 对象,并调用授权服务器来获取 token 。看到授权服务器的日志刷新,我非常兴奋。

下一步我需要弄清楚如何生成 org.springframework.security.core.userdetails.User 的扩展,以便我可以存储用户的权限。

另外 - 我还不太清楚 token 是如何存储的。我知道身份验证服务器会生成 token 并存储在 jdbc 中,但是 token 在客户端的何处/如何存储?

最佳答案

对于那些好奇的人,以下是我如何在客户端 (app1) 上设置身份验证提供程序。我仍然对资源服务器有问题(我会问一个单独的问题),但这是我所做的:

自定义 validator :

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private AppUserDAO appUserDAO;

    private String accessTokenUri = "http://localhost:8080/oauth/token";
    private String clientId = "clientid";
    private String clientSecret = "clientsecret";

    public AccessTokenProvider userAccessTokenProvider() {
        ResourceOwnerPasswordAccessTokenProvider accessTokenProvider = new ResourceOwnerPasswordAccessTokenProvider();
        return accessTokenProvider;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        final String username = authentication.getName();
        final String password = authentication.getCredentials().toString();

        List<String> scopes = new ArrayList<String>();
        scopes.add("read");

        final ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();

        resource.setUsername(username);
        resource.setPassword(password);
        resource.setAccessTokenUri(accessTokenUri);
        resource.setClientId(clientId);
        resource.setClientSecret(clientSecret);
        resource.setGrantType("password");
        resource.setScope(scopes);

        // Generate an access token
        final OAuth2RestTemplate template = new OAuth2RestTemplate(resource, new DefaultOAuth2ClientContext(new DefaultAccessTokenRequest()));
        template.setAccessTokenProvider(userAccessTokenProvider());

        OAuth2AccessToken accessToken = null;

        try {
            accessToken = template.getAccessToken();
            System.out.println("Grabbed access token from " + accessTokenUri);
        }
        catch (OAuth2AccessDeniedException e) {
            if (e.getCause() instanceof ResourceAccessException) {
                final String errorMessage = String.format(
                        "While authenticating user '%s': " + "Unable to access accessTokenUri '%s'.", username,
                        accessTokenUri);
                throw new AuthenticationServiceException(errorMessage, e);
            }
            throw new BadCredentialsException(String.format("Access denied for user '%s'.", username), e);
        }
        catch (OAuth2Exception e) {
            throw new AuthenticationServiceException(
                    String.format("Unable to perform OAuth authentication for user '%s'.", username), e);
        }

        // Determine roles for user
        List<GrantedAuthority> grantList = ...

        // Create custom user for the principal
        User user = .....

        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user, null /*dont store password*/, grantList);

        return token;
    }

    @Override
    public  boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
}

安全配置:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomAuthenticationProvider authProvider;

    ....

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authProvider);
    }   
}

关于java - 具有多个客户端的中央身份验证服务器,使用资源所有者密码凭据 oauth 流程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53728022/

相关文章:

java - 如何使用 Google Cloud Storage Java SDK 获取存储桶中最后创建的文件?

grails - Grails文件上传未使用正确的内容类型

java - Spring 安全http 404错误

Java JUnit4 : Make simple assertEquals Test pass

java - Kotlin硬编码字符串

java - 无法配置 spring boot 安全 - 总是 403

spring - 如何使用多个相互调用的自定义过滤器?

java - 越界异常(字符串)

java - 使用 Struts 2 实现 Spring Security

java - Spring 与 Hibernate 基于注解