将自定义UserDetailsService添加到Spring Security OAuth2应用程序

将自定义UserDetailsService添加到Spring Security OAuth2应用程序,spring,spring-security,spring-boot,spring-security-oauth2,spring-oauth2,Spring,Spring Security,Spring Boot,Spring Security Oauth2,Spring Oauth2,如何将下面的自定义userdetails服务添加到 默认用户和默认密码在authserver应用程序的文件中定义 但是,出于测试目的,我想添加以下自定义UserDetailsService: package demo; import java.util.List; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.Author

如何将下面的自定义
userdetails服务添加到

默认
用户
和默认
密码
authserver
应用程序的文件中定义

但是,出于测试目的,我想添加以下自定义
UserDetailsService

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;
    }
}
我尝试在
OAuth2AuthorizationConfig
中使用
@Autowire
用户连接到
授权服务器端点配置器
,如下所示:

@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 Security开发oauth服务器时,我遇到了类似的问题。我的情况略有不同,因为我想添加一个
UserDetailsService
来验证刷新令牌,但我认为我的解决方案也会对您有所帮助

与您一样,我第一次尝试使用
AuthorizationServerEndpointsConfigure
指定
UserDetailsService
,但这不起作用。我不确定这是一个bug还是出于设计,但是需要在
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);
}
当然,您还需要添加
@autowiredusers-userDetailsServiceWeb安全配置适配器中的某个位置

我想提及的其他事项:

  • 这可能是特定于版本的,我使用的是spring-security-oauth2.0.12
  • 我无法举出任何来源来解释为什么会这样,我甚至不确定我的解决方案是真正的解决方案还是黑客
  • 指南中提到的
    globalaauthenticationmanagerconfiguer
    几乎可以肯定是一个打字错误,我在Spring的源代码中找不到该字符串
    我遇到了相同的问题,最初的解决方案与Manan Mehta发布的相同。就在最近,spring security和spring oauth2的某些版本组合导致任何刷新令牌的尝试,导致HTTP 500错误,指出在我的日志中需要
    UserDetailsService

    相关堆栈跟踪如下所示:

    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
    的对象,而不是我的
    userdetailssservice
    。即使在我的
    websecurityConfigureAdapter
    中设置了
    UserDetailsService
    ,还有两个
    websecurityConfigureAdapter
    s。一个用于
    ResourceServerConfiguration
    ,另一个用于
    AuthorizationServerSecurityConfiguration
    ,这些配置从未获得我设置的
    UserDetailsService

    在通过Spring Security一路追踪来拼凑正在发生的事情时,我发现有一个“本地”
    AuthenticationManagerBuilder
    和一个“全局”
    AuthenticationManagerBuilder
    ,我们需要在全局版本上设置它,以便将此信息传递给这些其他构建器上下文

    因此,我提出的解决方案是以与其他上下文获取全局版本相同的方式获取“全局”版本。在我的
    websecurityConfigureAdapter
    中,我有以下内容:

    @Autowired
    public void setApplicationContext(ApplicationContext context) {
        super.setApplicationContext(context);
        AuthenticationManagerBuilder globalAuthBuilder = context
                .getBean(AuthenticationManagerBuilder.class);
        try {
            globalAuthBuilder.userDetailsService(userDetailsService);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    

    这起作用了。其他上下文现在有my
    UserDetailsService
    。我把这个留给将来偶然发现这个雷区的勇敢士兵。

    对于任何获得
    userdetails服务的人,在执行刷新令牌时需要服务
    错误,并且您确认您已经拥有
    userdetails服务
    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);
        }
    }
    
    这是我的尝试和错误,可能不适合你

    顺便说一句,如果您放弃“猜测”spring将选择哪个bean,您可以扩展
    AuthorizationServerConfigurerAdapter
    WebSecurityConfigureAdapter
    并自行配置所有内容,但我认为这会失去spring自动配置的功能。
    如果我只需要自定义一些配置,为什么我需要配置所有内容?

    我的要求是从oauth2电子邮件属性的后面获取一个数据库对象。我发现这个问题是因为我假设我需要创建一个自定义的用户详细信息服务。实际上,我需要实现OidcUser接口并将其挂接到该过程中

    起初我认为它是OAuth2UserService,但我已经设置了AWS Cognito身份验证提供程序,以便它是开放的id connect

    //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
    }
    
    //内部Web安全配置适配器
    http
    .oauth2Login()
    .userInfoEndpoint()
    .oidcUserService(新的CustomOidcUserServiceImpl());
    ...
    公共类CustomOidcUserServiceImpl实现OAuth2UserService{
    私有OidcUserService OIDCUSERVICE=新OIDCUSERVICE();
    @凌驾
    公共OidcUser loadUser(OidcUserRequest userRequest)引发OAuth2AuthenticationException{
    OidcUser OidcUser=oidcUserService.loadUser(userRequest);
    返回新的CustomUserPrincipal(oidcUser);
    }
    }
    ...
    公共类CustomUserPrincipal实现了OidcUser{
    私人用户;
    //将所有呼叫转接到包含的呼叫器
    }
    
    定制服务是任何定制逻辑都可以使用的地方。 我计划在我的
    CustomUserPrincipal
    上实现
    UserDetails
    接口,这样我就可以为live和tes提供不同的身份验证机制
    //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
    }