Spring Boot中基于Basic Auth实现OAuth2密码模式登录问题
问题描述
我有一个集成外部Keycloak的Spring Boot应用,配置了多套Http Security规则:
- API接口采用Bearer token认证
- Swagger使用授权码流程(需用户交互)
- 部分接口计划通过OAuth2密码授权类型替代原Basic Auth认证(原Basic Auth配置未生效)
现有配置
application.properties中的OAuth2配置
spring.security.oauth2.client.registration.keycloak2.client-id=${KEYCLOAK_RESOURCE} spring.security.oauth2.client.registration.keycloak2.client-secret=${KEYCLOAK_RESOURCE_CLIENT_SECRET} spring.security.oauth2.client.registration.keycloak2.authorization-grant-type=password spring.security.oauth2.client.registration.keycloak2.scope=openid spring.security.oauth2.client.provider.keycloak2.issuer-uri=${keycloak.auth-server-url}/realms/${keycloak.realm}
Basic Auth端点的配置代码
@Configuration @Order(2) public static class ProcessConfigurationAdapter extends WebSecurityConfigurerAdapter { public static class OAuth2PasswordAuthenticationProvider implements AuthenticationProvider { private final OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> accessTokenResponseClient; private final OAuth2UserService<OAuth2UserRequest, OAuth2User> userService; private final ClientRegistrationRepository clientRegistrationRepository; private GrantedAuthoritiesMapper authoritiesMapper = ((authorities) -> authorities); public OAuth2PasswordAuthenticationProvider( OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> accessTokenResponseClient, OAuth2UserService<OAuth2UserRequest, OAuth2User> userService, ClientRegistrationRepository clientRegistrationRepository) { super(); this.accessTokenResponseClient = accessTokenResponseClient; this.userService = userService; this.clientRegistrationRepository = clientRegistrationRepository; } @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { if (!(authentication instanceof UsernamePasswordAuthenticationToken)) { return null; } final UsernamePasswordAuthenticationToken usernamePassword = (UsernamePasswordAuthenticationToken) authentication; final String username = (String) usernamePassword.getPrincipal(); final String password = (String) usernamePassword.getCredentials(); final String registrationId = "keycloak2"; final ClientRegistration keycloak2 = clientRegistrationRepository.findByRegistrationId(registrationId); final OAuth2PasswordGrantRequest request = new OAuth2PasswordGrantRequest(keycloak2, username, password); final OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponseClient.getTokenResponse(request); final OAuth2User oauth2User = this.userService.loadUser(new OAuth2UserRequest( keycloak2, accessTokenResponse.getAccessToken(), accessTokenResponse.getAdditionalParameters())); final Collection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper .mapAuthorities(oauth2User.getAuthorities()); final OAuth2AuthenticationToken authenticationResult = new OAuth2AuthenticationToken(oauth2User, mappedAuthorities, registrationId); return authenticationResult; } @Override public boolean supports(Class authentication) { return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); } } @Override protected void configure(HttpSecurity http) throws Exception { http.antMatcher("/api/v1/process/**") .authorizeRequests() .anyRequest() .authenticated() .and() .oauth2Client() .and() .httpBasic(); http.csrf().disable(); } @Bean public OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> accessTokenResponseClient() { return new DefaultPasswordTokenResponseClient(); } @Bean public OAuth2PasswordAuthenticationProvider oAuth2PasswordAuthenticationProvider( OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> accessTokenResponseClient, OAuth2UserService<OAuth2UserRequest, OAuth2User> userService, ClientRegistrationRepository clientRegistrationRepository) { // Here I'm missing userService return new OAuth2PasswordAuthenticationProvider(accessTokenResponseClient, userService, clientRegistrationRepository); } }
遇到的错误
Parameter 1 of method oAuth2PasswordAuthenticationProvider in com.example.config.SecurityConfig$ProcessConfigurationAdapter required a bean of type 'org.springframework.security.oauth2.client.userinfo.OAuth2UserService' that could not be found.
原本认为OAuth2UserService会根据配置自动装配,但未成功,请问如何获取这个Bean?
解决方案
Spring Security不会自动为密码授权类型创建OAuth2UserService的Bean,需要手动定义,以下两种方式可选:
方法1:使用默认实现
直接注入Spring提供的DefaultOAuth2UserService,它能处理标准的OAuth2用户信息请求:
@Bean public OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() { return new DefaultOAuth2UserService(); }
方法2:自定义实现(适配Keycloak特性)
如果需要解析Keycloak返回的额外属性(如Realm角色、Client角色),可以扩展默认实现:
@Bean public OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() { DefaultOAuth2UserService delegate = new DefaultOAuth2UserService(); return request -> { OAuth2User oauth2User = delegate.loadUser(request); // 此处添加自定义逻辑,比如解析Keycloak的角色信息 return oauth2User; }; }
额外配置要点
- 需在配置类中注册自定义认证提供者:
在ProcessConfigurationAdapter中添加如下方法:@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(oAuth2PasswordAuthenticationProvider( accessTokenResponseClient(), oauth2UserService(), clientRegistrationRepository() )); } - 确保Keycloak客户端配置中开启了密码授权类型,且客户端访问类型设置为
confidential(因配置了client-secret)。
内容的提问来源于stack exchange,提问作者user1745656
相关产品推荐
相关产品推荐

