Spring Boot Security @RolesAllowed搭配自定义UserDetailsService报AccessDeniedException
问题根源
你自定义的User类在实现UserDetails接口时,getAuthorities()方法没有正确返回对应用户的权限列表,或是权限标识和@RolesAllowed注解配置的角色不匹配(Spring Security默认会给角色加上ROLE_前缀,和你配置的角色名对应不上就会抛出权限不足异常)。
修复方案
方案1:保留默认前缀,修改对应配置
直接使用Spring Security自带的User构建器构造用户,同时调整@RolesAllowed注解的参数匹配前缀:
- 修改
UserServiceImpl构造方法:
public UserServiceImpl() { BCryptPasswordEncoder encoder=new BCryptPasswordEncoder(); // 使用Spring Security自带的User构建器,roles方法会自动给角色加ROLE_前缀 UserDetails user1 = User.withUsername("user1") .password(encoder.encode("2005")) .roles("DEVELOPER") .build(); UserDetails user2 = User.withUsername("user2") .password(encoder.encode("2005")) .roles("ADMINISTRATOR") .build(); mapUsers.put(user1.getUsername(), user1); mapUsers.put(user2.getUsername(), user2); }
- 调整控制器的权限注解:
@GetMapping("/admin") @RolesAllowed("ROLE_ADMINISTRATOR") public ResponseEntity<?> getWelcomeMsgAdmin(){ return ResponseEntity.ok("hello admin"); } @GetMapping("/developer") @RolesAllowed("ROLE_DEVELOPER") public ResponseEntity<?> getWelcomeMsgDeveloper(){ return ResponseEntity.ok("hello dev"); }
方案2:移除默认前缀,保留原有写法
如果你不想修改现有的@RolesAllowed注解配置,可以新增配置关闭Spring Security默认的ROLE_前缀:
- 在安全配置类中新增Bean移除前缀:
@Bean public GrantedAuthorityDefaults grantedAuthorityDefaults() { return new GrantedAuthorityDefaults(""); // 清空默认的角色前缀 }
- 修改
UserServiceImpl构造方法,直接传入自定义的角色名作为权限:
public UserServiceImpl() { BCryptPasswordEncoder encoder=new BCryptPasswordEncoder(); UserDetails user1 = User.withUsername("user1") .password(encoder.encode("2005")) .authorities(new SimpleGrantedAuthority("DEVELOPER")) .build(); UserDetails user2 = User.withUsername("user2") .password(encoder.encode("2005")) .authorities(new SimpleGrantedAuthority("ADMINISTRATOR")) .build(); mapUsers.put(user1.getUsername(), user1); mapUsers.put(user2.getUsername(), user2); }
如果你要继续使用自定义User类
请确保自定义User类的getAuthorities()方法正确返回权限集合,示例实现如下:
public class User implements UserDetails { private String username; private String password; private String role; // 构造方法、普通getter省略 @Override public Collection<? extends GrantedAuthority> getAuthorities() { return Collections.singletonList(new SimpleGrantedAuthority(role)); } // 以下四个状态方法全部返回true即可,有特殊需求再自定义逻辑 @Override public boolean isAccountNonExpired() {return true;} @Override public boolean isAccountNonLocked() {return true;} @Override public boolean isCredentialsNonExpired() {return true;} @Override public boolean isEnabled() {return true;} }
配合方案2的移除前缀配置即可正常使用。
内容的提问来源于stack exchange,提问作者Loga Nathan
相关产品推荐
相关产品推荐

