自定义登录表单。配置Spring安全性以获取JSON响应 [英] Custom login form. Configure Spring security to get a JSON response
问题描述
我有一个简单的应用程序,它被分成两部分:
I have a simple application which is splitted into 2 parts :
- 使用Spring-boot / Spring公开REST服务的后端安全性
- 仅包含静态文件的前端。
请求由nginx接收监听端口80的服务器。
The requests are received by a nginx server which listens on port 80.
- 如果请求URL以/ api /开头,则请求被重定向到后端。
- 否则,请求由提供静态文件的nginx处理。
我创建了一个自定义登录表单(在前端部分),我正在尝试配置Spring-boot服务器。
I created a custom login form (in the frontend part) and I am trying to configure the Spring-boot server.
有很多例子我可以看到如何定义登录成功网址和登录错误网址,但我不希望Spring-security重定向用户。如果登录成功或HTTP 40x登录失败,我希望Spring-security使用HTTP 200回答。
There are a lot of examples where I can see how to define a "login success" url and a "login error" url but I do not want Spring-security to redirect the user. I want Spring-security to answer with HTTP 200 if the login succeeded or HTTP 40x is the login failed.
换句话说:我希望后端只回答JSON ,从不HTML。
In other words : I want the backend to only answer with JSON, never HTML.
到目前为止,当我提交登录表单时,请求被重定向,我得到默认的Spring登录表单作为答案。
Up to now, when I submit the login form, the request is redirected and I get the default Spring login form as an answer.
我试图使用 .formLogin()。loginProcessingUrl(/ login);
而不是 loginPage ()
:
@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("ADMIN");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/login");
推荐答案
感谢M. Denium并感谢本指南,我可以找到解决方案。
Thanks to M. Denium and thanks to this guide, I could find the solution.
首先,我遇到了登录表单本身的配置问题。由于后端的上下文路径设置为 / api
,自定义表单应该将表单参数提交到 / api / login
但我实际上是将数据提交到 / api / login /
(最后注意额外的 /
)。
First, I had a configuration problem with the login form itself. As the backend has a context-path set to /api
, the custom form should have submitted the form params to /api/login
but I was actually submitting the data to /api/login/
(Notice the extra /
at the end).
结果,我在不知不觉中试图访问受保护的资源!因此,请求由默认的 AuthenticationEntryPoint
处理,默认行为是将用户重定向到登录页面。
As a result, I was unknowingly trying to access a protected resource! Hence, the request was handled by the default AuthenticationEntryPoint
which default behavior is to redirect the user to the login page.
作为解决方案,我实现了自定义AuthenticationEntryPoint:
As a solution, I implemented a custom AuthenticationEntryPoint :
private AuthenticationEntryPoint authenticationEntryPoint() {
return new AuthenticationEntryPoint() {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.getWriter().append("Not authenticated");
httpServletResponse.setStatus(401);
}
};
}
然后在配置中使用它:
http
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint())
我为其他处理程序做了同样的事情:
and I did the same for the other handlers :
@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("ADMIN");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.successHandler(successHandler())
.failureHandler(failureHandler())
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
.authenticationEntryPoint(authenticationEntryPoint())
.and()
.csrf().csrfTokenRepository(csrfTokenRepository()).and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class)
;
}
private AuthenticationSuccessHandler successHandler() {
return new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
httpServletResponse.getWriter().append("OK");
httpServletResponse.setStatus(200);
}
};
}
private AuthenticationFailureHandler failureHandler() {
return new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.getWriter().append("Authentication failure");
httpServletResponse.setStatus(401);
}
};
}
private AccessDeniedHandler accessDeniedHandler() {
return new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletResponse.getWriter().append("Access denied");
httpServletResponse.setStatus(403);
}
};
}
private AuthenticationEntryPoint authenticationEntryPoint() {
return new AuthenticationEntryPoint() {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.getWriter().append("Not authenticated");
httpServletResponse.setStatus(401);
}
};
}
private Filter csrfHeaderFilter() {
return new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class
.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie == null || token != null
&& !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
};
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
这篇关于自定义登录表单。配置Spring安全性以获取JSON响应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!