java - 春季安全 | 为什么当它们有效时我会得到错误的凭据?
问题描述
所以我正在开发一个 Spring Boot 项目,安全性是我想要在这个项目中拥有的东西之一。我遇到了这个问题,我的代码总是出现 BadCredentialsException,但我认为我的凭据是正确的。
我的身份验证控制器:
@RestController
@CrossOrigin
public class JwtAuthenticationController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private JwtUserDetailsService userDetailsService;
@RequestMapping(value = "/authenticate", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtRequest authenticationRequest) throws Exception {
authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
final UserDetails userDetails = userDetailsService
.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtResponse(token));
}
private void authenticate(String username, String password) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException e) {
throw new Exception("USER_DISABLED", e);
} catch (BadCredentialsException e) {
throw new Exception("INVALID_CREDENTIALS", e);
}
}
@RequestMapping(value = "/register", method = RequestMethod.POST)
public ResponseEntity<?> saveUser(@RequestBody UserDTO user) throws Exception {
return ResponseEntity.ok(userDetailsService.save(user));
}
}
我的用户详细信息服务:
@Service
public class JwtUserDetailsService implements UserDetailsService {
@Autowired
private UserDao userDao;
@Autowired
private PasswordEncoder bcryptEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
DAOUser user = userDao.findByUsername(username);
if (user == null){
throw new UsernameNotFoundException("User not found with username: " + username);
}
return new User(user.getUsername(), user.getPassword(), new ArrayList<>());
}
public DAOUser save(UserDTO user) {
DAOUser newUser = new DAOUser();
newUser.setUsername(user.getUsername());
newUser.setPassword(bcryptEncoder.bCryptPasswordEncoder().encode(user.getPassword()));
return userDao.save(newUser);
}
}
我的网络安全配置:
@Configuration
@AllArgsConstructor
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {//provides security for endpoints
@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Autowired
private UserDetailsService jwtUserDetailsService;
@Autowired
private JwtRequestFilter jwtRequestFilter;
private final AccountService accountService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()//So we can send post requests without being rejected(if we using form based indication we want to enable this)
.authorizeRequests()
.antMatchers("/authenticate","/register")
.permitAll()//any request that goes trough that end point we want to allow.
.anyRequest()
.authenticated().and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
@Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider =
new DaoAuthenticationProvider();
provider.setPasswordEncoder(bCryptPasswordEncoder);
provider.setUserDetailsService(accountService);
return provider;
}
}
我的请求过滤器:
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
private JwtUserDetailsService jwtUserDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
if (authorizationHeader != null && authorizationHeader.startsWith("")){
jwt = authorizationHeader.substring(7);
username = jwtTokenUtil.extractUsername(jwt);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null){
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwt, userDetails)){
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
我的密码编码器:
@Configuration
public class PasswordEncoder{
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
}
我的用户道
@Entity
@Table(name = "myusers")
public class DAOUser {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column
private String username;
@Column
@JsonIgnore
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
我的 UserDao 界面
我知道这个名字令人困惑,但我只是按照教程操作,他给了它这个名字,我没有更改它,因为我希望它在重命名文件之前工作。
@Repository
public interface UserDao extends CrudRepository<DAOUser, Integer> {
DAOUser findByUsername(String username);
}
我想这些都是你需要帮助我的所有文件。如果您需要更多,请询问,我会上传它们。谁能帮我解决这个问题?谢谢!!
解决方案
如果您阅读了有关密码的 spring 文档(您应该在此处询问之前完成)
您将看到存储密码时的格式为:
{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
当您存储它时,您只是存储它,而不是在{bcrypt}
存储之前将前缀添加到生成的字符串中。
newUser.setPassword(bcryptEncoder.bCryptPasswordEncoder().encode(user.getPassword()));
因此,当您稍后提供密码时,您会得到错误的密码,因为 spring 在解码密码时不知道使用什么编码器。
推荐阅读
- sharepoint - 无法使用 ActiveX 控件从我们的 Web 应用程序查看或编辑 ms office 软件中的文件
- python - 读取多个文件,字典值被覆盖(添加列名)
- java - 如何修复 setError 消息以不同的语言出现?
- java - 显示数组的值。获取奇怪的值
- php - 安全威胁 - composer/installed.json
- java - 在 Java 中解析 HATEOAS JSON
- ruby-on-rails - Rails 中的实例变量是否在每次刷新页面时都会重置?
- java - java.lang.String 在android app中接收数据时引入新字符
- reactjs - Immutable 和 Reselect 的正确模式是什么?
- python - ResNet50 模型在 keras 中没有通过迁移学习进行学习