简体   繁体   English

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

[英]User authentication in oauth2 with Spring Boot

I have implemented OAuth 2.0 with spring boot. 我已经用Spring Boot实现了OAuth 2.0。 I am storing the hashed password (including a salt) into my database. 我将哈希密码(包括盐)存储到数据库中。

I'm new to Spring and can't figure out how could I authenticate a username/password. 我是Spring的新手,无法弄清楚如何验证用户名/密码。

This is my UserDAO class: 这是我的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);
}
}

And this is the security configurer from the Application class: 这是来自Application类的安全配置器:

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

Questions: 问题:

  1. How to retrieve the password entered by the user? 如何找回用户输入的密码? The implementation of UserDetailsService gives me the username entered by the user. UserDetailsS​​ervice的实现为我提供了用户输入的用户名。
  2. Is there an in-built method to handle this "hashed password+salt" authentication? 是否有内置方法来处理这种“散列密码+盐”身份验证? Am I getting it all wrong? 我弄错了吗?

Retrieving the principal, providing a salt and encoding respectively matching the password are all decoupled from each other in Spring Security. 在Spring Security中,检索主体,提供分别与密码匹配的salt和编码都相互分离。 Your UserDetails service is basically a correct way of fetching the principal. 您的UserDetails服务基本上是获取主体的正确方法。

You have to provide a PasswordEncoder, the default in Spring Boot is BCrypt, which doesn't match your hash. 您必须提供一个PasswordEncoder,Spring Boot中的默认值为BCrypt,它与您的哈希不匹配。 The old password encoder (ShaPasswordEncoder and such) as well as the salt sources are no longer picked up by Spring Boot and Spring Security. Spring Boot和Spring Security不再使用旧的密码编码器(ShaPasswordEncoder等)以及盐源。 You have to define them manually. 您必须手动定义它们。

This configuration should basically do what you want, given you have a UserDetails that contains a salt column. 假设您有一个包含Salt列的UserDetails ,则此配置基本上应该可以完成您想要的操作。 If you don't know how to do that, here's the complete implementation ShaPasswordEncoderConfig.java from my upcoming book. 如果您不知道该怎么做,这是我即将出版的书中的完整实现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);
    }
}

To make this work, you have to role your own UserDetails implementation and not use that one from Spring Security itself, since it has no attribute to store the salt. 为了使这项工作有效,您必须扮演自己的UserDetails实现角色,而不能使用Spring Security本身的实现,因为它没有存储盐的属性。

The SaltSource I used in the example is a lambda that that casts a UserDetails to my implementation and grabs the salt. 我在示例中使用的SaltSource是一个lambda,它将一个UserDetails强制转换为我的实现并捕获了盐。 You can also use ReflectionSaltSource which is one implementation that gets the salt via reflection. 您还可以使用ReflectionSaltSource ,这是一种通过反射获取盐分的实现。

Edit 编辑

UserDetails is easy to implement, see: UserDetails易于实现,请参见:

You could use an implementation like this: 您可以使用以下实现:

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;
    }   
}

I ended up implementing my own PasswordEncoder interface and using a local static variable to pass it the salt through db. 我最终实现了自己的PasswordEncoder接口,并使用一个本地静态变量将它通过db传递给了salt。

The implementation is experimental, based on my observation of flow of control: 根据我对控制流的观察,该实现是实验性的:

  1. client-secret validation in the PasswordEncoder PasswordEncoder中的客户端秘密验证
  2. If step-1 cleared, loadUserByUsername in userDAO. 如果清除了步骤1,请在userDAO中加载loadUserByUsername
  3. Password validation in PasswordEncoder PasswordEncoder中的密码验证

public class PasswordEncoderImpl implements PasswordEncoder {

    public static Constants constants = new Constants();

    @Override
    public String encode(CharSequence rawPassword) {
        return DigestUtils.sha256Hex(rawPassword.toString());
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        String salt = constants.getSalt();

    /**
     * If salt is null, it means the value in constants object is empty now.
     * => client-secret validation is going on
     * => we just need the comparision of plain-text string.
     */
        if(salt != null) {
//    case of password authentication
            return DigestUtils.sha256Hex(salt + rawPassword).equalsIgnoreCase(encodedPassword);
        } else {
        //case of client-secret authentication
            return rawPassword.equals(encodedPassword);
        }
    }
}

In my UserDAO, where I get the user-details from the persistence, I would store salt into the object constants before return the user: 在我的UserDAO中,从持久性中获取用户详细信息,在返回用户之前,我会将salt存储到对象常量中:

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

    System.out.println("Get user");
    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);

    constants.set(user.getSalt());

    return new User(
            user.getUsername(),
            user.getPasswordHash(),
            true, true, true, true,
            grantedAuthorities);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Spring引导oauth2管理httpbasic认证 - Spring boot oauth2 management httpbasic authentication Spring开机、zuul、oauth2认证问题 - Spring boot, zuul, oauth2 authentication problem 在 Spring 引导应用程序上启用 Spring JWT 身份验证和 OAuth2 身份验证 - Enable Spring JWT Authentication and OAuth2 Authentication on Spring Boot Application 带有本地用户数据库的 Spring Boot oauth2 - Spring Boot oauth2 with local user database OAuth2保护的具有本地用户数据库和身份验证的Spring Boot REST Api - OAuth2 protected Spring Boot REST Api with local user database and authentication Spring Boot Security OAuth2身份验证服务器和业务服务器的拆分 - Spring Boot Security OAuth2 Authentication server and business server splitting Spring Boot OAuth2具有基本身份验证和自定义UserDetailsS​​ervice - Spring Boot OAuth2 with basic authentication and custom UserDetailsService Spring引导基本身份验证和OAuth2在同一个项目中? - Spring boot Basic Authentication and OAuth2 in same project? 如何使用 Spring Boot feign 客户端进行 Oauth2 身份验证? - How to use Spring Boot feign client for Oauth2 authentication? 使用Spring Boot + OAuth2 + Gitlab身份验证无法重定向到应用程序 - No redirection to the app with Spring Boot + OAuth2 + Gitlab authentication
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM