Spring安全使用Bcrypt和hibernate返回错误的凭据

时间:2018-05-15 09:42:48

标签: java spring security spring-boot

正如标题所说,我使用Spring Security和JWT(使用hibernate和bCrypt)来注册用户并让他们登录。我已经跟着this tutorial在我的项目中完成了这项工作。当完成与教程完全相同(使用内存数据库)时,所有事情似乎都可以正常工作。但是当将代码集成到我自己的项目中时,身份验证会一直失败,从而产生一个"错误的凭据"例外。

主:

@SpringBootApplication
@EnableConfigurationProperties
public class ApiApplication {

@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
    return new BCryptPasswordEncoder();
}

public static void main(String[] args) {
    SpringApplication.run(ApiApplication.class, args);
}
}

我的websecurity配置如下所示:

@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {

private UserDetailsService userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;

public WebSecurity(UserDetailsService userDetailsService,
                   BCryptPasswordEncoder bCryptPasswordEncoder) {
    this.userDetailsService = userDetailsService;
    this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors().and().csrf().disable().authorizeRequests()
            .antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll() 
            .anyRequest().authenticated()
            .and()
            .addFilter(new JWTAuthenticationFilter(authenticationManager()))
            .addFilter(new JWTAuthorizationFilter(authenticationManager()))
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}

@Bean
CorsConfigurationSource corsConfigurationSource() {
    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
    return source;
}

}

身份验证过滤器,每当我尝试使用用户名和密码发送帖子时,即使使用正确的凭据,也会运行unsuccessfulAuthentication。

public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;

public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
    this.authenticationManager = authenticationManager;
}

@Override
public Authentication attemptAuthentication(HttpServletRequest req,
                                            HttpServletResponse res) throws AuthenticationException {
    try {
        User creds = new ObjectMapper()
                .readValue(req.getInputStream(), User.class);

        return authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        creds.getUsername(),
                        creds.getUsername(),
                        new ArrayList<>())
        );
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

@Override
protected void successfulAuthentication(HttpServletRequest req,
                                        HttpServletResponse res,
                                        FilterChain chain,
                                        Authentication auth) throws IOException, ServletException {
    System.out.println("This method never runs...");
    Claims claims = Jwts.claims()
            .setSubject(((org.springframework.security.core.userdetails.User) auth.getPrincipal()).getUsername())
            .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME));

    String token = Jwts.builder()
            .setClaims(claims)
            .signWith(SignatureAlgorithm.HS512, SECRET.getBytes())
            .compact();

    res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
}

@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
    super.unsuccessfulAuthentication(request, response, failed);
    System.out.println("FAILED");
    failed.printStackTrace(); // bad creds
}
}

UserDetailServiceImpl:

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

private UserRepository repository;

public UserDetailsServiceImpl(UserRepository applicationUserRepository) {
    this.repository = applicationUserRepository;
}

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User user = repository.findByUsername(username);
    if(user == null){
        System.out.println("User is null");
        throw new UsernameNotFoundException(username);
    }

    return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), emptyList());
}
}

用户实体:

@Data
@Entity
@Table(name = "user_entity")
public class User implements Serializable {

public User() { }

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@Column(name = "user_id", unique = true)
private long userId;

@NotEmpty
@Column(name = "username", unique = true)
private String username;

@NotEmpty
@Column(name = "user_rol")
@JsonProperty("userRol")
private String userRol;

@NotEmpty
@Column(name = "password")
private String password;
}

在我的用户控制器中,我加密密码如下:

    @PostMapping("/sign-up")
    public User signUp(@RequestBody User user) {
    user.setPassword(passwordEncoder.encode(user.getPassword()));
    user.setUserRol("ADMIN");
    return userService.signUpUser(user);
}

Everythings似乎没问题,Spring成功地将新用户放入数据库(使用加密密码),每当我进行登录尝试时,它也成功获得该用户,但身份验证仍然失败(具有正确的证书凭据)。所以我的猜测是bcypt密码编码器有问题......

我想知道的另一件事; / login路由来自哪里?这是Spring Security中的默认路由吗? (我从未宣布过)

感谢帮助人员!

1 个答案:

答案 0 :(得分:0)

也许是因为您的attemptAuthentication()方法错误。

@Override
public Authentication attemptAuthentication(HttpServletRequest req,
                                        HttpServletResponse res) throws AuthenticationException {
try {
    User creds = new ObjectMapper()
            .readValue(req.getInputStream(), User.class);

    return authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(
                    creds.getUsername(),
                    creds.getUsername(),
                    new ArrayList<>())
    );
} catch (IOException e) {
    throw new RuntimeException(e);
}

}

在UsernamePasswordAuthenticationToken的构造函数中,第二个参数应该是creds.getPassword()而不是creds.getUsername()。