当前位置: 首页 > 面试题库 >

Spring Security中具有密码授予权限的oAuth2客户端

方季同
2023-03-14
问题内容

我正在使用一组受oAuth2保护的服务。当前它是这样工作的:客户端使用其用户名和密码登录。我用这些交换令牌。我将令牌保存在会话中,并在每次调用服务时提交。它可以工作,但是问题是我完全手动执行此操作,而没有使用很多Spring
Security oAuth2支持。外观如下:

<!-- Configure Authentication mechanism -->
<authentication-manager alias="authenticationManager">
    <authentication-provider ref="oAuth2AuthenticationProvider"/>
</authentication-manager>


<beans:bean id="oAuth2AuthenticationProvider" class="my.custom.Oauth2AuthenticationProvider">
    <beans:constructor-arg name="accessTokenUri" value="http://x.x.x.x/oauth/token"/>
    <beans:constructor-arg name="clientId" value="myClientId"/>
    <beans:constructor-arg name="clientSecret" value="myClientSecret"/>
    <beans:constructor-arg name="scope">
        <beans:list>
            <beans:value>myScope</beans:value>
        </beans:list>
    </beans:constructor-arg>
</beans:bean>

<beans:bean id="resourceOwnerPasswordAccessTokenProvider" class="org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider"/>

如您所见,我自己创建了身份验证提供程序。它正在接受该标准,UsernamePasswordAuthenticationToken但正在产生自己的扩展,该扩展也保留了实际内容OAuth2AccessToken,因此将其保留在安全性上下文中。

public class Oauth2AuthenticationProvider implements AuthenticationProvider {

@Autowired
private ResourceOwnerPasswordAccessTokenProvider provider;

private String accessTokenUri;
private String clientId;
private String clientSecret;
private List<String> scope;

public Oauth2AuthenticationProvider(String accessTokenUri, String clientId, String clientSecret, List<String> scope) {
    this.accessTokenUri = accessTokenUri;
    this.clientId = clientId;
    this.clientSecret = clientSecret;
    this.scope = scope;
}

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    String username = authentication.getName();
    String password = authentication.getCredentials().toString();
    OAuth2AccessToken token = obtainToken(username, password);
    return handleLogonSuccess(authentication, token);
}

private OAuth2AccessToken obtainToken(String username, String password) {
    ResourceOwnerPasswordResourceDetails passwordResourceDetails = new ResourceOwnerPasswordResourceDetails();
    passwordResourceDetails.setUsername(username);
    passwordResourceDetails.setPassword(password);
    passwordResourceDetails.setClientId(clientId);
    passwordResourceDetails.setClientSecret(clientSecret);
    passwordResourceDetails.setScope(scope);
    passwordResourceDetails.setAccessTokenUri(accessTokenUri);
    DefaultAccessTokenRequest defaultAccessTokenRequest = new DefaultAccessTokenRequest();
    OAuth2AccessToken token;
    try {
        token = provider.obtainAccessToken(passwordResourceDetails, defaultAccessTokenRequest);
    } catch (OAuth2AccessDeniedException accessDeniedException) {
        throw new BadCredentialsException("Invalid credentials", accessDeniedException);
    }

    return token;
}

public OAuth2AccessToken refreshToken(OAuth2AuthenticationToken authentication) {
    OAuth2AccessToken token = authentication.getoAuth2AccessToken();
    OAuth2RefreshToken refreshToken = token.getRefreshToken();
    BaseOAuth2ProtectedResourceDetails resourceDetails = new BaseOAuth2ProtectedResourceDetails();
    resourceDetails.setClientId(clientId);
    resourceDetails.setClientSecret(clientSecret);
    resourceDetails.setScope(scope);
    resourceDetails.setAccessTokenUri(accessTokenUri);
    OAuth2AccessToken newToken = provider.refreshAccessToken(resourceDetails, refreshToken, new DefaultAccessTokenRequest());
    authentication.setoAuth2AccessToken(newToken);
    return newToken;
}

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

private Authentication handleLogonSuccess(Authentication authentication, OAuth2AccessToken token) {

    MyCustomOAuth2AuthenticationToken successAuthenticationToken = new MyCustomOAuth2AuthenticationToken(user, authentication.getCredentials(), calculateAuthorities(authentication), token);

    return successAuthenticationToken;
}

public list<GrantedAuthority> calculateAuthorities(Authentication authentication) {
        //my custom logic that assigns the correct role. e.g. ROLE_USER
}

}

如您所见,它基本上可以确保令牌保留在安全范围内,从这里我可以在每次调用后端服务之前简单地手动将其提取出来。同样,我将在每次调用前检查令牌的新鲜度。这很好用,但是我确定我可以在XML中使用Spring的oauth命名空间(我没有在使用Java
config),以多配置少代码的方式实现相同目的。我发现的大多数示例都包括我不关心并且只会让我感到困惑的oAuth服务器实现。

谁能帮我这个忙吗?


问题答案:

通过浏览Spring Security OAuth来源以及在线找到的其他解决方案的一些片段,我已经混搭了一个类似的解决方案。我正在使用Java
Config,但也许可以帮助您映射到xml配置,如下所示:

@Configuration
@EnableOAuth2Client
public class RestClientConfig {

    @Value("${http.client.maxPoolSize}")
    private Integer maxPoolSize;

    @Value("${oauth2.resourceId}")
    private String resourceId;

    @Value("${oauth2.clientId}")
    private String clientId;

    @Value("${oauth2.clientSecret}")
    private String clientSecret;

    @Value("${oauth2.accessTokenUri}")
    private String accessTokenUri;


    @Autowired
    private OAuth2ClientContext oauth2ClientContext;


    @Bean
    public ClientHttpRequestFactory httpRequestFactory() {
        return new HttpComponentsClientHttpRequestFactory(httpClient());
    }

    @Bean
    public HttpClient httpClient() {
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
        connectionManager.setMaxTotal(maxPoolSize);
        // This client is for internal connections so only one route is expected
        connectionManager.setDefaultMaxPerRoute(maxPoolSize);
        return HttpClientBuilder.create().setConnectionManager(connectionManager).build();
    }

    @Bean
    public OAuth2ProtectedResourceDetails oauth2ProtectedResourceDetails() {
        ResourceOwnerPasswordResourceDetails details = new ResourceOwnerPasswordResourceDetails();
        details.setId(resourceId);
        details.setClientId(clientId);
        details.setClientSecret(clientSecret);
        details.setAccessTokenUri(accessTokenUri);
        return details;
    }

    @Bean
    public AccessTokenProvider accessTokenProvider() {
        ResourceOwnerPasswordAccessTokenProvider tokenProvider = new ResourceOwnerPasswordAccessTokenProvider();
        tokenProvider.setRequestFactory(httpRequestFactory());
        return new AccessTokenProviderChain(
                  Arrays.<AccessTokenProvider> asList(tokenProvider)
                );
    }

    @Bean
    public OAuth2RestTemplate restTemplate() {
        OAuth2RestTemplate template = new OAuth2RestTemplate(oauth2ProtectedResourceDetails(), oauth2ClientContext);
        template.setRequestFactory(httpRequestFactory());
        template.setAccessTokenProvider(accessTokenProvider());
        return template;
    }   
}

我发现的重要一点是,即使对于单个提供程序,也需要使用AccessTokenProviderChain,否则自动令牌刷新(在身份验证之后)将无法工作。

要在第一个请求上设置用户凭据,您需要:

@Autowired
private OAuth2RestTemplate restTemplate;

restTemplate.getOAuth2ClientContext().getAccessTokenRequest().set("username", username);
restTemplate.getOAuth2ClientContext().getAccessTokenRequest().set("password", password);

然后,您可以使用RestTemplate方法正常发出请求,例如:

    String url = "http://localhost:{port}/api/users/search/findByUsername?username={username}";

    ResponseEntity<User> responseEntity = restTemplate.getForEntity(
            url, User.class, 8081, username);

如果要跟踪网络上的请求,可以将apache http客户端上的日志级别设置为DEBUG,例如使用Spring Boot:

logging.level.org.apache.http = DEBUG



 类似资料:
  • null 用户在登录表单中输入凭据->应用程序1将使用用户凭据从应用程序2获取令牌,其具有密码授予类型->使用令牌访问应用程序1的资源。 问题是Spring Security 5是否支持客户端的密码授予类型?我在Spring Security 5实现中找到了所有rest授予类型,但没有找到密码。

  • 我一直在广泛阅读有关OAuth和OpenID Connect的内容,但此问题专门涉及OAuth2资源所有者密码授予(又名OAuth2资源所有者凭据授予,又名OAuth2密码授予) 某些资源(例如Justin Richer的《OAuth2 in Action》一书)说不要使用OAuth2资源所有者密码授予进行身份验证-请参阅书中的第6.1.3节。 以下其他好资源都说我们可以使用OAuth2资源所有者

  • 在Ora11g中,我使用以下方式向用户授予所有权限。 但在ORA12C中,当我执行grant privileges时,我得到以下错误。 从命令-授予创建会话,向xx授予任何权限错误报告-ORA-00604:递归SQL级别1 ORA-20997发生错误:“授予任何权限”授予不允许的ORA-06512:在“RDSADMIN.RDSADMIN”处,第79行ORA-06512:在第2 00604行。000

  • 问题内容: 根据规范,不要求使用授权码补助金的令牌的请求作为要被认证,只要是包含在请求和所述是相同的一个用于生成所述代码。但是,使用Spring Security OAuth 2.0实现,即使从未为客户端分配秘密,端点上始终总是需要基本身份验证。 似乎由于接口中的方法,似乎支持允许客户端没有秘密。为了使没有秘密的客户端能够在上进行身份验证,我该怎么做? 4.1.3。访问令牌请求 客户端通过 按照附

  • 我是这方面的新手,读了很多之后,我觉得我不太明白如何在Keycloak中实现授权代码流。我的疑惑: > < li> 在创建了支持此流程的客户端后,如何执行凭据验证?默认情况下,如果我不做任何配置,我会得到一个登录表单。如果我在浏览器中打开这个html,并填写用户和密码字段,当我按下按钮时,它会将我发送到一个类型为.../realm/{ REAL _ NAME }/log in-actions/au

  • 本文向大家介绍oauth 授权码授予,包括了oauth 授权码授予的使用技巧和注意事项,需要的朋友参考一下 示例 第1步 第2步 资源