【发布时间】:2022-01-12 13:14:11
【问题描述】:
我目前正在开发一个带有 React 前端和 Spring Boot 后端的全栈 Web 应用程序。我已经实现了 Spring 安全性和 JWT 进行身份验证,但从那时起我就无法访问我的 API 端点(请参阅控制器)。我已经设法访问了 GET 请求端点,但是尽管在开始请求之前登录了后端,但 PUT 或 DELETE 请求似乎都不起作用。
我在另一篇文章中看到禁用 csrf 解决了这个问题,但我从来没有启用它,所以这对我不起作用。
WebSecurityConfig 文件:
@Configuration
@AllArgsConstructor
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/v*/registration/**")
.permitAll()
.anyRequest()
.authenticated().and()
.formLogin();
}
控制器(REST API)
@RestController
@RequestMapping(path = "/question")
@CrossOrigin("*")
public class QuestionController {
private final QuestionService questionService;
@Autowired
public QuestionController(QuestionService questionService) {
this.questionService = questionService;
}
@CrossOrigin("*")
@GetMapping("/all")
public ResponseEntity<List<Question>> getAllQuestions() {
List<Question> questions = questionService.findAllQuestions();
return new ResponseEntity<>(questions, HttpStatus.OK);
}
@CrossOrigin("*")
@GetMapping("/find/{id}")
public ResponseEntity<Question> getQuestionById(@PathVariable("id") Long id) {
Question question = questionService.findQuestionById(id);
return new ResponseEntity<>(question, HttpStatus.OK);
}
@CrossOrigin("*")
@PostMapping("/add")
public ResponseEntity<Question> addQuestion(@RequestBody Question question) {
Question newQuestion = questionService.addQuestion(question);
return new ResponseEntity<>(newQuestion, HttpStatus.CREATED);
}
@CrossOrigin("*")
@PutMapping("/update/{id}")
public ResponseEntity<Question> updateQuestion(@RequestBody Question question) {
Question updateQuestion = questionService.updateQuestion(question);
return new ResponseEntity<>(updateQuestion, HttpStatus.OK);
}
@CrossOrigin("*")
@DeleteMapping("(/delete/{id}")
public ResponseEntity<Question> deleteQuestion(@PathVariable("id") Long id) {
questionService.deleteQuestion(id);
return new ResponseEntity<>(HttpStatus.OK);
}
}
编辑:这是实现 UserDetailsService 的代码
@Service
@Autowired can be left out by using this annotation.
@AllArgsConstructor
public class BenutzerkontoService implements UserDetailsService {
private final static String USER_NOT_FOUND_MSG = "User with email %s not found";
private final BenutzerkontoRepository benutzerkontoRepository;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
private final ConfirmationTokenService confirmationTokenService;
public List<Benutzerkonto> findAllBenutzerkonto() {
// findAll() returns a list of all user objects
return benutzerkontoRepository.findAll();
}
/**
* This method is responsible for identifying the given email inside the database.
*
* @param email
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
return benutzerkontoRepository.findByEmail(email).orElseThrow(() -> new UsernameNotFoundException(String.format(USER_NOT_FOUND_MSG, email)));
}
/**
* The following function checks, whether the user already exists (by email) and registers the user with an
* encoded password, if the email address does not exist already.
*
* The user also gets a random JSON Web Token assigned
*
* @param benutzerkonto
* @return
*/
public String signUpUser(Benutzerkonto benutzerkonto) {
// Check whether user exists
boolean userExists = benutzerkontoRepository.findByEmail(benutzerkonto.getEmail()).isPresent();
if (userExists) {
throw new IllegalStateException("Email is already taken");
}
// Encode the user password
String encodedPassword = bCryptPasswordEncoder.encode(benutzerkonto.getPassword());
// Replace the plain text password with the encoded version
benutzerkonto.setPasswort(encodedPassword);
// Save user to database
benutzerkontoRepository.save(benutzerkonto);
// Create random String via the UUID class for using it as token
String token = UUID.randomUUID().toString();
// Instantiate ConfirmationToken class, which defines the token for account confirmation
ConfirmationToken confirmationToken = new ConfirmationToken(
token,
LocalDateTime.now(),
// Make token invalid after 15 minutes
LocalDateTime.now().plusMinutes(15),
benutzerkonto
);
// Save token to database
// TODO: Shouldn't it be saved by a confirmationTokenRepository object? Why does this also work?
confirmationTokenService.saveConfirmationToken(confirmationToken);
return token;
}
/**
* This function takes the email address as a parameter and enables/activates the email for logging in.
*
* @param email
* @return
*/
public int enableAppUser(String email) {
return benutzerkontoRepository.enableAppUser(email);
}
/**
* This method adds a new user account to the database, but it searches for the passed value of email
* inside the database first. The user object "benutzerkonto" will only be saved in the database repository,
* if the email does not exist already.
*
* @param benutzerkonto
*/
public void addNewUser(Benutzerkonto benutzerkonto) {
// userEmailPresence can be null, if the email does not exist in the database yet, which is why it's an Optional.
Optional<Benutzerkonto> userEmailPresence = benutzerkontoRepository.findBenutzerkontoByEmail(benutzerkonto.getUsername());
if (userEmailPresence.isPresent()) {
throw new IllegalStateException("Email already taken.");
} else {
benutzerkontoRepository.save(benutzerkonto);
}
}
}
Edit2:这是用户类
@Getter
@Setter
@EqualsAndHashCode
@NoArgsConstructor
@Entity
@Table
public class Benutzerkonto implements Serializable, UserDetails {
@SequenceGenerator(
name = "student_sequence",
sequenceName = "student_sequence",
allocationSize = 1
)
@Id
@GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "student_sequence"
)
@Column(nullable = false)
private Long id;
private String email;
private String passwort;
@Column(nullable = false, updatable = false)
@Enumerated(EnumType.STRING)
private UserRole rolle;
private Boolean locked = false;
// false by default, because user has to confirm via email first
private Boolean enabled = false;
// Constructor
public Benutzerkonto(String email, String passwort, UserRole rolle) {
this.email = email;
this.passwort = passwort;
this.rolle = rolle;
}
@Override
public String toString() {
return "Benutzerkonto{" +
"id=" + id +
", email='" + email + '\'' +
", passwort='" + passwort + '\'' +
", rolle=" + rolle +
", locked=" + locked +
", enabled=" + enabled +
'}';
}
// Methods of UserDetails interface
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(rolle.name());
return Collections.singletonList(authority);
}
@Override
public String getPassword() {
return passwort;
}
@Override
public String getUsername() {
return email;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return !locked;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return enabled;
}
}
【问题讨论】:
-
但是,尽管在开始请求之前在后端登录,但 PUT 或 DELETE 请求似乎都不起作用。 当然,您必须先进行身份验证,这就是您使用 @ 实现的987654329@.
-
您是否在方法
deleteQuestion中添加了断点?您的响应是 404,这意味着您的控制器方法未找到或该方法找不到对象。 -
没错,我已经通过 Spring Security 登录表单登录了后端,然后访问了删除端点。我还没想通,为什么还没找到呢
标签: java spring-boot api spring-security