编码密码看起来不像BCrypt

时间:2018-03-31 00:11:54

标签: java spring spring-boot spring-data-jpa

我正在使用Spring Boot,Spring Security,OAuth2和JWT来验证我的应用程序,但我不断收到这个令人讨厌的错误,我不知道出了什么问题。我的CustomDetailsService课程:

@Service
public class CustomDetailsService implements UserDetailsService {

    private static final Logger logger = LoggerFactory.getLogger(CustomDetailsService.class);

    @Autowired
    private UserBO userBo;

    @Autowired
    private RoleBO roleBo;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        AppUsers appUsers = null;
        try {
            appUsers = this.userBo.loadUserByUsername(username);
            System.out.println("========|||=========== "+appUsers.getUsername());
        }catch(IndexOutOfBoundsException e){
            throw new UsernameNotFoundException("Wrong username");
        }catch(DataAccessException e){
            e.printStackTrace();
            throw new UsernameNotFoundException("Database Error");
        }catch(Exception e){
            e.printStackTrace();
            throw new UsernameNotFoundException("Unknown Error");
        }

        if(appUsers == null){
            throw new UsernameNotFoundException("Bad credentials");
        }
        logger.info("Username: "+appUsers.getUsername());
        return buildUserFromUserEntity(appUsers);
    }

    private User buildUserFromUserEntity(AppUsers authUsers) {
        Set<UserRole> userRoles = authUsers.getUserRoles();

        boolean enabled = true;
        boolean accountNotExpired = true;
        boolean credentialsNotExpired = true;
        boolean accountNotLocked = true;

        if (authUsers.getAccountIsActive()) {
            try {
                if(authUsers.getAccountExpired()){
                    accountNotExpired = true;
                } else if (authUsers.getAccountIsLocked()) {
                    accountNotLocked = true;
                } else {
                    if (containsRole((userRoles), roleBo.findRoleByName("FLEX_ADMIN"))){
                        accountNotLocked = false;
                    }
                }
            }catch(Exception e){
                enabled = false;
                e.printStackTrace();
            }
        }else {
            accountNotExpired = false;
        }
        // convert model user to spring security user
        String username = authUsers.getUsername();
        String password = authUsers.getPassword();

        List<GrantedAuthority> authorities = buildUserAuthority(userRoles);

        User springUser = new User(username, password,enabled, accountNotExpired, credentialsNotExpired, accountNotLocked, authorities);
        return springUser;
    }
}

OAuth2Config

@Configuration
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Bean
    public JwtAccessTokenConverter tokenConverter() {
        JwtAccessTokenConverter tokenConverter = new JwtAccessTokenConverter();
        tokenConverter.setSigningKey(PRIVATE_KEY);
        tokenConverter.setVerifierKey(PUBLIC_KEY);
        return tokenConverter;
    }

    @Bean
    public JwtTokenStore tokenStore() {
        return new JwtTokenStore(tokenConverter());
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpointsConfigurer) throws Exception {
        endpointsConfigurer.authenticationManager(authenticationManager)
                .tokenStore(tokenStore())
                .accessTokenConverter(tokenConverter());
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer securityConfigurer) throws Exception {
        securityConfigurer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient(CLIENT_ID)
                .secret(CLIENT_SECRET)
                .scopes("read","write")
                .authorizedGrantTypes("password","refresh_token")
                .accessTokenValiditySeconds(20000)
                .refreshTokenValiditySeconds(20000);
    }
}

SecurityConfig

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    CustomDetailsService customDetailsService;

    @Bean
    public PasswordEncoder encoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    @Autowired
    protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder.userDetailsService(customDetailsService).passwordEncoder(encoder());
        System.out.println("Done...finito");
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.NEVER);
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManagerBean();
    }
}

除了以下内容外没有错误消息:

Hibernate: select appusers0_.id as id1_2_, appusers0_.account_expired as account_2_2_, appusers0_.account_is_active as account_3_2_, appusers0_.account_is_locked as account_4_2_, appusers0_.bank_acct as bank_acc5_2_, appusers0_.branch_id as branch_i6_2_, appusers0_.bvn as bvn7_2_, appusers0_.create_date as create_d8_2_, appusers0_.created_by as created_9_2_, appusers0_.email as email10_2_, appusers0_.email_verified_code as email_v11_2_, appusers0_.gender as gender12_2_, appusers0_.gravatar_url as gravata13_2_, appusers0_.is_deleted as is_dele14_2_, appusers0_.lastname as lastnam15_2_, appusers0_.middlename as middlen16_2_, appusers0_.modified_by as modifie17_2_, appusers0_.modified_date as modifie18_2_, appusers0_.orgnization_id as orgniza19_2_, appusers0_.password as passwor20_2_, appusers0_.phone_no as phone_n21_2_, appusers0_.surname as surname22_2_, appusers0_.token_expired as token_e23_2_, appusers0_.username as usernam24_2_ from users appusers0_ where appusers0_.username=?
Tinubu
2018-03-31 01:42:03.255  INFO 4088 --- [nio-8072-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2018-03-31 01:42:03.255  INFO 4088 --- [nio-8072-exec-2] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2018-03-31 01:42:03.281  INFO 4088 --- [nio-8072-exec-2] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 26 ms
2018-03-31 01:42:03.489  WARN 4088 --- [nio-8072-exec-2] o.s.s.c.bcrypt.BCryptPasswordEncoder     : Encoded password does not look like BCrypt

我的实体模型类是:

@Entity
@Table(name="USERS")
@DynamicUpdate
public class AppUsers {

    @Id
    @Column(name="ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @ApiModelProperty(notes = "The user auto generated identity", required = true)
    private Long id;

    @Column(name="username")
    @ApiModelProperty(notes = "The username parameter", required = true)
    private String username;

    @Column(name="password")
    @ApiModelProperty(notes = "The password parameter", required = true)
    private String password;

    @JsonManagedReference
    @OneToMany(mappedBy="appUsers")
    private Set<UserRole> userRoles;

'''''' setters and getters
}

Role实体:

@Entity
@Table(name="ROLE")
public class Role {

    @javax.persistence.Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "role_id", unique = true, nullable = false)
    private Long Id;

    @Column(name = "name")
    private String roleName;

   @JsonManagedReference
    @OneToMany(mappedBy="role")
    private Set<UserRole> userRoles;

   //getters and setters

}

UserRole实体:

@Entity
@Table(name="USER_ROLE")
@DynamicUpdate
public class UserRole   implements Serializable {

    private static final long serialVersionUID = 6128016096756071383L;

    @Id
    @Column(name="ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @ApiModelProperty(notes = "The userrole auto generated identity", required = true)
    private long id;

    @JsonBackReference
    @ManyToOne//(fetch=FetchType.LAZY)
    private AppUsers appUsers;

    @JsonBackReference
    @ManyToOne//(fetch=FetchType.LAZY)
    private Role role;

   // getters and setters
}

数据库中的密码已正确加密Spring security BCrypt及其数据类型为varchar(255),大于60。

22 个答案:

答案 0 :(得分:18)

你能仔细检查一下你的客户机密码是否被编码?

@Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
    configurer
            .inMemory()
            .withClient(clientId)
            .secret(passwordEncoder.encode(clientSecret))
            .authorizedGrantTypes(grantType)
            .scopes(scopeRead, scopeWrite)
            .resourceIds(resourceIds);
}

答案 1 :(得分:13)

BCryptPasswordEncoder无法将原始密码与编码密码匹配时显示此警告。

哈希密码现在可能是“ $ 2b”或“ $ 2y”。

Spring Security中有一个错误,该错误的正则表达式总是在寻找“ $ 2a”。将调试点放在matches()中的BCryptPasswordEncoder.class函数上。

答案 2 :(得分:10)

当oauth2依赖关系转移到云时,我开始面对这个问题。先前它是安全框架的一部分:

len(A intersect B) / len(A union B)

现在它是云框架的一部分:

<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
</dependency>

因此,如果您使用的是云依赖关系(Finchley.RELEASE),则可能需要对秘密进行如下编码:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

答案 3 :(得分:5)

PasswordEncoder应该像这样设置:

Sub OnSlideShowPageChange()
MsgBox("Hello")
End Sub

答案 4 :(得分:1)

在Spring Security 5中,默认编码器是DelegatingPasswordEncoder,它需要密码存储格式。

阅读this

    private PasswordEncoder delegateEncoder =
            PasswordEncoderFactories.createDelegatingPasswordEncoder();

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception{
            clients
                    .jdbc(dataSource)
                    .passwordEncoder(delegateEncoder);
    }

使用默认编码器password生成secretDelegatingPasswordEncoder代码

System.out.println(delegateEncoder.encode("123123"));
// it generates the encoded code something like this: 
// {bcrypt}$2a$10$0aISzamI0jBCVTxONzJlHOk7O7QS.XPFIheLVhXultVa9Ju7SarZ6

答案 5 :(得分:1)

我遇到了同样的问题,问题是您应该对 inMemory 密码进行编码,因为当您单击登录表单时,Spring Security 会从 HTTP Basic Auth 标头中提取提取的密码,散列并自动将其与来自您的 UserDetails 对象的散列密码。如果两者匹配,则用户已成功通过身份验证,否则您将收到此错误。

例如,

内存密码 = "123"
passwordEncoder.encode("123")

参考:link

答案 6 :(得分:1)

对于我自己类似的情况,我只是像这样passwordEncoder().encode("password")而不是原始字符串"password"来编码密码:

authenticationManagerBuilder
  .inMemoryAuthentication()
  .withUser("user")
  // Just changed here
  .password(passwordEncoder().encode("password"))
  .roles("USER");


@Bean
public PasswordEncoder passwordEncoder() {
  return new BCryptPasswordEncoder();
}

答案 7 :(得分:1)

如果您将Spring Boot从1x迁移到2x,请用client_secret更新表oauth_client_details中的字段BCryptPasswordEncoder。编码秘密使用:

    BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
    String password = "12345678";
    String encodedPassword = passwordEncoder.encode(password);

    System.out.println();
    System.out.println("Password is         : " + password);
    System.out.println("Encoded Password is : " + encodedPassword);
    System.out.println();

    boolean isPasswordMatch = passwordEncoder.matches(password, encodedPassword);
    System.out.println("Password : " + password + "   isPasswordMatch    : " + isPasswordMatch);

答案 8 :(得分:1)

到目前为止,在Spring Boot 2.1.7.RELEASE中,我仍然遇到此问题。 我使用了一些在线工具,这些工具给我的哈希值从$ 2b或$ 2y开始, Spring的BCryptPasswordEncoder不允许这样做:

public class BCryptPasswordEncoder implements PasswordEncoder {
    private Pattern BCRYPT_PATTERN = Pattern
            .compile("\\A\\$2a?\\$\\d\\d\\$[./0-9A-Za-z]{53}");
...

解决方案:使用BCryptPasswordEncoder类对密码进行编码:

BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
System.out.println(encoder.encode("admin"));

然后:

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
        throws Exception {
    auth.inMemoryAuthentication()
            .withUser("admin")
            .password("{bcrypt}$2a$10$6CW1agMzVzBhxDzK0PcxrO/cQcmN9h8ZriVEPy.6DJbVeyATG5mWe")
            .roles("ADMIN");
}

答案 9 :(得分:1)

BCryptPasswordEncoder不会剥离{bcrypt} id,但是DelegatingPasswordEncoder会这样做。当我将BCryptPasswordEncoder明确定义为DaoAuthenticationProvider的编码器时,它将在BCryptPasswordEncoder(不带id条)上调用matchs方法,但在DelegatingPasswordEncoder(带id条)上没有调用

答案 10 :(得分:1)

请检查您的方法UserDetails loadUserByUsername(String username)是否返回有效的UserDetail对象。如果返回的对象为null /值无效的对象,那么您也会看到此错误。

答案 11 :(得分:0)

秘密使用noop进行测试。

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients.inMemory()
            .withClient("angular")
            .secret("{noop}@ngular0")
            .scopes("read", "write")
            .authorizedGrantTypes("password")
            .accessTokenValiditySeconds(1800);
}

答案 12 :(得分:0)

识别此问题的最佳方法“ 编码后的密码看起来不像BCrypt ”是在类 org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder 中设置一个断点>。然后检查警告的根本原因。

if (!BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
    logger.warn("Encoded password does not look like BCrypt");
    return false;
}

答案 13 :(得分:0)

您可能在安全配置SecurityConfig

中缺少此bean
@Bean
public DaoAuthenticationProvider getAuthenticationProvider() {
    DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
    authenticationProvider.setUserDetailsService(customDetailsService);
    authenticationProvider.setPasswordEncoder(encoder());
    return authenticationProvider;
}

答案 14 :(得分:0)

我通过使用60个字符以上的长密码摆脱了此错误消息,例如CXPW3XT2vXwBZk9mYZ5eCrKPM8kXJC6bbwJQjtGq2NQRYQPzsvqTwYz8JvWhWD5KLrrUHHammBNV3tkkyA4U

答案 15 :(得分:0)

  1. 如果您在密码前添加了“ {bcrypt}”,但未相应配置您的应用,则会收到此错误。考虑删除它。
  2. 如果仍然无法解决,只需将用户密码的列长度设置为60(如果您在开始时添加了{bcryt},则为68)对我有用!

答案 16 :(得分:0)

我在进行Spring Security课程时遇到了这个错误。

我的问题是,即使我在AuthenticationManager中使用了编码,例如:

    @Autowired
    public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder());
    }

问题是保存用户时我没有对密码进行编码!! 示例:

final Principal entity = new Principal(loginName, passwordEncoder.encode(pass), roles);

答案 17 :(得分:0)

UserDetails 实体必须返回编码后的密码。这取决于实现。

class MyUserDetails: UserDetails {

    //...

    override fun getPassword(): String {
        return passwordEncoder().encode("aPassword")
    }

    private fun passwordEncoder(): PasswordEncoder {
        return BCryptPasswordEncoder()
    }

}

GL

答案 18 :(得分:0)

我有同样的错误,这是由于密码列的数据类型, 该列的长度固定为空白(CHARACTER),因此请确保您使用的是VARCHAR数据类型,否则将密码列的长度更改为60。

答案 19 :(得分:0)

问题是密码中的空格。

尝试在从自定义 UserDetailsS​​ervice 类返回时修剪密码。

答案 20 :(得分:0)

我将我的 bcrypt 哈希值从 $2y 更改为 $2a,即将 $2y$12$0Evlz//oO 更改为 $2a$12$0Evlz//oO 并且它对我有用。

答案 21 :(得分:0)

考虑到我的用例与 Spring Boot 中的不同,我已经尝试了上述所有解决方案。就我而言,我将我的 DBMS 从 MySQL 切换到 PostgreSQL,并且该应用程序在 MySQL 的情况下运行良好,登录/注销功能运行良好。但是当我切换到 Postgres 时,这个错误开始出现在登录功能中。我在 pgAdmin 中注意到,BCrypt 密码最后有多余的空格,我什至尝试手动删除这些空格,但每次都自行回滚更改。

最后我在某处读到,BCrypt 加密文本长度是固定的,即 60 个字符。但是我在 Postgres 中的密码字段是 80 个字符。我只是将字段长度从 80 个字符更改为 60 个字符。您可以使用 GUI 执行此操作,也可以使用 ALTER TABLE 命令从 CLI 执行此操作,任何适合您的操作均可。