正如标题所说,我使用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中的默认路由吗? (我从未宣布过)
感谢帮助人员!
答案 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()。