Spring security UsernamePasswordAuthenticationToken 始终返回 403:用户凭据已过期
Posted
技术标签:
【中文标题】Spring security UsernamePasswordAuthenticationToken 始终返回 403:用户凭据已过期【英文标题】:Spring security UsernamePasswordAuthenticationToken always returns 403: User credentials have expired 【发布时间】:2021-07-06 14:51:10 【问题描述】:我正在尝试在我的 kotlin 应用程序中实现基于 Spring 安全 JWT 的授权,但在每次 /authenticate(登录)时,UsernamePasswordAuthenticationToken 都会在数据库中找到用户,但返回 403:用户凭据已过期,即使对于刚刚添加的用户也是如此。
SecurityConfiguration 类:
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
class SecurityConfiguration(
private val userDetailsService: UserDetailService,
private val jwtAuthorizationFilter: JwtAuthorizationFilter,
) : WebSecurityConfigurerAdapter()
@Throws(Exception::class)
override fun configure(http: HttpSecurity)
http.cors().and()
.csrf().disable()
http.addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter::class.java)
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.antMatchers("/api/authenticate").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
@Bean
@Throws(Exception::class)
override fun authenticationManagerBean(): AuthenticationManager
return super.authenticationManagerBean()
@Throws(Exception::class)
public override fun configure(auth: AuthenticationManagerBuilder)
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder())
@Bean
fun passwordEncoder() = BCryptPasswordEncoder()
@Bean
fun corsConfigurationSource(): CorsConfigurationSource
val source = UrlBasedCorsConfigurationSource()
source.registerCorsConfiguration("/**", CorsConfiguration().applyPermitDefaultValues())
return source
AuthorizationFilter 类
@Component
class JwtAuthorizationFilter(
private val jwtUtil: JtwUtil,
private val userDetailsService: UserDetailsService) : OncePerRequestFilter()
@Throws(IOException::class, ServletException::class)
override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse,
filterChain: FilterChain
)
val header = request.getHeader(SecurityConstants.TOKEN_HEADER)
if (StringUtils.isEmpty(header) || !header.startsWith(SecurityConstants.TOKEN_PREFIX))
filterChain.doFilter(request, response)
return
val jwt: String = header.substring(7)
val username: String? = jwtUtil.extractUsername(jwt)
if (username != null && SecurityContextHolder.getContext().authentication == null)
val userDetails: UserDetails = userDetailsService.loadUserByUsername(username)
val isValidToken: Boolean = jwtUtil.validateToken(jwt, userDetails)
if (isValidToken)
val usernamePasswordAuthenticationToken =
UsernamePasswordAuthenticationToken(userDetails, null, userDetails.authorities)
usernamePasswordAuthenticationToken.details =
WebAuthenticationDetailsSource().buildDetails(request)
SecurityContextHolder.getContext().authentication =
usernamePasswordAuthenticationToken
filterChain.doFilter(request, response)
用户详细信息服务:
@Service
class UserDetailService(private val userRepository: UserRepository): UserDetailsService
override fun loadUserByUsername(username: String?): UserDetails?
try
val user: UserTable = userRepository.findByUsername(username)
?: throw UsernameNotFoundException("Username $username not found")
return AppUserDetails(user)
catch (e: Exception)
throw Exception(e)
这是我调用的 API 来尝试授权数据库中的用户
@RequestMapping("/api/authenticate")
@RestController
class AuthenticationController(
private val authenticationManager: AuthenticationManager,
private val userDetailsService: UserDetailsService,
private val userDetailService: UserDetailService,
private val jwtUtil: JtwUtil
)
@PostMapping
fun authenticateUser(@RequestBody authenticationRequest: AuthenticationRequest):
ResponseEntity<AuthenticationResponse>
try
authenticationManager.authenticate
(UsernamePasswordAuthenticationToken(authenticationRequest.username,
authenticationRequest.password))
val userDetails: UserDetails =
userDetailsService.loadUserByUsername(authenticationRequest.username)
val jwt: String = jwtUtil.generateToken(userDetails)
return ResponseEntity.ok(AuthenticationResponse(jwt))
catch (e: BadCredentialsException)
throw BadCredentialsException("Incorrect username or password $e")
我调试了请求,发现这个方法认证失败:
【问题讨论】:
【参考方案1】:发现问题,可能对其他人有帮助。在我映射用户的 UserDetails 服务中,我将方法硬编码为始终返回 false
override fun isCredentialsNonExpired(): Boolean
return false
【讨论】:
以上是关于Spring security UsernamePasswordAuthenticationToken 始终返回 403:用户凭据已过期的主要内容,如果未能解决你的问题,请参考以下文章
Spring Security:2.4 Getting Spring Security
没有 JSP 的 Spring Security /j_spring_security_check
Spring Security 登录错误:HTTP 状态 404 - /j_spring_security_check