使用Spring Boot在oauth2中进行用户身份验证

时间:2017-05-03 09:28:41

标签: java spring authentication spring-boot oauth-2.0

我已经使用spring boot实现了OAuth 2.0。我将散列密码(包括盐)存储到我的数据库中。

我是Spring的新手,无法弄清楚如何验证用户名/密码。

这是我的UserDAO类:

@Service
public class UserDAO implements UserDetailsService{

@Autowired
private LoginDetailsManager loginDetailsManager;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

    System.out.println("Get user");
    LoginDetails user = loginDetailsManager.getByUsername(username);
    System.out.println(user.toString());
    if (user == null) {
        // Not found...
        throw new UsernameNotFoundException(
                "User " + username + " not found.");
    }

    GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_USER");
    List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
    grantedAuthorities.add(grantedAuthority);

    String password = user.getPasswordHash();
    String salt = user.getSalt();
    return new UserDetailsImpl(
            user.getUsername(),
            user.getPasswordHash(),
            salt,
            grantedAuthorities);
}
}

这是Application类的安全配置器:

@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
    security.allowFormAuthenticationForClients();
}

问题:

  1. 如何检索用户输入的密码? UserDetailsS​​ervice的实现为我提供了用户输入的用户名。
  2. 是否有内置方法来处理这种“哈希密码+盐”身份验证?我弄错了吗?

1 个答案:

答案 0 :(得分:2)

在Spring Security中,检索主体,提供分别匹配密码的salt和编码都是相互分离的。您的UserDetails服务基本上是获取主体的正确方法。

你必须提供一个PasswordEncoder,Spring Boot中的默认值是BCrypt,它与你的哈希值不匹配。 Spring Boot和Spring Security不再使用旧密码编码器(ShaPasswordEncoder等)以及salt源。你必须手动定义它们。

如果您的UserDetails包含salt列,则此配置应该基本上可以执行您想要的操作。如果您不知道如何做到这一点,请参阅我即将出版的书中的完整实施ShaPasswordEncoderConfig.java

@Configuration
public class ShaPasswordEncoderConfig 
    extends WebSecurityConfigurerAdapter {

    final UserDetailsService userDetailsService;

    public ShaPasswordEncoderConfig(final UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    public void configure(
        AuthenticationManagerBuilder auth
    ) {
        DaoAuthenticationProvider authProvider
            = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(
            userDetailsService);
        authProvider.setPasswordEncoder(
            new ShaPasswordEncoder(256));
        authProvider.setSaltSource(
            user -> ((UserWithSalt)user).getSalt());

        auth.authenticationProvider(authProvider);
    }
}

为了使这项工作,您必须为自己的UserDetails实现角色,而不是使用Spring Security本身的那个,因为它没有存储salt的属性。

我在示例中使用的SaltSource是一个lambda,它将UserDetails强制转换为我的实现并获取盐。您还可以使用ReflectionSaltSource这是一个通过反射获取salt的实现。

修改

UserDetails易于实施,请参阅:

你可以使用这样的实现:

class UserDetailsImpl implements UserDetails {

    private final String username;
    private final String hashedPassword;
    private final String salt;
    private final List<GrantedAuthority> grantedAuthorities;

    public UserDetailsImpl(String username, String hashedPassword, String salt, List<GrantedAuthority> grantedAuthorities) {
        this.username = username;
        this.hashedPassword = hashedPassword;
        this.salt = salt;
        this.grantedAuthorities = grantedAuthorities;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return grantedAuthorities;
    }

    @Override
    public String getPassword() {
        return hashedPassword;
    }

    @Override
    public String getUsername() {
        return username;
    }

    public String getSalt() {
        return salt;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }   
}