弹簧启动@PreAuthorize注释似乎不起作用



我有一个使用@PreAuthorize的大问题。Spring Boot似乎没有注意到当前登录的用户确实具有执行操作所需的角色这一事实,因为用户在下面的用户实现中的getAuthorities()方法中获得其角色:

我得到的异常

org.springframework.security.access.AccessDeniedException: Access Denied
at org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.attempt
at org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.attemptAuthorization(AuthorizationManagerBeforeMethodInterceptor.java:257)
at org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.invoke(AuthorizationManagerBeforeMethodInterceptor.java:198)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:752)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:703)
at com.issue.tracker.ticket.TicketController$$SpringCGLIB$$0.createTicket(<generated>)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:578)

等等……(我将根据要求包括更多的日志,但其余的似乎只是填充)

相关配置(如果我错过了什么请告诉我):

我的配置类

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableAspectJAutoProxy
@EnableMethodSecurity
@EnableJpaAuditing(auditorAwareRef = "auditorAware")
class SecurityConfig {
private final UserRepository userRepository;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/authenticate").permitAll()
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.anyRequest().authenticated())
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.httpBasic(Customizer.withDefaults())
.headers(header -> header.frameOptions().sameOrigin())
.build();
}
@Bean
public AuthenticationManager authenticationManager(
UserDetailsService userDetailsService) {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return new ProviderManager(authenticationProvider);
}
@Bean
public UserDetailsService userDetailsService() {
return username -> userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
}
// ...stuff for JWT tokens to work, Bcrypt, other stuff, etc. not included
}

我的用户实体实现

@Data
@Entity
@Table(name = "users")
@Slf4j
class User extends BaseEntity implements UserDetails {
@NotNull
private String username;
@NotNull
@Column(name = "password_")
private String password;
@NotNull
private String firstName;
@NotNull
private String lastName;
@NotNull
private String email;
@ManyToMany
@JoinTable(name = "user_roles",
joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
private Set<Role> roles;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> userRoles = roles.stream().map(role -> new SimpleGrantedAuthority(role.getRoleName())).collect(Collectors.toList());
log.info(userRoles.toString());
// And yes, upon logging in, I do get the ROLE_ADMIN role 
// in the logs, so it's not like a problem with the user 
// itself not having the role
return userRoles;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}

我的控制器

@RequestMapping("/ticket")
@RequiredArgsConstructor
@RestController
class TicketController {
private final TicketServiceImpl ticketService;
private final CustomModelMapper modelMapper;
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
@PostMapping("/save")
public TicketDto createTicket(@RequestBody @Valid TicketDto ticketDto) {
Ticket ticketRequest = modelMapper.map(ticketDto, Ticket.class);
Ticket ticket = ticketService.createTicket(ticketRequest);
return modelMapper.map(ticket, TicketDto.class);
}
}

  • Spring版本:6.0.4
  • Java版本:19.0.1

到目前为止我尝试过的一些事情:

  1. 替换我的userDetailsService实现以检查问题是否与我的User实现有关:
    @Bean
    public UserDetailsService userDetailsService() {
    UserDetails theManager = User.withUsername("admin1")
    .passwordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder()::encode)
    .password("admin1").roles("ADMIN").build();
    InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager();
    userDetailsManager.createUser(theManager);
    return userDetailsManager;
    }
    

2. Changing my **@PreAuthorize("hasAuthority('ROLE_ADMIN')")** to:
- `hasRole('ROLE_ADMIN')`
- `hasRole('ADMIN')`
- I also tried changing `@PreAuthorize` to `@RolesAllowed` (with the necessary `jsr250Enabled=true` on the `@EnableMethodSecurity` annotation in the config class of course)
- Removing the `@Valid` annotation from the controller method parameter
None of these things have worked and I'm all out of ideas and have no clue how to proceed from here.

感谢@MarcusHertdaCoregio

在使用TRACE级别的调试后,我发现Marcus是对的,如果您提供JWT,那么您的所有角色都将以SCOPE_作为前缀默认情况下。因此,如果你想让你的@PreAuthorize工作,只有ROLE_ADMIN和ROLE_USER不能单独工作,你需要使用SCOPE_ROLE_ADMIN或SCOPE_ROLE_USER等等。

相关内容

  • 没有找到相关文章

最新更新