我正在尝试使用Spring Boot和依赖项创建Oauth身份验证/授权服务器 * spring-security-oauth2-autoconfigure * nimbus-jose-jwt
问题是我不想指定UserDetailsService,因为有关用户帐户的信息位于另一个不公开密码的服务中。该服务只有一个API,其中输入是用户/密码,输出是用户信息(如果用户存在/凭据正确)。
所以我的代码/配置与文档略有不同。
@EnableAuthorizationServer
@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
//injections
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.tokenStore(jwtTokenStore)
.accessTokenConverter(accessTokenConverter)
.authenticationmanager(authenticationmanager);
}
}
和
@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//injections
@Bean
@Override
public Authenticationmanager authenticationmanagerBean() throws Exception {
return super.authenticationmanagerBean();
}
@Override
protected void configure(AuthenticationmanagerBuilder authenticationmanagerBuilder) {
authenticationmanagerBuilder.authenticationProvider(travelerauthenticationProvider); //my custom // authentication provider that calls the other service for checking credentials
}
}
和
@Component
public class TravelerauthenticationProvider implements AuthenticationProvider {
private static final Logger LOGGER = LoggerFactory.getLogger(TravelerauthenticationProvider.class);
private OrderTravelerProfileclient travelerProfileclient;
public TravelerauthenticationProvider(OrderTravelerProfileclient travelerProfileclient) {
this.travelerProfileclient = travelerProfileclient;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (authentication.getName() == null || (authentication.getcredentials().toString().isEmpty())) {
return null;
}
var username = authentication.getName();
var password = authentication.getcredentials().toString();
try {
travelerProfileclient.authenticate(username,password);
} catch (Exception e) {
LOGGER.error("checking traveler {} credentials failed",username,e);
throw new BadCredentialsException("wrong traveler credentials");
}
var authorities = Set.of(new SimpleGrantedAuthority("traveler"));
var updatedAuthentication = new usernamePasswordauthenticationToken(username,password,authorities);
return updatedAuthentication;
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(usernamePasswordauthenticationToken.class);
}
}
与client_credentials和密码流相关的所有工作均正常,但是当我尝试使用refresh_token流时,它抱怨UserDetailsService is required
。在不定义UserDetailsService且仅中继自定义身份验证提供程序的情况下,如何解决该问题?
更新: 显然,refresh_token流具有对身份验证(凭据)的重新检查,该身份验证需要使用另一个身份验证提供程序来输入PreAuthenticatedAuthenticationToken.class类型。
所以我创建了一个新的身份验证提供程序,如下所示:
@Component
public class TravelerRefreshTokenBasedAuthenticationProvider implements AuthenticationProvider {
private static final Logger LOGGER = LoggerFactory.getLogger(TravelerRefreshTokenBasedAuthenticationProvider.class);
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
var currentAuthentication = (PreAuthenticatedAuthenticationToken) authentication;
//.....
return updatedAuthentication;
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(PreAuthenticatedAuthenticationToken.class);
}
}
并将我的安全配置更新为:
@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//injections
//this bean will be more configured by the method below and it will be used by spring boot
//for authenticating requests. Its kind of an equivalent to userDetailsService
@Bean
@Override
public Authenticationmanager authenticationmanagerBean() throws Exception {
return super.authenticationmanagerBean();
}
@Override
protected void configure(AuthenticationmanagerBuilder authenticationmanagerBuilder) {
authenticationmanagerBuilder.authenticationProvider(travelerUserPassBasedAuthenticationProvider);
authenticationmanagerBuilder.authenticationProvider(travelerRefreshTokenBasedAuthenticationProvider);
}
}
问题是spring无法在refresh_token流中识别我的身份验证提供程序,并尝试使用默认的身份验证提供程序。而且默认的方法是尝试使用不存在的UserDetailsService。
我还觉得我不需要创建另一个提供程序,并且可以重用前一个提供程序。因为检查哪个spring不能使用我的自定义提供程序是针对用户/ pass的检查;我在以前的身份验证提供程序中所做的。
总而言之,直到现在,我觉得我不得不引入我的自定义提供程序来使refresh_token流与密码流相比有所不同