【问题标题】:Spring Security using Bcrypt and hibernate returning bad credentialsSpring Security 使用 Bcrypt 和休眠返回错误凭据
【发布时间】:2018-05-15 18:38:56
【问题描述】:

正如标题所说,我正在使用 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);
}
}

我的网络安全配置如下所示:

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

}

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

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

一切似乎都很好,Spring 成功地将一个新用户放入数据库(使用加密密码),每当我尝试登录时,它也会成功获取该用户,但身份验证仍然失败(当然使用正确的凭据)。所以我猜bcypt密码编码器有问题......

我想知道的另一件事; /login 路由从何而来?这是 Spring Security 中的默认路由吗? (我从来没有声明过)

感谢大家的帮助!

【问题讨论】:

    标签: java spring security spring-boot


    【解决方案1】:

    可能是由于您的尝试身份验证()方法中的拼写错误。

    @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()。

    【讨论】:

    • 天哪...花了我 2 天时间。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2017-07-30
    • 2018-11-08
    • 2017-04-25
    • 1970-01-01
    • 2017-11-02
    • 2014-11-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多