当前位置: 首页 > 知识库问答 >
问题:

Spring Security返回403和有效JWT

公羊曜灿
2023-03-14

我正在使用Postman测试我在Spring Boot 2.2.6和Spring Security中创建的简单OAuth2 API。当我请求新用户凭据时,我成功地收到了一个JWT,但是当我尝试使用头文件中的此令牌访问它们时,我的所有endpoint都返回了403禁止错误。

我的课程如下:

我的服务器安全配置:

@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配置:

@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")
    }
}

我的用户详细信息服务:

@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()
    }
}

任何帮助都将不胜感激,我在这里不知所措。

调试日志如下所示:

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角色。

更新:我添加了一个过滤器并打印出了承载令牌。它确实存在。

共有2个答案

危飞跃
2023-03-14

添加以下过滤器似乎可以解决问题。

class JwtAuthorizationFilter(
        authenticationManager: AuthenticationManager,
        @Qualifier("userService")
        private val userDetailsService: UserService,
        private val jwt: JwtProperties,
        private val passwordEncoder: PasswordEncoder

) : BasicAuthenticationFilter(authenticationManager) {
    private val log: Logger = LoggerFactory.getLogger(JwtAuthorizationFilter::class.java)

    @Throws(ServletException::class, IOException::class)
    override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) {
        val header = request
            .getHeader(jwt.authorizationHeaderString)
            ?.takeIf { s -> s.startsWith(jwt.tokenBearerPrefix) }

        if (header == null) {
            filterChain.doFilter(request, response)
            return
        }

        val auth = authenticate(request)

        if (auth != null) {
            log.info("Authentication valid for ${auth.principal}.")
            SecurityContextHolder.getContext().authentication = auth
        }

        log.info("Bearer token processed. Continue.")
        filterChain.doFilter(request, response)
    }

    private fun authenticate(request: HttpServletRequest): UsernamePasswordAuthenticationToken? {
        val token = request.getHeader(jwt.authorizationHeaderString)

        if (token != null) {
            val claims = JWT
                .require(Algorithm.HMAC256(jwt.signingKey))
                .build()
                .verify(token.replace("${jwt.tokenBearerPrefix} ", ""))
                .claims

            val username = claims["user_name"]?.asString()
            val authorities = claims["authorities"]
                    ?.asArray(String::class.java)
                    ?.map { s -> SimpleGrantedAuthority(s) }
                    ?: return null

            if (username != null) {
                return UsernamePasswordAuthenticationToken(username, null, authorities)
            }
        }

        return null
    }
}

你需要追加

            .and()
                .addFilter(
                        JwtAuthorizationFilter(authenticationManager(), userDetailsService as UserService, jwt, passwordEncoder())
                )

连接到服务器安全HTTP配置。

巫培
2023-03-14

有点晚了,但更简单的解决方案可能是创建一个自定义Jwt身份验证转换器:

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())
  }
}

然后将转换器提供给覆盖乐趣配置(http:HttpSecurity)实现,如:

.jwtAuthenticationConverter(CustomJwtAuthenticationConverter())

 类似资料:
  • 问题内容: 我有以下代码: 那应该从给定的URL返回图像。 我测试了以下两个随机选择的URL: https://www.google.co.ma/images/srpr/logo4w.png http://www.earthtimes.org/newsimage/osteoderms-storing-minerals-helped-huge-dinosaurs-survive_3011.jpg 第

  • 问题内容: 我正在尝试制作Sitecraper。我是在本地计算机上制作的,在那儿工作得很好。当我在服务器上执行相同操作时,它显示403禁止错误。我正在使用PHP简单HTML DOM解析器 。我在服务器上收到的错误是这样的: 警告:file_get_contents(http://example.com/viewProperty.html?id=7715888)[function.file- get

  • 我的安全配置似乎不正确。无论我在使用hasRole时做什么,我的endpoint总是返回403。 此外,除非我在这两个和。很明显,我遗漏了一些东西。 基本上,我希望所有内容都需要身份验证,但只有当用户是某些组的成员时(现在只需要admin),少数endpoint才可以访问。 我的安全配置如下。旁边的一切都有效。 我的AuthenticationConfiguration如下 我的Authoriza

  • 我试图利用Laravel5.7中新的签名中间件,但由于某些原因,生成的签名URL返回403个无效签名。 我使用最新的Laravel版本,PHP 7.2 这是我的web.php路线: 这是在我的控制器: 生成URL并显示如下内容: https://example.com/report/1/1?expires=1545440368 但是,当我点击链接时,结果是403,并显示消息:“无效签名” 有什么想

  • 我是spring security的新手,尝试在spring boot rest服务上实现基本身份验证。我使用基于数据库的身份验证,并具有用户和角色表。当我向我的应用程序中的任何具有正确凭据的控制器发出请求时,它总是给我403禁止。我不明白为什么。我多次检查角色是否正确。在数据库中,角色名是“USER”、“RESTAURANT”和“ADMIN”。我尝试了ROLE\uPrefix和独立大写语法,但两

  • 我已经配置了一个自定义过滤器,它为除登录之外的每个URL授予spring权限: 和一个spring配置,该配置使用该权限保护所有请求(但不包括登录): 但是,除了登录之外,每个请求都会得到HTTP 403禁止。 我已经调试并确保过滤器中的代码确实被触发。 有什么问题吗? 编辑-当将Spring Security日志放入调试时,我得到以下堆栈跟踪: