将自定义UserDetailsS​​ervice添加到Spring Security OAuth2应用程序

Cod*_*Med 7 spring spring-security spring-boot spring-security-oauth2 spring-oauth2

如何添加自定义UserDetailsService下面这个春天的OAuth2样

默认默认user值在应用程序passwordapplication.properties文件中定义authserver.

不过,我想以下自定义添加UserDetailsServicedemo包中的authserver应用程序用于测试目的:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,这UserDetailsService还没有autowired,它故意使用不安全的密码,因为它仅用于测试目的.

需要什么样的具体变化来进行GitHub的示例应用程序,使用户可以为登录username=Samwisepassword=TheShire,或username=Frodopassword=MyRing 是否需要对AuthserverApplication.java其他地方进行更改?


几点建议:


春天的OAuth2开发指南说,使用一个GlobalAuthenticationManagerConfigurer配置UserDetailsService全球.但是,使用Google搜索这些名称会产生不太有用的结果.

此外,使用内部弹簧安全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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我试着用@Autowire里面的OAuth2AuthorizationConfig连接UsersAuthorizationServerEndpointsConfigurer如下:

@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
        ;
}
Run Code Online (Sandbox Code Playgroud)

但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
Run Code Online (Sandbox Code Playgroud)

我还能尝试什么?

Jam*_*der 7

我的要求是从 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
}
Run Code Online (Sandbox Code Playgroud)

自定义服务是任何定制逻辑都可以使用的地方。我计划UserDetails在我的上实现接口CustomUserPrincipal,以便我可以为实时和测试提供不同的身份验证机制,以促进自动化 ui 测试。


小智 6

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

像您一样,我首先尝试指定UserDetailsServiceusing AuthorizationServerEndpointsConfigurer,但这不起作用。我不确定这是一个错误还是设计使然,但UserDetailsService需要在 中设置AuthenticationManager以便各种 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
  }

}
Run Code Online (Sandbox Code Playgroud)

我认为如果您从第 73 行开始更改以下内容,它可能对您有用:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.parentAuthenticationManager(authenticationManager)
        .userDetailsService(userDetailsService);
}
Run Code Online (Sandbox Code Playgroud)

您当然还需要在@Autowired Users userDetailsService;某处添加WebSecurityConfigurerAdapter

我想提的其他事情:

  1. 这可能是版本特定的,我在 spring-security-oauth2 2.0.12
  2. 我无法引用任何来源来解释为什么会这样,我什至不确定我的解决方案是真正的解决方案还是黑客。
  3. GlobalAuthenticationManagerConfigurer提到在引导几乎可以肯定是一个错字,我无法找到源代码串的任何地方对任何春。


Mic*_*ler 5

我遇到了同样的问题,最初与 Manan Mehta 发布的解决方案相同。就在最近,spring security 和 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)
Run Code Online (Sandbox Code Playgroud)

您可以在底部看到DefaultTokenServices正在尝试刷新令牌。然后它调用一个AuthenticationManager重新进行身份验证(以防用户撤销权限或用户被删除等),但这就是一切都无法解决的地方。你看到的堆栈跟踪的顶部UserDetailsServiceDelegator是什么得到调用loadUserByUsername,而不是我的美丽UserDetailsService。即使在我的内部WebSecurityConfigurerAdapter设置了UserDetailsService,还有另外两个WebSecurityConfigurerAdapters。一个用于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();
    }
}
Run Code Online (Sandbox Code Playgroud)

这奏效了。其他上下文现在有我的UserDetailsService. 我把它留在这里,留给将来偶然发现这个雷区的任何勇敢的士兵。