Spring Security全局方法安全不生效 集成Keycloak OAuth2报错排查
问题修复方案
错误根因说明
- 调用findAll()接口报错:缺少
spring-security-data依赖导致SecurityEvaluationContextExtension未生效,或是Spring版本兼容性问题,Repository层的SpEL表达式无法获取到Security上下文中的authentication对象 - 调用add()接口403:Spring Security默认JWT转换器将Token中的
sub字段作为用户名,而你的Token中实际用户名字段为user_name,导致@PreAuthorize("#workout.user == authentication.name")校验不通过
修复步骤
步骤1:补充依赖
首先确认项目引入了spring-security-data依赖,这是Security上下文在Repository层SpEL生效的前提:
Maven配置:
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-data</artifactId> </dependency>
Gradle配置:
implementation 'org.springframework.security:spring-security-data'
步骤2:自定义JWT转换器,正确映射用户名和权限
你的Token中用户名字段为user_name、权限字段为authorities,和Spring默认映射规则不符,需要自定义转换器:
在ResourceServerConfig配置类中添加如下Bean:
@Bean public JwtAuthenticationConverter jwtAuthenticationConverter() { JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); // 指定权限读取字段 grantedAuthoritiesConverter.setAuthoritiesClaimName("authorities"); // 去掉默认的SCOPE_前缀 grantedAuthoritiesConverter.setAuthorityPrefix(""); JwtAuthenticationConverter authenticationConverter = new JwtAuthenticationConverter(); authenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter); // 指定用户名字段 authenticationConverter.setPrincipalClaimName("user_name"); return authenticationConverter; }
修改HttpSecurity配置,关联自定义转换器:
@Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests(authz -> authz .mvcMatchers(HttpMethod.DELETE, "/**").hasAuthority("fitnessadmin") .anyRequest().authenticated()) .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter())) ); }
步骤3:优化查询逻辑(彻底解决Repository层SpEL报错)
为了避免不同Spring版本的Repository SpEL兼容性问题,建议把用户过滤逻辑移到Service层:
修改WorkoutRepository:
public interface WorkoutRepository extends JpaRepository<Workout, Integer> { List<Workout> findAllByUser(String user); }
修改WorkoutService的findWorkouts方法:
public List<Workout> findWorkouts() { String currentUser = SecurityContextHolder.getContext().getAuthentication().getName(); return repository.findAllByUser(currentUser); }
步骤4:可选适配高版本Spring Security
如果你的Spring Boot版本>=2.7,@EnableGlobalMethodSecurity已废弃,可替换为新注解:
@EnableMethodSecurity(prePostEnabled = true)
内容的提问来源于stack exchange,提问作者Thomson Mathew
相关产品推荐
相关产品推荐

