如何将下面的自定义UserDetailsService
添加到此Spring OAuth2示例?
默认user
和默认password
在authserver
应用程序的application.properties
文件中定义。
但是,出于测试目的,我想将以下自定义UserDetailsService
添加到authserver
应用程序的demo
包中:
package demo;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.stereotype.Service;
@Service
class Users implements UserDetailsManager {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String password;
List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER");
if (username.equals("Samwise")) {
auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_HOBBIT");
password = "TheShire";
}
else if (username.equals("Frodo")){
auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_HOBBIT");
password = "MyRing";
}
else{throw new UsernameNotFoundException("Username was not found. ");}
return new org.springframework.security.core.userdetails.User(username, password, auth);
}
@Override
public void createUser(UserDetails user) {// TODO Auto-generated method stub
}
@Override
public void updateUser(UserDetails user) {// TODO Auto-generated method stub
}
@Override
public void deleteUser(String username) {// TODO Auto-generated method stub
}
@Override
public void changePassword(String oldPassword, String newPassword) {
// TODO Auto-generated method stub
}
@Override
public boolean userExists(String username) {
// TODO Auto-generated method stub
return false;
}
}
正如您所看到的,这个UserDetailsService
还不是autowired
,它故意使用不安全的密码,因为它只是为测试目的而设计的。
需要对GitHub示例应用程序进行哪些特定更改,以便用户可以使用password=TheShire
以username=Samwise
登录,或者使用password=MyRing
以username=Frodo
登录是否需要对AuthserverApplication.java
或其他地方进行更改?
建议:
Spring OAuth2开发人员指南规定使用GlobalAuthenticationManagerConfigurer
全局配置UserDetailsService
。然而,在谷歌上搜索这些名字并不会产生什么有用的结果。
此外,另一个使用内部spring-security INSTEAD OF OAuth的应用程序使用以下语法来连接UserDetailsService
,但我不确定如何将其语法调整为当前的OP:
@Order(Ordered.HIGHEST_PRECEDENCE)
@Configuration
protected static class AuthenticationSecurity extends GlobalAuthenticationConfigurerAdapter {
@Autowired
private Users users;
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(users);
}
}
我尝试在OAuth2AuthorizationConfig
中使用@Autowire
将Users
连接到AuthorizationServerEndpointsConfigurer
,如下所示:
@Autowired//THIS IS A TEST
private Users users;//THIS IS A TEST
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.accessTokenConverter(jwtAccessTokenConverter())
.userDetailsService(users)//DetailsService)//THIS LINE IS A TEST
;
}
但是Spring Boot日志表明没有找到用户Samwise
,这意味着UserDetailsService
没有成功连接。以下是Spring Boot日志的相关摘录:
2016-04-20 15:34:39.998 DEBUG 5535 --- [nio-9999-exec-9] o.s.s.a.dao.DaoAuthenticationProvider :
User 'Samwise' not found
2016-04-20 15:34:39.998 DEBUG 5535 --- [nio-9999-exec-9]
w.a.UsernamePasswordAuthenticationFilter :
Authentication request failed:
org.springframework.security.authentication.BadCredentialsException:
Bad credentials
我还能尝试什么
我在使用Spring Security开发oauth服务器时遇到了类似的问题。我的情况略有不同,因为我想添加UserDetailsService
来验证刷新令牌,但我认为我的解决方案也会对您有所帮助。
和您一样,我第一次尝试使用AuthorizationServerEndpointsConfigurer
指定UserDetailsService
,但这不起作用。我不确定这是一个错误还是设计错误,但需要在AuthenticationManager
中设置UserDetailsService
,以便各种oauth2类找到它
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
Users userDetailsService;
@Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// other stuff to configure your security
}
}
我认为,如果你从第73行开始更改以下内容,它可能对你有用:
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
当然,您还需要在WebSecurityConfigurerAdapter
中的某个位置添加@Autowired Users userDetailsService;
我想提到的其他事情:
- 这可能是特定于版本的,我使用的是spring-security-outh2 2.0.12
- 我无法引用任何来源来解释为什么会这样,我甚至不确定我的解决方案是真正的解决方案还是黑客
- 指南中提到的
GlobalAuthenticationManagerConfigurer
几乎可以肯定是一个拼写错误,我在Spring的源代码中找不到这个字符串
我的要求是从oauth2电子邮件属性的后面获取一个数据库对象。我发现了这个问题,因为我认为我需要创建一个自定义的用户详细信息服务。事实上,我需要实现OidcUser接口并连接到该过程中。
起初我以为它是OAuth2UserService,但我已经设置了我的AWS Cognito身份验证提供商,使其成为开放的id连接。。
//inside WebSecurityConfigurerAdapter
http
.oauth2Login()
.userInfoEndpoint()
.oidcUserService(new CustomOidcUserServiceImpl());
...
public class CustomOidcUserServiceImpl implements OAuth2UserService<OidcUserRequest, OidcUser> {
private OidcUserService oidcUserService = new OidcUserService();
@Override
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
OidcUser oidcUser = oidcUserService.loadUser(userRequest);
return new CustomUserPrincipal(oidcUser);
}
}
...
public class CustomUserPrincipal implements OidcUser {
private OidcUser oidcUser;
//forward all calls onto the included oidcUser
}
定制服务是任何定制逻辑都可以使用的地方。我计划在我的CustomUserPrincipal
上实现UserDetails
接口,这样我就可以拥有不同的实时和测试身份验证机制,以便于自动化ui测试。
我遇到了同样的问题,最初的解决方案与Manan Mehta发布的相同。就在最近,spring-security和spring-oauth2的某些版本组合导致任何刷新令牌的尝试,导致HTTP500错误,在我的日志中声明UserDetailsService is required
。
相关堆栈跟踪如下所示:
java.lang.IllegalStateException: UserDetailsService is required.
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$UserDetailsServiceDelegator.loadUserByUsername(WebSecurityConfigurerAdapter.java:463)
at org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper.loadUserDetails(UserDetailsByNameServiceWrapper.java:68)
at org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider.authenticate(PreAuthenticatedAuthenticationProvider.java:103)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174)
at org.springframework.security.oauth2.provider.token.DefaultTokenServices.refreshAccessToken(DefaultTokenServices.java:150)
您可以在底部看到DefaultTokenServices
正在尝试刷新令牌。然后,它调用AuthenticationManager
进行重新身份验证(以防用户撤销权限或用户被删除等),但这就是一切的症结所在。您可以在堆栈跟踪的顶部看到,UserDetailsServiceDelegator
是对loadUserByUsername
的调用,而不是我漂亮的UserDetailsService
。尽管在我的WebSecurityConfigurerAdapter
中,我设置了UserDetailsService
,但还有另外两个WebSecurityConfigurerAdapter
。一个用于ResourceServerConfiguration
,一个用于AuthorizationServerSecurityConfiguration
,这些配置永远不会得到我设置的UserDetailsService
。
在通过Spring Security进行跟踪以拼凑正在发生的事情时,我发现有一个"本地"AuthenticationManagerBuilder
和一个"全局"AuthenticationManagerBuilder
,我们需要在全局版本上设置它,以便将这些信息传递给其他构建器上下文。
因此,我提出的解决方案是以其他上下文获得全局版本的方式获得"全局"版本。在我的WebSecurityConfigurerAdapter
中,我有以下内容:
@Autowired
public void setApplicationContext(ApplicationContext context) {
super.setApplicationContext(context);
AuthenticationManagerBuilder globalAuthBuilder = context
.getBean(AuthenticationManagerBuilder.class);
try {
globalAuthBuilder.userDetailsService(userDetailsService);
} catch (Exception e) {
e.printStackTrace();
}
}
这起到了作用。其他上下文现在有我的UserDetailsService
。我把这件事留给将来偶然发现这个雷区的任何勇敢的士兵。
对于任何在刷新令牌时出现UserDetailsService is required
错误的人,您确认您已经有了UserDetailsService
bean。
试着添加这个:
@Configuration
public class GlobalSecurityConfig extends GlobalAuthenticationConfigurerAdapter {
private UserDetailsService userDetailsService;
public GlobalSecurityConfig(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
}
这是我的尝试和错误,也许对你不起作用。
顺便说一句,如果你放弃"猜测"哪个bean将在春天采摘,你可以扩展AuthorizationServerConfigurerAdapter
和WebSecurityConfigurerAdapter
,并自己配置所有东西,但我认为这已经失去了春天自动配置的能力。如果我只需要自定义一些配置,为什么我需要配置所有内容?