[英]Getting 403 Response from Spring Boot Rest Service on Raspberry Pi
我的设置:
我有一个调用 spring boot rest api 的 vue 前端。 在本地,我在单独的 docker 容器中运行它,在我的本地机器上一切正常。
出于测试目的,我将树莓派连接到我的路由器。 为此,我还在不同的 docker 容器中运行前端和 spring boot。
这些设置之间的唯一区别是,我不能在我的树莓派上使用FROM openjdk:11
映像,因为它不支持 arm32v7。 在那里我使用了eclipse-temurin:11
容器。
这是我的 docker-compose.yml:
version: "3.4"
services:
db:
image: jsurf/rpi-mariadb
environment:
MYSQL_ROOT_PASSWORD: 'admin'
MYSQL_DATABASE: 'ghp_board'
MYSQL_USER: 'ghp_board_admin'
MYSQL_PASSWORD: 'ghp_board'
volumes:
- ./data/mariadb:/docker-entrypoint-initdb.d
ports:
- "33006:3306"
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "8081:8080"
container_name: ghp_board_frontend
volumes:
- ./frontend:/usr/src/app/ghp_board_frontend
depends_on:
- db
backend:
build:
context: ./backend
dockerfile: Dockerfile
depends_on:
- db
ports:
- "8886:8080"
这是我用于后端容器的Dockerfile
:
FROM eclipse-temurin:11
COPY ./system/build/libs/system-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
我想要什么
Spring Boot 应用程序现在有两个控制器(News 和 Demand),它们应该通过 REST API 向前端提供数据。 两个控制器都有几乎相同的端点:
import org.springframework.web.multipart.MultipartFile;
import org.webjars.NotFoundException;
import javax.lang.model.element.NestingKind;
import javax.persistence.EntityNotFoundException;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Optional;
@RestController
@AllArgsConstructor
@CrossOrigin
public class NewsController {
@Autowired
private final NewsService newsService;
private JwtAuthenticationService jwtAuthenticationService;
@GetMapping(path = "/api/v1/news", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> getAllNews(HttpServletRequest request) {
return new ResponseEntity<>(newsService.getAllNewsEntries(), HttpStatus.OK);
}
@GetMapping(path = "/api/v1/news/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> getNews(@PathVariable("id") Long id, HttpServletRequest request) {
return new ResponseEntity<>(newsService.getNewsEntry(id), HttpStatus.OK);
}
// Post -> receive a new NewsEntry
@PostMapping(
path = "/api/v1/news",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> createNews(@RequestParam("file") Optional<MultipartFile> file, @RequestParam("newsData") String newsEntryString, HttpServletRequest request) throws IOException {
ObjectMapper mapper = new ObjectMapper()
.findAndRegisterModules()
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
NewsEntryDto newsEntryDto = mapper.readValue(newsEntryString, NewsEntryDto.class);
return new ResponseEntity<>(newsService.insertNewNewsEntry(newsEntryDto, file), HttpStatus.OK);
}
//Put -> update an existing NewsEntry
@PutMapping(
path = "/api/v1/news",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<?> updateNews(
@RequestParam("file") Optional<MultipartFile> file,
@RequestParam("newsUpdateId") Long newsUpdateId,
@RequestParam("newsData") String newsUpdateString,
HttpServletRequest request) throws IOException
{
ObjectMapper mapper = new ObjectMapper()
.findAndRegisterModules()
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
NewsEntryDto newsUpdateDto = mapper.readValue(newsUpdateString, NewsEntryDto.class);
return new ResponseEntity<>(newsService.updateNewsEntry(newsUpdateId, newsUpdateDto, file), HttpStatus.OK);
}
//Delete -> delete an existing NewsEntry
@DeleteMapping(
path = "/api/v1/news/{id}"
)
public ResponseEntity<?> deleteNews(@PathVariable("id") Long newsDeleteId) {
newsService.deleteNewsEntry(newsDeleteId);
return new ResponseEntity<>("", HttpStatus.OK);
}
}
package com.example.system.demand;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.AllArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.webjars.NotFoundException;
import java.util.List;
import java.util.Optional;
@RestController
@AllArgsConstructor
@CrossOrigin
public class DemandController {
private DemandService demandService;
@GetMapping(path = "/api/v1/demand", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> getAllDemands() {
return new ResponseEntity<>(demandService.getAllDemandEntries(), HttpStatus.OK);
}
@GetMapping(path = "/api/v1/demand/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> getDemandById(@PathVariable("id") Long id) {
return new ResponseEntity<>(demandService.getDemandById(id), HttpStatus.OK);
}
@PostMapping(
path = "/api/v1/demand",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<?> insertNewDemand(
@RequestParam Optional<List<MultipartFile>> files,
@RequestParam String demandData
) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper()
.findAndRegisterModules()
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
DemandEntryDto demandEntryDto = mapper.readValue(demandData, DemandEntryDto.class);
return new ResponseEntity<>(demandService.insertNewDemandEntry(demandEntryDto, files), HttpStatus.OK);
}
@ExceptionHandler(NotFoundException.class)
public ResponseEntity<?> handlerNotFoundException(NotFoundException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
}
}
我的问题:
当我从前端调用 GET-Endpoint /api/v1/news
时,我从数据库中获取所有条目,所以这是有效的。 GET-Endpoint /api/v1/news/{id}
和 POST-Endpoint 也可以正常工作。 但是 NewsController 的其他 Endpoints PUT 和 DELETE 以及 DemandController 的所有 Endpoints 正在接收 Responsecode 403 - Forbidden
。 我真的不明白为什么会这样。
我的WebSecurityConfig
不应该有任何解释这种行为的规范:
package com.example.system.security;
import com.example.system.security.jwt.JwtAuthenticationEntrypoint;
import com.example.system.security.jwt.JwtAuthenticationProvider;
import com.example.system.security.jwt.JwtAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@SuppressWarnings("SpringJavaAutowiringInspection")
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationEntrypoint unauthorizedHandler; // Gibt 403 zurück, wenn nicht authorisiert
@Autowired
private JwtAuthenticationProvider jwtAuthenticationProvider;
@Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) {
authenticationManagerBuilder.authenticationProvider(jwtAuthenticationProvider);
}
@Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() {
return new JwtAuthenticationTokenFilter();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.cors().and() // Aktivieren von Cross-Site
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() // Gibt 403 zurück, wenn nicht authorisiert
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/")
.permitAll()
.antMatchers("/home")
.permitAll()
.antMatchers("/api/v1/login")
.permitAll()
.anyRequest().authenticated();
httpSecurity.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
httpSecurity.headers().cacheControl();
}
}
并且 axois 调用的标头也是相同的:
getNews() {
const token = localStorage.getItem('token');
const header = {
'Content-Type' : 'application/x-www-form-urlencoded',
'Authorization': `Bearer ${token}`,
'Access-Control-Allow-Origin' : '*',
'Access-Control-Allow-Methods':'GET,PUT,POST,DELETE,PATCH,OPTIONS',
}
return axios.get(process.env.VUE_APP_BACKEND_URL + "/api/v1/news", {
headers: header
});
},
getNewsDetail(id) {
const token = localStorage.getItem('token');
const header = {
'Content-Type' : 'application/x-www-form-urlencoded',
'Authorization': `Bearer ${token}`,
'Access-Control-Allow-Origin' : '*',
'Access-Control-Allow-Methods':'GET,PUT,POST,DELETE,PATCH,OPTIONS',
}
return axios.get(process.env.VUE_APP_BACKEND_URL + "/api/v1/news/" + id, {
headers: header
});
},
insertNewsEntry(data) {
const token = localStorage.getItem('token');
const header = {
//'Content-Type' : 'application/json',
'Content-Type' : 'multipart/form-data',
'Authorization': `Bearer ${token}`,
'Access-Control-Allow-Origin' : '*',
'Access-Control-Allow-Methods':'GET,PUT,POST,DELETE,PATCH,OPTIONS',
}
return axios.post(process.env.VUE_APP_BACKEND_URL + "/api/v1/news", data, {
headers: header
});
},
updateNewsEntry(data) {
const token = localStorage.getItem('token');
const header = {
//'Content-Type' : 'application/json',
'Content-Type' : 'multipart/form-data',
'Authorization': `Bearer ${token}`,
'Access-Control-Allow-Origin' : '*',
'Access-Control-Allow-Methods':'GET,PUT,POST,DELETE,PATCH,OPTIONS',
}
return axios.put(process.env.VUE_APP_BACKEND_URL + "/api/v1/news", data, {
headers: header
});
},
deleteNewsEntry(id) {
const token = localStorage.getItem('token');
const header = {
//'Content-Type' : 'application/json',
'Content-Type' : 'multipart/form-data',
'Authorization': `Bearer ${token}`,
'Access-Control-Allow-Origin' : '*',
'Access-Control-Allow-Methods':'GET,PUT,POST,DELETE,PATCH,OPTIONS',
}
return axios.delete(process.env.VUE_APP_BACKEND_URL + "/api/v1/news/" + id, {
headers: header
});
}
有谁知道我在这里错过了什么?
对于任何未来的读者,如果评论被删除,我建议使用
http.cors().and().csrf().disable()...
而不是.csrf().disable().cors().and()..
在configure
方法中作为cors
& 的使用顺序csrf
在原始帖子中不正确,问题得到解决后记。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.