我已经浪费了10个小时试图弄明白一件简单的事情。
我有一个反应前端和一个Spring Boot后端在端口8080上运行。
我在前端有自己的登录表单。
在一个理想的世界里,我所希望的是让Spring Boot像往常一样进行表单验证,而没有那个丑陋的引导表单。
我设置了一个基本的内存身份验证只是为了练习,但它根本不起作用
我有两个选择:
>
在Restendpoint中处理身份验证,然后将安全上下文设置为Restendpoint,而不是过滤器
以下是代码:
配置文件:
@Configuration
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
@Autowired
private TestFilter testFilter;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("mama")
.password("mama")
.roles("AUTH");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//Request authorization
http
.authorizeRequests()
.mvcMatchers("/api/vault").hasRole("AUTH")
.mvcMatchers("/api/vault/**").hasRole("AUTH")
.mvcMatchers("/**").permitAll();
//Disable default authentication behaviour:
//http.httpBasic().disable();
http.formLogin()
.loginPage("/")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/auth-success")
.failureUrl("/auth-failure");
//CSRF Protection:
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-CSRF-TOKEN");
repository.setParameterName("_csrf");
http.csrf()
.csrfTokenRepository(repository);
http.addFilterBefore(testFilter, UsernamePasswordAuthenticationFilter.class);
}
@Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
}
默认控制器:
@Controller
public class App {
/**
*
*/
@GetMapping(value = "/{path:[^\\.]*}")
public String index() {
return "index";
}
}
简单测试API:
@RestController
public class TestApi {
@GetMapping(value = "/api/get-csrf", consumes = {"application/json"})
public CsrfTestResponse getCsrf(HttpServletRequest request){
TestingLombokWorks testingLombokWorks = new TestingLombokWorks("Mama", "Mama is the best!");
CsrfToken token = new HttpSessionCsrfTokenRepository().loadToken(request);
CsrfTestResponse testResponse = new CsrfTestResponse();
testResponse.setCsrfHeaderName(token.getHeaderName());
testResponse.setCsrfParameterName(token.getParameterName());
testResponse.setCsrfToken(token.getToken());
testResponse.setTestingLombokWorks(testingLombokWorks);
return testResponse;
}
@GetMapping("/api/welcome")
public String publicResource(){
return "Welcome API!";
}
@GetMapping("/api/login-success")
public String loginSuccess() {
return "Login Successful!";
}
@PostMapping("/api/post-test")
public CsrfTestResponse postTest(HttpServletRequest request){
CsrfToken token = new HttpSessionCsrfTokenRepository().loadToken(request);
CsrfTestResponse testResponse = new CsrfTestResponse();
testResponse.setCsrfHeaderName(token.getHeaderName());
testResponse.setCsrfParameterName(token.getParameterName());
testResponse.setCsrfToken(token.getToken());
return testResponse;
}
@PostMapping("/login")
public String testLogin(){
return "Login Works?!";
}
@GetMapping("/auth-success")
public String authSuccess(){
return "Auth Success!";
}
@GetMapping("/auth-failure")
public String authFailure(){
return "Auth Failure!";
}
}
要测试的过滤器:
@Component
public class TestFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
System.out.println("Username?: " + request.getParameter("username"));
System.out.println("Password?: " + request.getParameter("password"));
filterChain.doFilter(request, response);
}
}
反应前端:
import React from 'react'
import { useForm } from 'react-hook-form'
export default function Login(props) {
const {register, handleSubmit, formState} = useForm();
return (
<form onSubmit={handleSubmit((data, event) => {
event.preventDefault();
let formData = new FormData();
formData.append("username", data.username);
formData.append("password", data.password);
let body = new URLSearchParams(formData);
let str = `username=${data.username}&password=${data.password}`;
console.log(str);
fetch('/login', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
Accept: '*/*',
[props.csrfHeaderName]: props.csrfToken
},
body: str
})
.then(response => response.text())
.then(data => console.log(data))
.catch(error => console.log(error));
})}
>
<legend>Login</legend>
<div>
<input
{
...register("username", {
value: "",
required: true,
})
}
type="text"
placeholder='Username: '
/>
</div>
<div>
<input
{
...register("password", {
value: "",
required: true,
})
}
type="password"
placeholder='Password: '
/>
</div>
<button type='submit'>Login</button>
</form>
)
}
我创建了一个测试过滤器只是为了测试,它证明了我的观点。当使用正常的服务器端呈现的默认Spring Boot表单提交表单时,请求可以工作,但当使用JavaScript发送请求时,请求失败。
当我看到登录请求的日志时,我根本没有看到它到达那个endpoint,而是直接转到 /auth-failure:
Username?: null
Password?: null
Username?: null
Password?: null
2022-04-04 15:53:45.960 DEBUG 20327 --- [nio-8080-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.example.springreact.api.TestApi#authFailure()
2022-04-04 15:53:45.960 DEBUG 20327 --- [nio-8080-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.example.springreact.api.TestApi#authFailure()
2022-04-04 15:53:45.960 DEBUG 20327 --- [nio-8080-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.example.springreact.api.TestApi#authFailure()
2022-04-04 15:53:45.961 DEBUG 20327 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet : GET "/auth-failure", parameters={}
2022-04-04 15:53:45.961 DEBUG 20327 --- [nio-8080-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.example.springreact.api.TestApi#authFailure()
2022-04-04 15:53:45.961 DEBUG 20327 --- [nio-8080-exec-5] m.m.a.RequestResponseBodyMethodProcessor : Using 'text/plain', given [*/*] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json]
2022-04-04 15:53:45.961 DEBUG 20327 --- [nio-8080-exec-5] m.m.a.RequestResponseBodyMethodProcessor : Writing ["Auth Failure!"]
2022-04-04 15:53:45.962 DEBUG 20327 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet : Completed 200 OK
我想知道进行这种简单身份验证的最佳方法是什么。
我尝试了一切可以想象的方法,我用ObjectMapper尝试了JSON post body,它抛出了IOException,没有任何有意义的错误消息,
Java有时会令人沮丧,甚至过时,当你最终花10个小时去做简单的事情时。
Java有时会令人沮丧,甚至过时,当你最终花10个小时去做简单的事情时。
这不能说。只是你错过了很多重要的信息来理解它为什么会失败。
(1)
http.formLogin()
.loginPage("/")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/auth-success")
.failureUrl("/auth-failure");
这是为了与Spring MVC一起使用,不在使用@RestController
所指示的Rest Spring Boot应用范围内。Spring登录表单旨在交付html表单,系统使用MVC模式进行授权。它不符合您的需求,因为您有另一个前端和另一个框架来执行简单的ajax调用。
第二)
在你明白为什么(1)失败后,你必须决定你可以走哪条路。您可以切换到基本身份验证,也可以尝试实现更复杂的东西,比如JWT身份验证令牌。如果你搜索得足够多,可以找到更多的解决方案,但我想说最常见的就是我在这里提到的那些。
调整应用程序以使用其中一种安全结构,然后更新前端以进行此类调用,您应该会没事。
我看到这个错误在chrome控制台" 加载http://localhost:4001/api/v1/sessions/new失败:对预试请求的响应没有通过权限改造检查:请求的资源上没有“访问-控制-允许-起源”标头。因此不允许访问“起源http://localhost:4044”。响应有HTTP状态代码405。 如果我只是尝试和卷曲完全相同的API调用它工作正常: 为什么它与CURL一起工作,而不
当我试图通过邮递员发送时,我有一个请求正在正常工作。我试图实现相同的使用代码,我面临一个错误。 我正在使用的代码- 我得到的错误是- org.springframework.web.client.HttpClientErr异常$未授权: 401未授权 但《邮递员》中同样的作品也有同样的细节。 我想说明一下 后端API不在我手里。 clientid,clientSecret,access_token
请问大佬们,如何通过axios,去实现post请求,并且请求参数为JOSN格式传入body内?我这样的写法有什么错误吗?请求就提示跨域报错
问题内容: 我在用JavaScript抓取API挣扎。当我尝试通过获取将某些内容发布到服务器时,请求正文为空数组。但是,当我使用Postman时,它可以工作…这是我在NodeJS中的服务器代码: 这是我的客户: 问题是在服务器端,req.body为空。有人能帮我吗?谢谢 ! 问题答案: 问题是 从文档中 … 防止该方法成为HEAD,GET或POST之外的任何其他内容, 并且 阻止 标头成为 简单标
每当页面尝试提交2MB或更大的文件时,都会发生此错误。但是参数在帖子里!我已经检查了Chrome开发工具。有人知道这个错误吗?Springboot 2.0.3。释放
可以在模板中直接使用$Request对象,直接输入它的属性或调用它的大部分方法,但只支持方法的第一个参数; // 调用Request对象的get方法 传入参数为id {$Request.get.id} // 调用Request对象的param方法 传入参数为name {$Request.param.name} // 调用Request对象的param方法 传入参数为post.post_title