Spring Security 始终返回 403 禁止访问,拒绝访问。

13
我想让管理员能够访问管理员页面并执行管理员操作,但当我尝试通过设置仅允许具有“admin”角色的用户访问带有/admin/**路径的URL时,它返回403禁止访问的错误。但是我已经检查了用户的权限是否设置为“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);
        }
    }
}
我可以说我用postman测试了我的应用程序,登录和注册都正常工作。当用户登录后,我可以使用正确的数据和用户权限获取令牌,但是为什么当我尝试访问“/ admin / building / add”网址时,它返回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"),但仍然无法运行。

在数据库中,管理员角色保存为ROLE_ADMIN。


1
你完全绕过了Spring Security的登录验证。使用Spring Security来处理登录,不要重新发明轮子。 - M. Deinum
请查看此处:https://dev59.com/xmIk5IYBdhLWcg3wN71j#54978734 - Manas Ranjan Mahapatra
3个回答

11
尝试像这样:

试试这样:

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, "/**");
    }
}

完整配置示例:使用基本身份验证保护Spring REST API


不推荐使用web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");,请勿使用。 - wonsuc

4
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.
    }

}

这个方法对我有用。现在我能够成功提交我的POST请求了。


2
这将禁用CSRF,但不会影响会话,也不建议这样做。https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF) 你的请求失败的原因是你没有提供CSRF cookie/header。 - Darren Forsythe

0

在SecurityConfig中尝试这个:

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

这个回答提供了什么新的内容吗? - NatFar
请编辑您的答案以解释这是什么以及它如何解决问题。 - Legxis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接