将自定义 UserDetailsService 添加到 Spring Security OAuth2 应用程序
Add custom UserDetailsService to Spring Security OAuth2 app
如何将下面的自定义 UserDetailsService
添加到 this Spring OAuth2 sample?
默认 user
和默认 password
在 authserver
应用程序的 application.properties
文件中定义。
但是,我想将以下自定义 UserDetailsService
添加到 the demo
package of the authserver
app 以进行测试:
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
,它故意使用不安全的密码,因为它仅用于测试目的。
需要对 the GitHub sample app so that a user can login as username=Samwise
with password=TheShire
, or as username=Frodo
with password=MyRing
? Do changes need to be made to AuthserverApplication.java
或其他地方进行哪些具体更改?
建议:
Spring OAuth2 Developer Guide 表示使用 GlobalAuthenticationManagerConfigurer
全局配置 UserDetailsService
。然而,用谷歌搜索这些名字产生的结果不是很有帮助。
此外,另一个使用内部 spring 安全性而不是 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 引导日志显示未找到用户 Samwise
,这意味着 UserDetailsService
未成功连接。以下是 Spring 引导日志的相关摘录:
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 安全性的 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-oauth2 2.0.12
- 我无法引用任何来源来说明为什么会这样,我什至不确定我的解决方案是真正的解决方案还是黑客。
- 指南中提到的
GlobalAuthenticationManagerConfigurer
几乎可以肯定是错字,我在 Spring 中的任何内容的源代码中都找不到该字符串。
我 运行 遇到了同样的问题,最初的解决方案与 Manan Mehta 发布的相同。就在最近,spring 安全和 spring oauth2 的某些版本组合导致任何刷新令牌的尝试导致 HTTP 500 错误,在我的日志中指出 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安全拼凑到底是怎么回事,发现有一个"local"AuthenticationManagerBuilder
和一个"global"AuthenticationManagerBuilder
我们需要在全局版本上设置它,以便将此信息传递给这些其他构建器上下文。
所以,我想出的解决方案是获取 "global" 版本,就像其他上下文获取全局版本一样。在我的 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);
}
}
这是我的尝试和错误,可能不适合你。
顺便说一下,如果你放弃 "guess" 哪个 bean 将由 spring 挑选,你可以扩展 AuthorizationServerConfigurerAdapter
和 WebSecurityConfigurerAdapter
并自己配置所有东西,但我认为它失去了 spring 自动配置的能力。
如果我只需要自定义一些配置,为什么我需要配置一切?
我的要求ui评论是从 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 测试。
如何将下面的自定义 UserDetailsService
添加到 this Spring OAuth2 sample?
默认 user
和默认 password
在 authserver
应用程序的 application.properties
文件中定义。
但是,我想将以下自定义 UserDetailsService
添加到 the demo
package of the authserver
app 以进行测试:
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
,它故意使用不安全的密码,因为它仅用于测试目的。
需要对 the GitHub sample app so that a user can login as username=Samwise
with password=TheShire
, or as username=Frodo
with password=MyRing
? Do changes need to be made to AuthserverApplication.java
或其他地方进行哪些具体更改?
建议:
Spring OAuth2 Developer Guide 表示使用 GlobalAuthenticationManagerConfigurer
全局配置 UserDetailsService
。然而,用谷歌搜索这些名字产生的结果不是很有帮助。
此外,另一个使用内部 spring 安全性而不是 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 引导日志显示未找到用户 Samwise
,这意味着 UserDetailsService
未成功连接。以下是 Spring 引导日志的相关摘录:
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 安全性的 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-oauth2 2.0.12
- 我无法引用任何来源来说明为什么会这样,我什至不确定我的解决方案是真正的解决方案还是黑客。
- 指南中提到的
GlobalAuthenticationManagerConfigurer
几乎可以肯定是错字,我在 Spring 中的任何内容的源代码中都找不到该字符串。
我 运行 遇到了同样的问题,最初的解决方案与 Manan Mehta 发布的相同。就在最近,spring 安全和 spring oauth2 的某些版本组合导致任何刷新令牌的尝试导致 HTTP 500 错误,在我的日志中指出 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安全拼凑到底是怎么回事,发现有一个"local"AuthenticationManagerBuilder
和一个"global"AuthenticationManagerBuilder
我们需要在全局版本上设置它,以便将此信息传递给这些其他构建器上下文。
所以,我想出的解决方案是获取 "global" 版本,就像其他上下文获取全局版本一样。在我的 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);
}
}
这是我的尝试和错误,可能不适合你。
顺便说一下,如果你放弃 "guess" 哪个 bean 将由 spring 挑选,你可以扩展 AuthorizationServerConfigurerAdapter
和 WebSecurityConfigurerAdapter
并自己配置所有东西,但我认为它失去了 spring 自动配置的能力。
如果我只需要自定义一些配置,为什么我需要配置一切?
我的要求ui评论是从 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 测试。