Spring boot api给出403禁止错误

Posted

技术标签:

【中文标题】Spring boot api给出403禁止错误【英文标题】:Spring boot api gives 403 forbidden error 【发布时间】:2021-09-04 22:24:56 【问题描述】:

我有一个带有 mongo 数据库和 spring security 作为依赖项的 spring boot 应用程序。 它有两个服务,第一个用于身份验证,第二个用于应用程序资源(实体、服务控制器)。 这是我在身份验证服务中的配置类:

@Configuration
@EnableWebSecurity
public class AuthServerSecurityConfig extends WebSecurityConfigurerAdapter 

@Override
@Bean
protected UserDetailsService userDetailsService() 
    return new MongoUserDetailsService();


@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception 
    auth.userDetailsService(userDetailsService());


@Override
protected void configure(HttpSecurity http) throws Exception 
    http.csrf().disable()
    .authorizeRequests().anyRequest().authenticated();
    System.out.println("auth");


@Override
public void configure(WebSecurity web) throws Exception 
    super.configure(web);


@Bean(name="authenticationManager")
@Lazy
@Override
public AuthenticationManager authenticationManagerBean() throws Exception 
    return super.authenticationManagerBean();


这是其余控制器:

@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping(value = "/api/users")
public class UserController 

@Autowired
UserServiceImpl userServiceImpl;

//Getting all users
@GetMapping(value = "")
public List<UserDTO> getAllUsers() 
    return userServiceImpl.getAllUsers();
    


//Getting a user by ID
@GetMapping(value = "/profil/userId")
public UserDTO getUserById(@PathVariable String userId) 
    return userServiceImpl.getUserById(userId);


//Getting a user by Username
@GetMapping(value = "/profil/username/username")
public UserDTO getUserByUsernameOrEmail(String username) 
    return userServiceImpl.getUserByUsernameOrEmail(username);


//Logout user and delete token
@PostMapping("/logout")
public void logout(HttpServletRequest request) 
     userServiceImpl.logout(request);
    

我将配置方法更改为:

@Override
protected void configure(HttpSecurity http) throws Exception 
    http
    .csrf().disable()
    .authorizeRequests() // authorize
    .anyRequest().authenticated() // all requests are authenticated
    .and()
    .httpBasic();

    http.cors();
    

现在我在访问受保护资源时得到 401 未授权。问题是现在即使我在请求标头中发送正确的不记名令牌,我仍然得到 401 未授权“访问此资源需要完全身份验证”

更新: 我将项目架构从微服务更改为一个简单的 Spring Boot 项目。 这是“AuthServerSecurityConfig”类的新代码

@Configuration
public class AuthServerSecurityConfig extends WebSecurityConfigurerAdapter 

@Override
@Bean
protected UserDetailsService userDetailsService() 
    return new MongoUserDetailsService();


@Autowired
BCryptPasswordEncoder passwordEncoder;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception 
    //auth.userDetailsService(userDetailsService());
    auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder);


@Override
protected void configure(HttpSecurity http) throws Exception 
    http
    .csrf().disable()
    .anonymous().disable()
    .authorizeRequests()
    .antMatchers("/oauth/token").permitAll().and()
    .httpBasic();

    http.cors();


@Override
public void configure(WebSecurity web) throws Exception 
    super.configure(web);


@Bean(name="authenticationManager")
@Lazy
@Override
public AuthenticationManager authenticationManagerBean() throws Exception 
    return super.authenticationManagerBean();


还有这个“ResourceServerConfig”代码:

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter 


@Autowired private ResourceServerTokenServices tokenServices;

@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception 
    resources.resourceId("foo").tokenServices(tokenServices);


@Override
public void configure(HttpSecurity http) throws Exception 
    
        http
        .authorizeRequests() // authorize
        .antMatchers("/oauth/**").permitAll();
        
        http
        .authorizeRequests().antMatchers("/api/**").authenticated();
        
        http
        .headers().addHeaderWriter(new HeaderWriter() 
        @Override
        public void writeHeaders(HttpServletRequest request, HttpServletResponse response) 
            response.addHeader("Access-Control-Allow-Origin", "*");
            if (request.getMethod().equals("OPTIONS")) 
                response.setHeader("Access-Control-Allow-Methods", request.getHeader("Access-Control-Request-Method"));
                response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
            
        
    );


当我尝试访问受保护的资源时,我得到“错误”:“未经授权”, “error_description”:“访问此资源需要完全身份验证”,这是正常行为。问题是现在我无法登录以获取用户 aceess_token。

访问此端点“http://localhost:8080/oauth/token?grant_type=password&username=user&password=user”时,我得到“401 未授权”。

这些是默认的 init 用户凭据,该用户存在于我的 mongodatabase 中,密码格式正确且加密,密码以“$2a”开头,包含“60”个字符。

我进入“编码密码看起来不像 BCrypt 身份验证失败:尝试登录时,控制台中的密码与存储的值不匹配。

【问题讨论】:

你的休息控制器里有什么?有@PreAuthorize 注释吗? 我有一个 @PreAuthorize 注释,但在我试图访问的其余控制器上没有:我更新了我的问题!! 您在哪个端点上得到 403 响应?您是否有可以正常工作的端点?请与调试分享完整的堆栈错误 在“api/users”上。当我删除“.authorizeRequests().anyRequest().authenticated()”时它们工作正常 您是否对这些请求使用经过身份验证的会话? 【参考方案1】:

这是一个使用 jwt 令牌检查配置 spring security 的示例: 您可以将数据源从 h2 更改为 mongodb,并找到我的 repo 中使用的过滤器和提供程序:

https://github.com/e2rabi/sbs-user-management/tree/main/sbs-user-management



@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
@FieldDefaults(level = PRIVATE, makeFinal = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter 

    private static final RequestMatcher PUBLIC_URLS = new OrRequestMatcher(
             // Put your public API here 
            new AntPathRequestMatcher("/public/**"),
            new AntPathRequestMatcher("/h2-console/**"),

    );
    private static final RequestMatcher PROTECTED_URLS = new NegatedRequestMatcher(PUBLIC_URLS);

    TokenAuthenticationProvider provider;

    SecurityConfig(final TokenAuthenticationProvider provider) 
        super();
        this.provider = requireNonNull(provider);
    


   @Override
    protected void configure(final AuthenticationManagerBuilder auth) 
        auth.authenticationProvider(provider);
    
    @Override
    public void configure(final WebSecurity web) 
        web.ignoring().requestMatchers(PUBLIC_URLS);
    

    @Override
    protected void configure(final HttpSecurity http) throws Exception 
        http
                .sessionManagement()
                .sessionCreationPolicy(STATELESS)
                .and()
                .exceptionHandling()
                // this entry point handles when you request a protected page and you are not yet
                // authenticated
                .defaultAuthenticationEntryPointFor(forbiddenEntryPoint(), PROTECTED_URLS)
                .and()
                .authenticationProvider(provider)
                .addFilterBefore(restAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .authorizeRequests()
                .requestMatchers(PROTECTED_URLS)
                .authenticated()
                .and()
                .csrf().disable()
                .cors().disable()
                .formLogin().disable()
                .httpBasic().disable()
                .logout().disable();

                // h2 console config
                http.headers().frameOptions().sameOrigin();
                // disable page caching
                http.headers().cacheControl();
    

    @Bean
    TokenAuthenticationFilter restAuthenticationFilter() throws Exception 
        final TokenAuthenticationFilter filter = new TokenAuthenticationFilter(PROTECTED_URLS);
        filter.setAuthenticationManager(authenticationManager());
        filter.setAuthenticationSuccessHandler(successHandler());
        return filter;
    

    @Bean
    SimpleUrlAuthenticationSuccessHandler successHandler() 
        final SimpleUrlAuthenticationSuccessHandler successHandler = new SimpleUrlAuthenticationSuccessHandler();
        successHandler.setRedirectStrategy(new NoRedirectStrategy());
        return successHandler;
    

    @Bean
    PasswordEncoder passwordEncoder()
        return new BCryptPasswordEncoder();
    

    @Bean
    FilterRegistrationBean disableAutoRegistration(final TokenAuthenticationFilter filter) 
        final FilterRegistrationBean registration = new FilterRegistrationBean(filter);
        registration.setEnabled(false);
        return registration;
    

    @Bean
    AuthenticationEntryPoint forbiddenEntryPoint() 
        return new HttpStatusEntryPoint(FORBIDDEN);
    

【讨论】:

原来我必须加密密码和客户端密码才能登录并获取令牌 我看到您正在使用 oauth2 授权代码工作流程保护您的 apis 我的解决方案是 jwt 不记名令牌身份验证【参考方案2】:

ResourceServerConfig 类文件中,将configure 方法中的代码更改为以下代码。

http
    .csrf().disable()
    .anonymous().disable()
    .authorizeRequests()
    .antMatchers("/oauth/token").permitAll().and()
    .httpBasic();

让我知道它是否有效。

【讨论】:

原来我必须加密密码和客户端密码才能登录并获取令牌

以上是关于Spring boot api给出403禁止错误的主要内容,如果未能解决你的问题,请参考以下文章

Jquery POST在spring mvc中给出403禁止错误

Yahoo Finance API 在旧 URL 和 User-Agent 上给出错误 403(禁止)

删除操作返回错误:出现意外错误(类型=禁止,状态=403)。禁止的

即使禁用 csrf,Spring security 403 禁止错误也会不断发生

Spring Boot - KeyCloak 指向 403 禁止

spring boot angular 2 post方法403错误