首页 文章

为什么Spring Security不能在这个Spring Boot REST API项目上运行?它总是给我“403 Forbidden”错误

提问于
浏览
0

我正在开发一个 Spring Boot 1.4.1 应用程序(实现一些REST Web服务),我发现在尝试将 Spring Security 实现到这个项目时遇到了一些困难 .

我有以下情况:

1)我有实现Spring Security UserDetails 接口的 CustomUserDetails

import com.betrivius.domain.User;
import com.betrivius.domain.UserRole;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

public class CustomUserDetails extends User implements UserDetails {

    private static final long serialVersionUID = 1L;


    public CustomUserDetails(User user){
        super(user);
    }


    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
        for(UserRole role : this.getUserRoles() ){
            GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role.getName());
            authorities.add(grantedAuthority);
        }

        return authorities;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    public boolean isEnabled() {
        return true;
    }

    @Override
    public String getUsername() {
        return super.getUsername();
    }

}

2)然后我有 CustomUserDetailsService 实现Spring Security UserDetailsService 接口:

import com.betrivius.dao.UserDAO;
import com.betrivius.domain.User;
import com.betrivius.security.bean.CustomUserDetails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Transactional
@Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService{
    @Autowired
    private UserDAO userDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userDao.findByUsername(username);
        if(user == null){
            throw new UsernameNotFoundException("No user present with username: "+username);
        }else{
            return new CustomUserDetails(user);
        }
    }

}

3)最后我有一个名为 WebSecurityConfig 的Spring Security confinguration类,扩展了 WebSecurityConfigurerAdapter

import com.betrivius.security.service.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = CustomUserDetailsService.class)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordencoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/Accomodation/**").access("hasRole('ROLE_USER')")
                .anyRequest().permitAll()
                .and()
                .csrf().disable();

  /*.and()
    .formLogin().loginPage("/login")
    .usernameParameter("username").passwordParameter("password")
  .and()
    .logout().logoutSuccessUrl("/login?logout") 
   .and()
   .exceptionHandling().accessDeniedPage("/403")
  .and()
    .csrf();*/
    }

    @Bean(name = "passwordEncoder")
    public PasswordEncoder passwordencoder() {
        return new BCryptPasswordEncoder();
    }
}

正如您在配置类中的前一个代码片段中所看到的,我已经指定只有设置了 ROLE_USER 的用户才能访问 /Accomodation/ 路径下的所有资源,我通过以下方式完成了:

.antMatchers("/Accomodation/**").access("hasRole('ROLE_USER')")

4)这是 UserDAO ,它获取用于前一个 CustomUserDetailsService 类的用户信息:

@Repository
@Transactional(propagation = Propagation.MANDATORY)
public interface UserDAO extends JpaRepository<User, Long> {

    User findByUsername(String username);

    @Query("SELECT r FROM User u JOIN u.userRoles r where u.username = :username")
    List<UserRole> findRoleByUserName(String username);
}

用户名和密码在数据库中是stord,并且是:

USERNAME:ErSabba

密码:pswd

User 表与 User_Roles 有多对多的关系, ErSabba 用户的作用是 ROLE_USER (尝试设置 USER 作为角色) .

因此,通过 PostMan Chrome插件,我会对网址执行GET请求:

http://localhost:8080/Accomodation/7

以这种方式传递以前的凭证:

enter image description here

正如您所看到的,问题是将正确的用户名和密码插入 basic authentication form 它会给我 403 error Access denied .

我认为这意味着我已经执行了身份验证,但我没有授权 .

奇怪的是,我也试图把坏凭证,我总是得到相同而不是 401 Bad Credential 错误 .

我还在 loadUserByUsername() 方法的这一行上放了一个断点到 CustomUserDetailsService 类(检索 User 信息) .

User user = userDao.findByUsername(username);

在调试模式下运行它不会在此行停止,所以似乎 Spring Security 配置不能正常工作...

可能是什么问题呢?我错过了什么?我该如何解决这个问题?

1 回答

  • 0

    我建议你在没有数据库的情况下进行测试 . 您可以提供 inMemory 配置,如下所示:

    auth.inMemoryAuthentication().withUser("ErSabba").password("pswd").authorities("ROLE_USER");
    

    然后尝试登录 . 如果它可以工作,它将显示DB中的凭据有问题 .

    如何在DB中存储密码?如果它是纯文本,它将无法正常工作,因为您有密码编码器,它应该将您的密码读作哈希 .

    希望有所帮助 .

相关问题