spring security的permitAll()在JWT认证过滤器中不起作用。

huangapple go评论217阅读模式
英文:

spring security permitAll() not working for JWT Authentication filter

问题

问题出在使用了自定义的JWT身份验证过滤器的应用程序上,该过滤器扩展了UsernamePasswordAuthenticationFilter,该过滤器接受用户凭据并返回一个长期有效的JWT。

问题似乎出在permitAll()上,它应该绕过自定义的授权过滤器。然而,在调试模式下,我可以看到首先调用了自定义的JwtAuthorizationFilter而不是自定义的JwtAuthenticationFilter过滤器,这最终导致了403禁止访问的响应。

请注意.antMatchers(HttpMethod.POST, "/login").permitAll()这一行。由于在用户尚未登录时尚未生成JWT,因此应该可以访问/login端点而无需JWT。

以下是您的代码片段:

  1. // JwtAuthenticationFilter.java
  2. public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
  3. // ...
  4. }
  5. // JwtAuthorizationFilter.java
  6. public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
  7. // ...
  8. }
  9. // SecurityConfiguration.java
  10. @Configuration
  11. @EnableWebSecurity
  12. public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
  13. // ...
  14. }

请求和响应的截图没有完整显示,所以如果您需要关于问题的更多信息,请提供完整的请求和响应内容。

英文:

The issue is with the app uses custom JWT authentication filter which extends UsernamePasswordAuthenticationFilter which accepts user credentials and generates a long-lived JWT in return.

The issue seems to be with permitAll() which should bypass custom Authorization filter.However in debug mode I could see call to custom JwtAuthorizationFilter first instead of custom JwtAuthenticationFilter Filter which eventually results with 403 forbidden Access denied response.

Note the .antMatchers(HttpMethod.POST, "/login").permitAll() line. /login endpoint should be accessible without JWT since the JWT has not yet been generated when the user has not yet logged in.

Below is my code

> JwtAuthenticationFilter.java

  1. public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
  2. //
  3. private AuthenticationManager authenticationManager;
  4. private final static UrlPathHelper urlPathHelper = new UrlPathHelper();
  5. public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
  6. this.authenticationManager = authenticationManager;
  7. setFilterProcessesUrl("/login");
  8. }
  9. /**
  10. * Trigger when we issue POST request to login / we also need to pass in
  11. * {"username: " username, "password": password} in the request body
  12. */
  13. @Override
  14. public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
  15. throws AuthenticationException {
  16. // Grab credentials and map them to login viewmodel
  17. LoginViewModel credentials = null;
  18. try {
  19. credentials = new ObjectMapper().readValue(request.getInputStream(), LoginViewModel.class);
  20. } catch (IOException e) {
  21. e.printStackTrace();
  22. }
  23. // Create login token
  24. UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
  25. credentials.getUsername(), credentials.getPassword(), new ArrayList<>());
  26. // Authenciate user
  27. Authentication auth = authenticationManager.authenticate(authenticationToken);
  28. return auth;
  29. }
  30. @Override
  31. protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
  32. Authentication authResult) throws IOException, ServletException {
  33. // Grab principal
  34. UserPrincipal principal = (UserPrincipal) authResult.getPrincipal();
  35. // Create JWT Token
  36. String token = JWT.create().withSubject(principal.getUsername())
  37. .withExpiresAt(new Date(System.currentTimeMillis() + JwtProperties.EXPIRATION_TIME))
  38. .sign(HMAC512(JwtProperties.SECRET.getBytes()));
  39. // add token in response
  40. response.addHeader(JwtProperties.HEADER_STRING, JwtProperties.TOKEN_PREFIX + token);
  41. }
  42. @Override
  43. protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
  44. AuthenticationException failed) throws IOException, ServletException {
  45. logger.debug("failed authentication while attempting to access "
  46. + urlPathHelper.getPathWithinApplication((HttpServletRequest) request));
  47. // Add more descriptive message
  48. response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed");
  49. }
  50. }

> JwtAuthorizationFilter.java

  1. public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
  2. private UserRepository userRepository;
  3. public JwtAuthorizationFilter(AuthenticationManager authenticationManager, UserRepository userRepository) {
  4. super(authenticationManager);
  5. this.userRepository = userRepository;
  6. }
  7. @Override
  8. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
  9. //Read the Authorization header, where the JWT token should be
  10. String header = request.getHeader(JwtProperties.HEADER_STRING);
  11. //If header does not contain BEARER or is null delegate to Spring impl and exit
  12. if (header == null || !header.startsWith(JwtProperties.TOKEN_PREFIX)) {
  13. chain.doFilter(request, response);
  14. return;
  15. }
  16. // If header is present, try grab user principal from db and perform authorization
  17. Authentication authentication = getUsernamePasswordAuthentication(request);
  18. SecurityContextHolder.getContext().setAuthentication(authentication);
  19. // Continue filter execution
  20. chain.doFilter(request, response);
  21. }
  22. private Authentication getUsernamePasswordAuthentication(HttpServletRequest request){
  23. String token = request.getHeader(JwtProperties.HEADER_STRING)
  24. .replace(JwtProperties.TOKEN_PREFIX, "");
  25. if(token !=null){
  26. //parse the token validate it
  27. String userName = JWT.require(Algorithm.HMAC512(JwtProperties.SECRET.getBytes()))
  28. .build()
  29. .verify(token)
  30. .getSubject();
  31. // Search in the DB if we find the user by token subject(username)
  32. // If so, then grab user details and create auth token using username, pass, authorities/roles
  33. if(userName != null){
  34. User user = userRepository.findByUsername(userName);
  35. UserPrincipal principal = new UserPrincipal(user);
  36. UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userName, null, principal.getAuthorities());
  37. return authenticationToken;
  38. }
  39. return null;
  40. }
  41. return null;
  42. }
  43. }

> SecurityConfiguration.java

  1. Configuration
  2. @EnableWebSecurity
  3. public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
  4. private UserPrincipalDetailsService userPrincipalDetailsService;
  5. private UserRepository userRepository;
  6. public SecurityConfiguration(UserPrincipalDetailsService userPrincipalDetailsService,
  7. UserRepository userRepository) {
  8. this.userPrincipalDetailsService = userPrincipalDetailsService;
  9. this.userRepository = userRepository;
  10. }
  11. @Override
  12. protected void configure(AuthenticationManagerBuilder auth) {
  13. auth.authenticationProvider(authenticationProvider());
  14. }
  15. @Override
  16. protected void configure(HttpSecurity http) throws Exception {
  17. http
  18. // remove csrf state in session because in jwt do not need them
  19. .csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
  20. .authorizeRequests().antMatchers(HttpMethod.POST, "/login").permitAll()
  21. .antMatchers("/api/public/management/*").hasRole("MANAGER").antMatchers("/api/public/admin/*")
  22. .hasRole("ADMIN").anyRequest().authenticated().and()
  23. // add jwt filters (1. authentication, 2. authorization_)
  24. .addFilter(new JwtAuthenticationFilter(authenticationManager()))
  25. .addFilter(new JwtAuthorizationFilter(authenticationManager(), this.userRepository));
  26. // configure access rules
  27. }
  28. @Bean
  29. DaoAuthenticationProvider authenticationProvider() {
  30. DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
  31. daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
  32. daoAuthenticationProvider.setUserDetailsService((UserDetailsService) this.userPrincipalDetailsService);
  33. return daoAuthenticationProvider;
  34. }
  35. @Bean
  36. PasswordEncoder passwordEncoder() {
  37. return new BCryptPasswordEncoder();
  38. }
  39. }

Request,Response

spring security的permitAll()在JWT认证过滤器中不起作用。

Can someone suggest whats wrong here..Appreciate your help..Thanks in advance..!!!

答案1

得分: 1

似乎你的路径是错误的。当你查看你的请求体时,你会发现路径显示为:/login%0A。这看起来你的URL末尾多了一个额外的字符。尝试在Postman中重新编写URL。

英文:

It seems that your path is wrong. When you look at your body you can see that the path shows following: /login%0A. This seems that you have an extra character at the end of your URL. Just try to rewrite the URL in Postman.

答案2

得分: 1

请考虑使用BasicAuthenticationFilter中的shouldNotFilter方法。它继承自OncePerRequestFilter,因此您可以在过滤器类中按如下方式使用:

  1. @Override
  2. protected boolean shouldNotFilter(HttpServletRequest request) {
  3. // 在此处编写代码
  4. }
英文:

please consider to use shouldNotFilter method from BasicAuthenticationFilter. It extends OncePerRequestFilter so you can use it in filtering class as below:

  1. @Override
  2. protected boolean shouldNotFilter(HttpServletRequest request) {
  3. // code here
  4. }
  5. </details>

huangapple
  • 本文由 发表于 2020年10月5日 15:02:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/64203795.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定