Spring Security使用有效的JWT返回403 [英] Spring Security returns 403 with valid JWT
问题描述
我正在使用Postman测试我在带有Spring Security的Spring Boot 2.2.6中创建的简单OAuth2 API.请求新的用户凭据时,我成功接收到JWT,但是当我尝试使用标头中的此令牌访问它们时,所有端点均返回403 Forbidden错误.
I'm using Postman to test a simple OAuth2 API I'm creating in Spring Boot 2.2.6 with Spring Security. I successfully receive a JWT when requesting new user credentials, but all of my endpoints return a 403 Forbidden error when I attempt to access them with this token in my headers.
我的课程如下:
我的服务器安全性配置:
My server security configuration:
@Configuration
@EnableWebSecurity
@Order(1)
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
class ServerSecurityConfiguration(
@Qualifier("userService")
private val userDetailsService: UserDetailsService
) : WebSecurityConfigurerAdapter() {
private val logger: Logger = LoggerFactory.getLogger(ServerSecurityConfiguration::class.java)
@Bean
fun authenticationProvider(): DaoAuthenticationProvider {
val provider = DaoAuthenticationProvider()
provider.setPasswordEncoder(passwordEncoder())
provider.setUserDetailsService(userDetailsService)
return provider
}
@Bean
fun passwordEncoder(): PasswordEncoder {
return BCryptPasswordEncoder()
}
@Bean
@Throws(Exception::class)
override fun authenticationManagerBean(): AuthenticationManager {
return super.authenticationManagerBean()
}
@Throws(Exception::class)
override fun configure(auth: AuthenticationManagerBuilder) {
auth
.parentAuthenticationManager(authenticationManagerBean())
.authenticationProvider(authenticationProvider())
.userDetailsService(userDetailsService)
.and()
}
@Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http
.cors().and().csrf().disable() // remove for production
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.authorizeRequests()
.antMatchers(
"/",
"/index.html",
"/**/*.js",
"/**/*.html",
"/**/*.css",
"/**/*.woff",
"/**/*.woff2",
"/**/*.svg",
"/**/*.ttf",
"/**/*.ico",
"/**/*.eot",
"/**/assets/*",
"/api/login/**",
"/oauth/token",
"/oauth/authorize"
)
.permitAll()
.antMatchers(HttpMethod.POST, "/api/submissions")
.authenticated()
.antMatchers(HttpMethod.POST, "/api/users")
.hasAuthority(Role.ADMIN.name)
.antMatchers(HttpMethod.POST,"/api/**")
.hasAuthority(Role.ADMIN.name)
.antMatchers(HttpMethod.DELETE, "/api/**")
.hasAuthority(Role.ADMIN.name)
.antMatchers(HttpMethod.PUT, "/api/**")
.hasAnyAuthority(Role.ADMIN.name)
.antMatchers(HttpMethod.GET, "/api/**")
.authenticated()
.anyRequest()
.authenticated()
}
}
我的OAuth2配置:
My OAuth2 configuration:
@Configuration
@EnableAuthorizationServer
class OAuth2Configuration(
@Qualifier("authenticationManagerBean") private val authenticationManager: AuthenticationManager,
private val passwordEncoder: PasswordEncoder,
private val userService: UserService,
private val jwt: JwtProperties
) : AuthorizationServerConfigurerAdapter() {
private val logger = LoggerFactory.getLogger("OAuth2Configuration")
@Throws(Exception::class)
override fun configure(clients: ClientDetailsServiceConfigurer?) {
clients
?.inMemory()
?.withClient(jwt.clientId)
?.secret(passwordEncoder.encode(jwt.clientSecret))
?.accessTokenValiditySeconds(jwt.accessTokenValiditySeconds)
?.refreshTokenValiditySeconds(jwt.refreshTokenValiditySeconds)
?.authorizedGrantTypes(*jwt.authorizedGrantTypes)
?.scopes("read", "write")
?.resourceIds("api")
}
override fun configure(endpoints: AuthorizationServerEndpointsConfigurer?) {
endpoints
?.tokenStore(tokenStore())
?.accessTokenConverter(accessTokenConverter())
?.userDetailsService(userService)
?.authenticationManager(authenticationManager)
}
@Bean
fun accessTokenConverter(): JwtAccessTokenConverter {
val converter = JwtAccessTokenConverter()
converter.setSigningKey(jwt.signingKey)
return converter
}
@Bean
@Primary
fun tokenServices(): DefaultTokenServices {
val services = DefaultTokenServices()
services.setTokenStore(tokenStore())
return services
}
@Bean
fun tokenStore(): JwtTokenStore {
return JwtTokenStore(accessTokenConverter())
}
}
我的资源服务器配置:
@Configuration
@EnableResourceServer
class ResourceServerConfiguration : ResourceServerConfigurerAdapter() {
override fun configure(resources: ResourceServerSecurityConfigurer?) {
resources?.resourceId("api")
}
}
我的用户详细信息服务:
My user details service:
@Service
class UserService(private val repository: UserRepository) : UserDetailsService {
private val logger: Logger = LoggerFactory.getLogger(UserService::class.java)
override fun loadUserByUsername(username: String?): UserDetails {
val user = repository.findByUsername(username)
?: throw UserNotFoundException("User with username $username not found.")
return org.springframework.security.core.userdetails.User
.withUsername(user.name)
.password(user.passwordHash)
.authorities(user.role.name)
.build()
}
}
任何帮助将不胜感激,我在这里很茫然.
Any help would be appreciated, I'm at a loss here.
调试日志如下:
2020-04-21 08:05:42.583 DEBUG 14388 --- [nio-8080-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/api/submissions'; against '/api/**'
2020-04-21 08:05:42.583 DEBUG 14388 --- [nio-8080-exec-3] o.s.s.w.a.i.FilterSecurityInterceptor : Secure object: FilterInvocation: URL: /api/submissions; Attributes: [authenticated]
2020-04-21 08:05:42.584 DEBUG 14388 --- [nio-8080-exec-3] o.s.s.w.a.i.FilterSecurityInterceptor : Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@ac165fba: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
2020-04-21 08:05:42.584 DEBUG 14388 --- [nio-8080-exec-3] o.s.s.access.vote.AffirmativeBased : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@1097cbf1, returned: -1
2020-04-21 08:05:42.601 DEBUG 14388 --- [nio-8080-exec-3] o.s.s.w.a.ExceptionTranslationFilter : Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84) ~[spring-security-core-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233) ~[spring-security-core-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:123) ~[spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:90) ~[spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:118) ~[spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:158) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:92) [spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) [spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) [spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) [spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.33.jar:9.0.33]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.33.jar:9.0.33]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) [spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.33.jar:9.0.33]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.33.jar:9.0.33]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) [spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.33.jar:9.0.33]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.33.jar:9.0.33]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) [spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.33.jar:9.0.33]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.33.jar:9.0.33]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) [tomcat-embed-core-9.0.33.jar:9.0.33]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.33.jar:9.0.33]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) [tomcat-embed-core-9.0.33.jar:9.0.33]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.33.jar:9.0.33]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.33.jar:9.0.33]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.33.jar:9.0.33]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.33.jar:9.0.33]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373) [tomcat-embed-core-9.0.33.jar:9.0.33]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.33.jar:9.0.33]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-embed-core-9.0.33.jar:9.0.33]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1594) [tomcat-embed-core-9.0.33.jar:9.0.33]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.33.jar:9.0.33]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_121]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_121]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.33.jar:9.0.33]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_121]
我的用户对象似乎没有获得ADMIN角色.
It looks like my user object isn't getting the ADMIN role.
更新: 我添加了一个过滤器并打印出承载令牌.它确实存在.
Update: I added a filter and printed out the bearer token. It does exist.
推荐答案
有点晚了,但是更简单的解决方案是创建自定义Jwt身份验证转换器:
A little bit late, but a simpler solution could be creating a custom Jwt Authentication Converter:
class CustomJwtAuthenticationConverter : Converter<Jwt, AbstractAuthenticationToken> {
private val jwtGrantedAuthoritiesConverter = JwtGrantedAuthoritiesConverter()
override fun convert(source: Jwt): AbstractAuthenticationToken {
val scopes = jwtGrantedAuthoritiesConverter.convert(source)
val authorities = source.getClaimAsStringList("authorities")?.map { SimpleGrantedAuthority(it) }
return JwtAuthenticationToken(source, scopes.orEmpty() + authorities.orEmpty())
}
}
,然后将转换器提供给您的override fun configure(http: HttpSecurity)
实现,例如:
and then supply the converter to your override fun configure(http: HttpSecurity)
implementation, like:
.jwtAuthenticationConverter(CustomJwtAuthenticationConverter())
这篇关于Spring Security使用有效的JWT返回403的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!