SpringBoot Security 访问API始终报401

前言

用POSTMAN或者在页面前端登录访问后端API时,始终返回401.返回401有很多原因造成的,主要分为两个方面来看:

  1. 配置上的问题。确实没有权限。可以去检查一下数据库,看看相关的用户,权限有没有配上。
  2. 代码上的问题。配置上已经配置了权限,任然无法访问。

这里主要讲代码上的问题。

一、 UserDetails实现类里的getAuthorities重写方法,返回null.

public class UserDto extends DeepflowAbstractDto implements UserDetails {

 private String name;

 private String password;

 private Integer age;

 private String phoneNumber;

 private List<RoleDto> authorities;

 @Override
 public Collection<RoleDto> getAuthorities() {
   return null;
 }
 
 ...
}

getAuthorities这个方法是返回当前登录用户具有哪些角色,如果返回null的话,即使数据库里给用户配置了角色,框架也认为这个用户没有任何角色可以访问这个api,自然也就报401异常。这个问题一般是编写代码是粗心导致的,耗费了大量的时间而且不容易排查。所以写代码一定要细心呀。
代码改成下面应该就可以了:

public class UserDto extends DeepflowAbstractDto implements UserDetails {

  private String name;

  private String password;

  private Integer age;

  private String phoneNumber;

  private List<RoleDto> authorities;

  @Override
  public Collection<RoleDto> getAuthorities() {
    return authorities;
  }
  
  ...
}

二、 UserDetailsService实现类没有往登录用户里塞进角色信息

@Component
public class DeepflowUserDetailsService implements UserDetailsService {

  @Inject
  private UserService userService;

  @Inject
  private UserRoleService userRoleService;

  @Inject
  private RoleService roleService;

  @Override
  public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
    UserDto userDto = userService.findUserByName(userName);
    if (null != userDto) {
      List<Long> roleIds = userRoleService.findByUserId(userDto.getId())//
          .stream()//
          .map(UserRoleDto::getRoleId)//
          .collect(Collectors.toList());
      List<RoleDto> roleDtos = roleService.findByIds(roleIds);
      // userDto.setAuthorities(roleDtos);
    }

    return userDto;
  }

}

这里如果再给框架返回的用户信息里,没有用户的角色的话,在上一步return authorities;任然会返回一个null.所以这里把注释去掉应该就可以了。一般刚接触的可能会踩到这个坑。

三、访问@PreAuthorize修饰的方法报401

当访问某些被@PreAuthorize(“hasRole(‘ADMIN’)”)注解修饰的方法时,登录用户已经配置了ADMIN角色,可还是报401。一个可能的原因是,数据库中角色的名字要存为ROLE_ADMIN。
原因是源码org.springframework.security.access.vote.RoleVoter类中定义了一个前缀private String rolePrefix = “ROLE_”;,类中的supports方法会拿权限参数和rolePrefix进行匹配,查看是否是以ROLE_开头。

public class RoleVoter implements AccessDecisionVoter<Object> {
    // ~ Instance fields
    // ================================================================================================

    private String rolePrefix = "ROLE_";

    // ~ Methods
    // ========================================================================================================

    public String getRolePrefix() {
        return rolePrefix;
    }

    /**
     * Allows the default role prefix of <code>ROLE_</code> to be overridden. May be set
     * to an empty value, although this is usually not desirable.
     *
     * @param rolePrefix the new prefix
     */
    public void setRolePrefix(String rolePrefix) {
        this.rolePrefix = rolePrefix;
    }

    public boolean supports(ConfigAttribute attribute) {
        if ((attribute.getAttribute() != null)
                && attribute.getAttribute().startsWith(getRolePrefix())) {
            return true;
        }
        else {
            return false;
        }
    }
}

在supports方法中,会判断角色是否以ROLE_开头,如果不是的话,就会返回false.
解决办法:数据库中的角色名字以ROLE_为前缀存储

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值