无法解决403 Forbidden当使用开放的API与 Spring Boot 和swagger和允许其他请求

k75qkfdt  于 2024-01-08  发布在  Spring
关注(0)|答案(2)|浏览(363)

我在这里看了很多问题,但没有适合我的解决方案。
我有一个服务,我想运行开放的API和swagger用户界面上。
我在没有安全性的情况下运行它们没有问题,当我尝试使用下面的代码添加基本身份验证时,它可以按照我的意愿工作,但它也将我所有其他的API置于相同的身份验证之后,这是我不希望的。我想我可以添加一个额外的requestMatcher,如下所示:

  1. @Bean
  2. public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
  3. httpSecurity.authorizeHttpRequests()
  4. .requestMatchers("/swagger-ui/**",
  5. "/v3/api-docs/**",
  6. "/v3/api-docs",
  7. "/swagger-ui.html")
  8. .hasRole("USER")
  9. .requestMatchers("/**").permitAll()
  10. .and()
  11. .httpBasic(Customizer.withDefaults());
  12. return httpSecurity.build();
  13. }

字符串
当我这样做的时候,我的其他API是可以访问的,没有安全性,正如我所期望的,当我去swagger ui我得到弹出窗口,我可以登录,但然后get请求到/v3/api-config失败与一个403禁止错误或400错误,这取决于所有的requestMatcher位于何处。
正确的方法是什么?
我希望 Swagger 的API的背后基本认证,但所有其他API的任何人都可以访问。
我找不到现有的答案来帮助解决这个问题。
我目前的安全配置如下,这阻止了一切,并允许在身份验证后 swagger :

  1. @Configuration
  2. @EnableWebSecurity
  3. public class SecurityConfig {
  4. public static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
  5. @Bean
  6. public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
  7. httpSecurity.authorizeHttpRequests()
  8. .requestMatchers("/swagger-ui/**",
  9. "/v3/api-docs/**",
  10. "/v3/api-docs",
  11. "/swagger-ui.html")
  12. .hasRole("USER")
  13. .and()
  14. .httpBasic(Customizer.withDefaults());
  15. return httpSecurity.build();
  16. }
  17. @Bean
  18. public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
  19. UserDetails user = User.withUsername("user")
  20. .password(passwordEncoder.encode("password"))
  21. .roles("USER")
  22. .build();
  23. return new InMemoryUserDetailsManager(user);
  24. }
  25. @Bean
  26. public PasswordEncoder passwordEncoder() {
  27. return new BCryptPasswordEncoder();
  28. }
  29. }

smtd7mpg

smtd7mpg1#

为了只在swagger上进行身份验证,我实现了两个不同的过滤器链。一个用于swagger,另一个用于整个服务。这是我的SecurityConfig的样子:

  1. http.addFilterBefore(ssoAuthFilter, UsernamePasswordAuthenticationFilter.class);
  2. http.authorizeHttpRequests(c ->
  3. c.requestMatchers("/swagger-ui").hasAnyAuthority("STAFF","ADMIN","CASHIER","TENANTADMIN")
  4. .requestMatchers("/api-docs/swagger-config").permitAll()
  5. .requestMatchers("/swagger-ui/login").permitAll()
  6. .requestMatchers("/actuator/health").permitAll()
  7. .requestMatchers("/actuator/prometheus").permitAll()
  8. .requestMatchers("/api-docs").hasAnyAuthority("STAFF","ADMIN", "CASHIER", "TENANTADMIN")
  9. .requestMatchers("/api-docs/eod").hasAnyAuthority("ADMIN", "STAFF", "TENANTADMIN", "CASHIER")
  10. .requestMatchers("/api-docs/statistics").hasAnyAuthority("ADMIN", "STAFF", "CASHIER", "TENANTADMIN")
  11. .dispatcherTypeMatchers( DispatcherType.ERROR ).permitAll()
  12. .anyRequest().authenticated()
  13. );
  14. http.formLogin(form -> form
  15. .loginPage("/swagger-ui/login")
  16. .loginProcessingUrl("/swagger-ui/login")
  17. .successHandler(myAuthenticationSuccessHandler())
  18. .failureHandler(myAuthenticationFailureHandler())
  19. .permitAll()
  20. );
  21. http.addFilterBefore(swaggerAuthFilter, UsernamePasswordAuthenticationFilter.class).exceptionHandling(handling -> {
  22. handling.accessDeniedHandler(accessDeniedHandler());
  23. handling.authenticationEntryPoint(new CustomHttp403ForbiddenEntryPoint());
  24. });
  25. http.authenticationProvider(swaggerAuthProvider);

字符串
正如你所看到的,我已经为我的整个服务和SwaggerAuthFilter的 Swagger 的目的只SSOAuthFilter。
您还需要一个AuthenticationProvider:

  1. public class SwaggerAuthProvider implements AuthenticationProvider {
  2. private UserService userService;
  3. @Autowired
  4. public SwaggerAuthProvider(UserService userService) {
  5. this.userService = userService;
  6. }
  7. @Override
  8. public Authentication authenticate(Authentication authentication) throws AuthenticationException {
  9. String username = authentication.getName();
  10. String password = authentication.getCredentials().toString();
  11. Optional<WhoAmIResponse> whoAmIResponse = userService.authenticate(new LoginRequest(username,password));
  12. if (whoAmIResponse.isEmpty()) {
  13. log.debug("Authentication failed. Invalid credentials.");
  14. throw new BadCredentialsException("Authentication failed. Invalid credentials.");
  15. }
  16. // Create a new authenticated token with the JWT token
  17. return new JwtAuthenticationToken(
  18. whoAmIResponse.get().getToken(),
  19. username, password,
  20. List.of(
  21. new SimpleGrantedAuthority(
  22. whoAmIResponse.get().getType().toUpperCase()
  23. )
  24. )
  25. );
  26. }
  27. @Override
  28. public boolean supports(Class<?> authentication) {
  29. return authentication.isAssignableFrom(JwtAuthenticationToken.class);
  30. }
  31. }


这就是我的SwaggerAuthFilter看起来的样子:

  1. public class SwaggerAuthFilter extends OncePerRequestFilter {
  2. private final MyUserDetailsService userDetailsService;
  3. private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
  4. @Autowired
  5. public SwaggerAuthFilter(MyUserDetailsService userDetailsService) {
  6. this.userDetailsService = userDetailsService;
  7. }
  8. private String checkCookie(Cookie tokenCookie){
  9. String token = tokenCookie.getValue();
  10. if(token == null || token.length() < 7){
  11. log.debug("token problem with length {}", token.length());
  12. }
  13. if(token.startsWith("Bearer"))
  14. return token.substring(7);
  15. return token;
  16. }
  17. @Override
  18. protected void doFilterInternal(HttpServletRequest req,
  19. HttpServletResponse res,
  20. FilterChain chain) throws IOException, ServletException {
  21. String url = req.getRequestURI();
  22. if(
  23. (!req.getRequestURI().contains("swagger-ui") &&
  24. !req.getRequestURI().contains("api-docs")) ||
  25. req.getRequestURI().contains("login")
  26. ){
  27. log.debug("request with uri: {} was not associated with swagger filter", url);
  28. chain.doFilter(req, res);
  29. return;
  30. }
  31. Cookie tokenCookie = WebUtils.getCookie(req, "token");
  32. if(tokenCookie == null){
  33. log.debug("token cookie is null, redirecting to login page");
  34. redirectStrategy.sendRedirect(req, res, "/swagger-ui/login");
  35. return;
  36. }
  37. String token = checkCookie(tokenCookie);
  38. UsernamePasswordAuthenticationToken authentication;
  39. try {
  40. UserDetails userDetails = userDetailsService.loadUserByUsername(token);
  41. if(userDetails == null){
  42. log.debug("user retrieved by token in cookie is null, redirecting to login page");
  43. tokenCookie.setMaxAge(0);
  44. res.addCookie(tokenCookie);
  45. redirectStrategy.sendRedirect(req, res, "/swagger-ui/login");
  46. return;
  47. }
  48. authentication =
  49. new JwtAuthenticationToken(token,userDetails,
  50. null,
  51. userDetails.getAuthorities());
  52. authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(req));
  53. } catch (AuthException | NullPointerException | IllegalArgumentException e) {
  54. log.debug("cannot authorize because {}", e.getLocalizedMessage());
  55. authentication = null;
  56. }
  57. SecurityContextHolder.getContext().setAuthentication(authentication); // set the user here
  58. chain.doFilter(req, res);
  59. }
  60. }


希望这能帮助您解决问题!

展开查看全部
ffx8fchx

ffx8fchx2#

这是一个奇怪的例子。在我工作的服务中,我们有一个端点叫做/error
Spring有一个默认的错误控制器,它也有一个名为/error的端点。
这在以前从来都不是问题,但是很明显,随着spring-security的加入,有些事情发生了变化,这就成了一个问题。
Spring不知道该使用哪个资源来做它想做的事情,所以选择了错误的资源,这导致了奇怪的行为。
有趣的是,没有实际的错误。
在调试日志中,我注意到我们出于某种原因调用了/error(仍然无法弄清楚这一点),并且有一个警告说预期的主体不在请求中,这很奇怪,因为我们没有调用该资源,并且失败的GET请求没有主体。
直到我们打开跟踪日志,我们才看到有两个冲突的/error资源,spring正在挑选资源,而不是它正在寻找的资源。
我们将错误资源重命名为其他内容,所有问题都消失了。
我觉得奇怪的是,当这种情况发生时,spring并没有正确地抱怨。
无论如何,它现在正在工作。

相关问题