英文:
Server not responding at login request with Spring Security
问题
以下是翻译好的内容:
我正在使用Spring Security
和JWT
来保护一个REST API(我没有使用Spring Boot
)。
当我尝试发送认证请求(/login
)到我的REST API时,Postman上显示无法获取任何响应
。
这是我的JWT过滤器:
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
...
public AuthenticationFilter(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
try {
... // 从请求中获取凭证
return getAuthenticationManager().authenticate(new UsernamePasswordAuthenticationToken(credentials.login, credentials.password));
}
catch (IOException e) { throw new RuntimeException(e); }
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
... // 生成jwtToken
response.setHeader("Authorization", jwtToken);
}
}
当我调试我的应用时,一切都正常工作,并且successfulAuthentication
方法被执行,我得到正确的令牌插入到请求的头部response.setHeader("Authorization", jwtToken);
。
但之后就好像我的REST API(或Spring Security或Tomcat)没有发送任何响应回来!
这是安全配置:
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()));
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
...
}
对于除了/login
之外的其他HTTP请求,在Postman中我得到了(403)的HTML响应,而不是JSON响应。
<!doctype html>
<html lang="en">
<head>
<title>HTTP Status 403 – Forbidden</title>
...
所以为什么我的服务器没有响应/login
请求?为什么Spring Security没有为所有HTTP请求发送JSON响应?
/login
请求之后的日志:
DEBUG o.s.security.web.FilterChainProxy - /login 在附加过滤器链的第1个位置中;触发过滤器: 'WebAsyncManagerIntegrationFilter'
DEBUG o.s.security.web.FilterChainProxy - /login 在附加过滤器链的第2个位置中;触发过滤器: 'SecurityContextPersistenceFilter'
DEBUG o.s.security.web.FilterChainProxy - /login 在附加过滤器链的第3个位置中;触发过滤器: 'HeaderWriterFilter'
DEBUG o.s.security.web.FilterChainProxy - /login 在附加过滤器链的第4个位置中;触发过滤器: 'LogoutFilter'
DEBUG o.s.security.web.util.matcher.OrRequestMatcher - 尝试使用Ant匹配器 [模式='/logout', GET] 进行匹配
DEBUG o.s.security.web.util.matcher.AntPathRequestMatcher - 请求 'POST /login' 不匹配 'GET /logout'
DEBUG o.s.security.web.util.matcher.OrRequestMatcher - 尝试使用Ant匹配器 [模式='/logout', POST] 进行匹配
DEBUG o.s.security.web.util.matcher.AntPathRequestMatcher - 正在检查请求匹配情况 : '/login'; 与 '/logout' 不匹配
DEBUG o.s.security.web.util.matcher.OrRequestMatcher - 尝试使用Ant匹配器 [模式='/logout', PUT] 进行匹配
DEBUG o.s.security.web.util.matcher.AntPathRequestMatcher - 请求 'POST /login' 不匹配 'PUT /logout'
DEBUG o.s.security.web.util.matcher.OrRequestMatcher - 尝试使用Ant匹配器 [模式='/logout', DELETE] 进行匹配
DEBUG o.s.security.web.util.matcher.AntPathRequestMatcher - 请求 'POST /login' 不匹配 'DELETE /logout'
DEBUG o.s.security.web.util.matcher.OrRequestMatcher - 没有找到匹配项
DEBUG o.s.security.web.FilterChainProxy - /login 在附加过滤器链的第5个位置中;触发过滤器: 'JwtAuthenticationFilter'
DEBUG o.s.security.web.util.matcher.AntPathRequestMatcher - 正在检查请求匹配情况 : '/login'; 与 '/login' 匹配
DEBUG security.JwtAuthenticationFilter - 请求正在处理认证
DEBUG o.s.security.authentication.ProviderManager - 使用 o.s.security.authentication.dao.DaoAuthenticationProvider 进行认证尝试
DEBUG o.s.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler - 为共享的EntityManager调用创建新的EntityManager
Hibernate: select ...
DEBUG o.s.security.web.header.writers.HstsHeaderWriter - 未注入HSTS头,因为它与请求匹配器 o.s.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@5b319bff 不匹配
DEBUG o.s.security.web.context.SecurityContextPersistenceFilter - 现在已清除SecurityContextHolder,因为请求处理已完成
英文:
I am securing a REST API with Spring Security
and JWT
(i'm not using Spring Boot
).
When i try to send an authentication request (/login
) to my REST API i got Could not get any response
on Postman
Here is my JWT filter
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
...
public AuthenticationFilter(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
try {
... // getting the credentials from the request
return getAuthenticationManager().authenticate(new UsernamePasswordAuthenticationToken(credentials.login, credentials.password));
}
catch (IOException e) { throw new RuntimeException(e); }
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
... // generating the jwtToken;
response.setHeader("Authorization", jwtToken);
}
}
When i am debugging my app everything works fine and the successfulAuthentication
method is executed and i get the right token inserted in the header request response.setHeader("Authorization", jwtToken);
.
But after that it's like my REST API (or Spring Security or Tomcat) don't send any response back !
Here is the security config :
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()));
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
...
}
For other HTTP requests other than /login
i got a (403) HTML response in Postman and NOT a JSON response.
<!doctype html>
<html lang="en">
<head>
<title>HTTP Status 403 – Forbidden</title>
...
So Why my server is not responding at /login
request ? and why Spring security is not sending JSON response for all the http requests ?
Logs after /login
request :
DEBUG o.s.security.web.FilterChainProxy - /login at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
DEBUG o.s.security.web.FilterChainProxy - /login at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
DEBUG o.s.security.web.FilterChainProxy - /login at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
DEBUG o.s.security.web.FilterChainProxy - /login at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
DEBUG o.s.security.web.util.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/logout', GET]
DEBUG o.s.security.web.util.matcher.AntPathRequestMatcher - Request 'POST /login' doesn't match 'GET /logout'
DEBUG o.s.security.web.util.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/logout', POST]
DEBUG o.s.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/login'; against '/logout'
DEBUG o.s.security.web.util.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/logout', PUT]
DEBUG o.s.security.web.util.matcher.AntPathRequestMatcher - Request 'POST /login' doesn't match 'PUT /logout'
DEBUG o.s.security.web.util.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/logout', DELETE]
DEBUG o.s.security.web.util.matcher.AntPathRequestMatcher - Request 'POST /login' doesn't match 'DELETE /logout'
DEBUG o.s.security.web.util.matcher.OrRequestMatcher - No matches found
DEBUG o.s.security.web.FilterChainProxy - /login at position 5 of 11 in additional filter chain; firing Filter: 'JwtAuthenticationFilter'
DEBUG o.s.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/login'; against '/login'
DEBUG security.JwtAuthenticationFilter - Request is to process authentication
DEBUG o.s.security.authentication.ProviderManager - Authentication attempt using o.s.security.authentication.dao.DaoAuthenticationProvider
DEBUG o.s.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler - Creating new EntityManager for shared EntityManager invocation
Hibernate: select ...
DEBUG o.s.security.web.header.writers.HstsHeaderWriter - Not injecting HSTS header since it did not match the requestMatcher o.s.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@5b319bff
DEBUG o.s.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed
答案1
得分: 0
以下是翻译好的内容:
首先要讨论的是,你收到的响应不是JSON(对于所有其他请求)。首先,URL端点上的方法应该被分配以生成你正在请求的内容。而你想要的内容,可以在Postman中的头部中提到,对于JSON,它应该是Accepts: Application/json
。
另一件事是为什么服务器没有响应你的登录请求,原因是你的情况下应该有最佳配置。
http.csrf().disable()
//你可以给出你想要允许的请求
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()));
英文:
The very first things, I will talk about the response the that you get is not in Json(For all other requests). See, first the method at the url endpoint should be assigned to produce the content that you're requesting. And the content that you want, you can mention in the headers in postman for json it should be Accepts : Application/json
The other thing is why the server is not responding to your login request is that, best configuration should be in your case.
http.csrf().disable()
//You can give the request that you want to allow
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()));
答案2
得分: 0
对于服务器不响应的问题,这是一个愚蠢的错误,在我的属性文件中,我把授权头的名称放在了双引号中:
security.token.header-string="Authorization"
而应该是:
security.token.header-string=Authorization
对于服务器未以 JSON 格式响应的部分,我遵循了这个答案。
因此,我不得不实现自定义的 authenticationEntryPoint
、accessDeniedHandler
、authenticationSuccessHandler
和 authenticationFailureHandler
,如下所示:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilter(getJwtAuthenticationFilter())
.exceptionHandling()
.authenticationEntryPoint((request, response, exception) -> {
response.setStatus(401);
})
.accessDeniedHandler((request, response, exception) -> {
response.setStatus(403);
});
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
使用 JwtAuthenticationFilter
:
@Bean
private JwtAuthenticationFilter getJwtAuthenticationFilter() throws Exception {
JwtAuthenticationFilter filter = new JwtAuthenticationFilter(authenticationManager());
filter.setFilterProcessesUrl("/api/login");
//filter.setAuthenticationSuccessHandler((request, response, authentication) -> {
// response.setStatus(200);
//});
filter.setAuthenticationFailureHandler((request, response, exception) -> {
response.setStatus(401);
});
return filter;
}
我已经注释掉了 filter.setAuthenticationSuccessHandler(...)
这一行,因为我已经在我的 JwtAuthenticationFilter
类中覆盖了 successfulAuthentication
方法。
英文:
For the issue with the server not responding it was stupid mistake, In my properties file i have put the Authorization header name with double quote
security.token.header-string="Authorization"
Instead of
security.token.header-string=Authorization
For the part which the server not responding with JSON format i follow this answer
So i had to implement a custom authenticationEntryPoint
, accessDeniedHandler
, authenticationSuccessHandler
and authenticationFailureHandler
like bellow :
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.
authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilter(getJwtAuthenticationFilter())
.exceptionHandling()
.authenticationEntryPoint((request, response, exception) -> {
response.setStatus(401);
})
.accessDeniedHandler((request, response, exception) -> {
response.setStatus(403);
})
;
http.
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
With the JwtAuthenticationFilter
@Bean
private JwtAuthenticationFilter getJwtAuthenticationFilter() throws Exception {
JwtAuthenticationFilter filter = new JwtAuthenticationFilter(authenticationManager());
filter.setFilterProcessesUrl("/api/login");
//filter.setAuthenticationSuccessHandler((request, response, authentication) -> {
// response.setStatus(200);
//});
filter.setAuthenticationFailureHandler((request, response, exception) -> {
response.setStatus(401);
});
return filter;
}
I have comment the line filter.setAuthenticationSuccessHandler(...)
because it will not be invoked since i already override the successfulAuthentication
method on my JwtAuthenticationFilter
class.
1: https://stackoverflow.com/a/32507217/1383538
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论