Spring Boot Kotlin中@PreAuthorize权限控制失效问题排查
问题描述
在Spring Boot Kotlin项目中实现了基于数据库的自定义UserDetailsService,但出现权限控制异常:仅拥有ROLE_USER权限的新用户,居然能访问需要ROLE_ADMIN权限的/admin路由。尝试使用hasRole('ADMIN')和hasAuthority('ROLE_ADMIN')两种表达式都无效,而且getAuthorities()与loadUserByUsername方法内的打印语句完全不执行,即使开启logging.level.org.springframework.security=DEBUG日志也无法进行调试。此前在Java的OAuth2项目中,该路由的权限限制可以正常生效,怀疑是Kotlin特有的问题。
相关代码如下:
TestController代码
@RestController class TestController() { @PreAuthorize("hasAuthority('ROLE_USER')") @GetMapping("/api/user") fun getUser() : String { return "hello user" } @PreAuthorize("hasAuthority('ROLE_ADMIN')") @GetMapping("/api/admin") fun getAdmin() : String { return "hello admin" } }
User实体类(实现UserDetails)
@Document(collection="users") class User ( val principalId: String, val name: String, val email: String, val service: AuthService, @JsonIgnore @get:JvmName("getPasswordJVM") var password: String? = null, val createdDate: Long = System.currentTimeMillis() / 1000, val draftGroups: MutableList<ObjectId> = mutableListOf<ObjectId>(), @Id val id : ObjectId = ObjectId.get(), var accountNonExpired: Boolean = true, var accountNonLocked: Boolean = true, var credentialsNonExpired: Boolean = true, var enabled: Boolean = true, var roles: String = "ROLE_USER" ) : UserDetails { override fun equals(other: Any?): Boolean = other is User && other.name == name && other.email == email var admin : Boolean = false override fun getAuthorities() : List<GrantedAuthority> { val authorities = mutableListOf<GrantedAuthority>() for (role in roles.split(",")) { authorities.add(SimpleGrantedAuthority(role)) } return authorities } override fun getUsername() : String { return this.name } override fun getPassword() : String { return this.password ?: "no password due to oauth" } override fun isAccountNonExpired() : Boolean { return this.accountNonExpired } override fun isAccountNonLocked() : Boolean { return this.accountNonLocked } override fun isCredentialsNonExpired() : Boolean { return this.credentialsNonExpired } override fun isEnabled() : Boolean { return this.enabled } }
自定义UserDetailsService
@Service public class MyUserDetailsService (private val userRepository: UserRepository) : UserDetailsService { init { println("user details service 13") } public override fun loadUserByUsername(s: String) : UserDetails { println("IN LOAD USER BY USERNAME") val users : List<User> = userRepository.findByName(s); if (users.size == 0) throw UsernameNotFoundException(String.format("Username[%s] not found")); return users[0] } }
Security配置类
@EnableWebSecurity @Configuration public class SecurityConfig( private val authenticationSuccessHandler: AuthenticationSuccessHandler, private val myUserDetailsService: MyUserDetailsService) { @Throws(Exception::class) @Bean public fun override(http: HttpSecurity): SecurityFilterChain { return http .csrf{csrf -> csrf.disable()} .authorizeRequests{auth -> auth auth.antMatchers("/api/user").authenticated() auth.antMatchers("/api/admin").authenticated() } .userDetailsService(myUserDetailsService) .oauth2Login() .successHandler(authenticationSuccessHandler) //\.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) // csrf token is stored in browser as cookie XSRF-TOKEN, it is send along with POST requests in request header X-XSRF-TOKEN .and() .build() } @Bean fun passwordEncoder(): PasswordEncoder { return PasswordEncoderFactories.createDelegatingPasswordEncoder() } }
内容的提问来源于stack exchange,提问作者Ethan Michel
相关产品推荐
相关产品推荐

