Spring Security 总是返回 403 被禁止,访问被拒绝

Posted

技术标签:

【中文标题】Spring Security 总是返回 403 被禁止,访问被拒绝【英文标题】:Spring Security Always returning 403 forbidden, Access denied 【发布时间】:2018-04-23 14:48:24 【问题描述】:

我想让管理员访问管理页面并执行管理操作,但是当我尝试通过设置 /admin/** 的 url 只能由具有角色 admin 的用户访问来做到这一点时,它返回 403 Forbidden,拒绝访问。但是我检查了用户的权限设置为 ROLE_ADMIN。我做错了什么?

用于用户登录的我的控制器

@RestController
public class UserController 

    @Autowired
    AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private AuthorityService authorityService;

    @Autowired
    private UserAuthorityService userAuthorityService;

    @Autowired
    TokenUtils tokenUtils;

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/api/login", method = RequestMethod.POST, produces = "text/html")
    public ResponseEntity<String> login(@RequestBody LoginDTO loginDTO) 
        try 
//          System.out.println(loginDTO.getUsername() + " " + loginDTO.getPassword());
            UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
                    loginDTO.getUsername(), loginDTO.getPassword());

            Authentication authentication = authenticationManager.authenticate(token);

            SecurityContextHolder.getContext().setAuthentication(authentication);

            UserDetails details = userDetailsService.loadUserByUsername(loginDTO.getUsername());

            return new ResponseEntity<String>(tokenUtils.generateToken(details), HttpStatus.OK);
         catch (Exception ex) 
            return new ResponseEntity<String>("Invalid login", HttpStatus.BAD_REQUEST);
        
    

    @RequestMapping(value = "/api/register", method = RequestMethod.POST, produces = "text/html")
    public ResponseEntity<String> register(@RequestBody RegisterDTO registerDTO) 
        try 
            System.out.println(registerDTO);
            User user = userService.findUserByUsername(registerDTO.getUsername());
//            // Check if user with that username exists
            if(user != null)
                // User with that username is found
                return new ResponseEntity<String>("User with that username exists", HttpStatus.BAD_REQUEST);
            
            // We need to save the user so his ID is generated
            User newUser = userService.saveUser(new User(registerDTO));

            UserAuthority userAuthority = userAuthorityService.save(new UserAuthority(newUser, authorityService.findOneByName("User")));

            Set<UserAuthority> authorities = new HashSet<>();
            authorities.add(userAuthority);

            newUser.setUserAuthorities(authorities);
            User savedUser = userService.save(newUser);
            return new ResponseEntity<String>("You have registered successfully with username " + savedUser.getUsername(), HttpStatus.OK);
         catch (Exception ex) 
            return new ResponseEntity<String>("Invalid register", HttpStatus.BAD_REQUEST);
        
    

我可以说我用邮递员测试了我的应用程序,登录和注册工作正常。当用户登录时,我可以使用正确的数据和用户权限获取令牌,但是为什么当我尝试访问 /admin/building/add url 时它返回 403 错误?

为管理页面添加建筑的我的控制器:

@RestController
public class BuildingController 

    @Autowired
    private BuildingService buildingService;

    @RequestMapping(value = "/admin/building/add", method = RequestMethod.POST, produces = "text/html")
    public ResponseEntity<String> addBuilding(@RequestBody BuildingDTO buildingDTO) 
        try
            Building newBuilding = new Building(buildingDTO);
            return new ResponseEntity<String>(newBuilding.getName(), HttpStatus.OK);
        catch (Exception ex) 
            return new ResponseEntity<String>("Data was not valid", HttpStatus.BAD_REQUEST);
        
    

我的 SecurityConfiguration.java

@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter 
    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    public void configureAuthentication(
            AuthenticationManagerBuilder authenticationManagerBuilder)
            throws Exception 

        authenticationManagerBuilder
                .userDetailsService(this.userDetailsService).passwordEncoder(
                        passwordEncoder());
    

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

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception 
        return super.authenticationManagerBean();
    

    @Bean
    public AuthenticationTokenFilter authenticationTokenFilterBean()
            throws Exception 
        AuthenticationTokenFilter authenticationTokenFilter = new AuthenticationTokenFilter();
        authenticationTokenFilter
                .setAuthenticationManager(authenticationManagerBean());
        return authenticationTokenFilter;
    

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception 
        httpSecurity
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
            .authorizeRequests()
                .antMatchers("/index.html", "/view/**", "/app/**", "/", "/api/login", "/api/register").permitAll()
                // defined Admin only API area 
                .antMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest()
                .authenticated()
                .and().csrf().disable();
                //if we use AngularJS on client side
//              .and().csrf().csrfTokenRepository(csrfTokenRepository()); 

        //add filter for adding CSRF token in the request 
        httpSecurity.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class);

        // Custom JWT based authentication
        httpSecurity.addFilterBefore(authenticationTokenFilterBean(),
                UsernamePasswordAuthenticationFilter.class);
    

    /**
     * If we use AngularJS as a client application, it will send CSRF token using 
     * name X-XSRF token. We have to tell Spring to expect this name instead of 
     * X-CSRF-TOKEN (which is default one)
     * @return
     */
    private CsrfTokenRepository csrfTokenRepository() 
          HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
          repository.setHeaderName("X-XSRF-TOKEN");
          return repository;
    
  

我应该提到我正在使用 Angularjs 作为前端,但即使这样我也可以登录并为该用户显示正确的权限。但由于某种原因,即使我以管理员身份登录,我也无法访问管理页面。

我还尝试了.hasAuthority("ROLE_ADMIN").hasRole("ROLE_ADMIN")(显示ROLE_ 的错误),所以我将其更改为.hasRole("ADMIN"),但它仍然无法正常工作。

在数据库中,admin 的角色保存为 ROLE_ADMIN。

【问题讨论】:

您在登录时完全绕过了 Spring Security。使用 Spring Security 来处理登录不要自己改造。 检查这里:***.com/questions/19468209/spring-security-403-error/… 【参考方案1】:

试试这样:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter 

    private static String REALM="MY_TEST_REALM";

    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception 
        auth.inMemoryAuthentication().withUser("bill").password("abc123").roles("ADMIN");
        auth.inMemoryAuthentication().withUser("tom").password("abc123").roles("USER");
    

    @Override
    protected void configure(HttpSecurity http) throws Exception 

      http.csrf().disable()
        .authorizeRequests()
        .antMatchers("/user/**").hasRole("ADMIN")
        .and().httpBasic().realmName(REALM).authenticationEntryPoint(getBasicAuthEntryPoint())
        .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);//We don't need sessions to be created.
    

    @Bean
    public CustomBasicAuthenticationEntryPoint getBasicAuthEntryPoint()
        return new CustomBasicAuthenticationEntryPoint();
    

    /* To allow Pre-flight [OPTIONS] request from browser */
    @Override
    public void configure(WebSecurity web) throws Exception 
        web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
    

完整的配置示例:Secure Spring REST API using Basic Authentication

【讨论】:

web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**"); 不推荐也不要使用。【参考方案2】:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter 

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.csrf().disable();// We don't need sessions to be created.
    


这是为我做的。现在我可以成功提交我的帖子请求了

【讨论】:

这会禁用 CSRF,而不是会话,不推荐。 owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)你的请求失败的原因是你没有提供 CSRF cookie/header【参考方案3】:

在 SecurityConfig 中试试这个:

.antMatchers("/api/admin").access("hasRole('ADMIN')")
.antMatchers("/api/user").access("hasRole('ADMIN') or hasRole('USER')")

【讨论】:

这个答案有什么新的吗? 请编辑您的答案以解释它的作用以及它如何解决问题中描述的问题

以上是关于Spring Security 总是返回 403 被禁止,访问被拒绝的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security 总是返回 403 被禁止,访问被拒绝

Spring Security 总是返回 HTTP 403 访问被拒绝 [关闭]

Spring security UsernamePasswordAuthenticationToken 始终返回 403:用户凭据已过期

Spring Security 返回 403 而不是 401 并创建无效的 Redis 会话 cookie

配置 Spring Security 为 REST URL 返回 403 并重定向到其他 URL 的登录

springsecurity解决post请求403问题