I am using Spring Boot, Spring Security, OAuth2 and JWT to authenticate my application, but I keep getting this nasty error and I don't have any idea what is wrong. My CustomDetailsService
class:
@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(); } }
No error message except:
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
My entity model classes are:
@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:
@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:
@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 }
My password in the database is properly encrypted Spring security BCrypt and it datatype is varchar(255) which is larger than 60.
BCryptPasswordEncoder is a single-way password encoder. The one-way encoding algorithm is used to encrypt a password. There's no way to decrypt the password. Alternatively, the one-way password encoder returns the same encrypted string if you call the encoding algorithm with the same password.
What is Bcrypt Encoding. As per wiki, bcrypt is a password hashing function designed by Niels Provos and David Mazières, based on the Blowfish cipher. Bcrypt uses adaptive hash algorithm to store password.
Understanding Password Encoder in Springboot: What is Bcrypt? It is a password hashing function based on BlowFish symmetric block cipher algorithm and crypt which your password hashing function in UNIX.
BCryptPasswordEncoder shows this warning when it fails to match a raw password with an encoded password.
The hashed password might be “$2b” or “$2y” now.
And there is a bug in Spring Security that has a regex always looking for “$2a”. Put a debug point at the matches()
function in the BCryptPasswordEncoder.class
.
Can you double check your client secret is encoded?
@Override public void configure(ClientDetailsServiceConfigurer configurer) throws Exception { configurer .inMemory() .withClient(clientId) .secret(passwordEncoder.encode(clientSecret)) .authorizedGrantTypes(grantType) .scopes(scopeRead, scopeWrite) .resourceIds(resourceIds); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With