首页 文章

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

提问于
浏览
-1

我已经使用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();
}

问题:

  • 如何检索用户输入的密码? UserDetailsService的实现为我提供了用户输入的用户名 .

  • 是否有内置方法来处理此"hashed password+salt"身份验证?我弄错了吗?

2 回答

  • 2

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

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

    这个配置应该基本上做你想要的,因为你有一个包含盐列的 UserDetails . 如果您从我即将出版的书中完整实施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的实现 .

    Edit

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

    我最终实现了自己的 PasswordEncoder 接口,并使用本地静态变量将salt传递给db .

    根据我对控制流程的观察,实施是实验性的:

    PasswordEncoder中的

    • 客户端密钥验证

    • 如果清除了步骤1,则在userDAO中 loadUserByUsername .

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

    在我的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);
    }
    

相关问题