【发布时间】:2020-09-25 09:26:34
【问题描述】:
使用 Boot 2.1.14.Release / security 5.1.10
我有以下需要指定安全性的端点
/token/exchange - 只有在请求具有 Okta JWT 时,此端点才应允许访问。它返回一个我通过 JJWT 手动创建的自定义 JWT。基本上,用户不会传递用户凭据,而是已经通过 Okta 进行了身份验证,并将提供该令牌作为他们的凭据。
我已经添加了 Okta 启动器,它按预期工作
/api/** - /api 下的任何端点都需要在授权标头中使用我的自定义 JWT
我有以下安全配置:
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
@Configuration
public class AppWebSecurityConfigurerAdapter {
@Configuration
@Order(1)
public static class OktaWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/token/exchange") <------- this should "pin" this config right?
.authorizeRequests()
.antMatchers("/token/exchange").authenticated() <--- is this needed?
.and()
.oauth2ResourceServer().jwt();
http.cors();
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
Okta.configureResourceServer401ResponseBody(http);
}
}
@Configuration
@Order(2)
@RequiredArgsConstructor
public static class ApiWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
private final CustomSecurityConfig customSecurityConfig; <--- JWT secret key in here
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/**")
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilter(new JwtFilter(authenticationManager(), customSecurityConfig))
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.cors();
}
}
}
还有以下JwtFilter
@Slf4j
public class JwtFilter extends BasicAuthenticationFilter {
private final CustomSecurityConfig customSecurityConfig;
public JwtFilter(AuthenticationManager authenticationManager, CustomSecurityConfig customSecurityConfig) {
super(authenticationManager);
this.customSecurityConfig = customSecurityConfig;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
try {
UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
if (authentication == null) {
chain.doFilter(request, response);
return;
}
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
} catch (Exception exception){
log.error("API authentication failed", exception);
SecurityContextHolder.clearContext();
}
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = new DefaultBearerTokenResolver().resolve(request);
if (token == null) {
return null;
}
Algorithm algorithm = Algorithm.HMAC256(customSecurityConfig.getSecret());
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer(CustomSecurityConfig.ISSUER)
.build();
DecodedJWT jwt = verifier.verify(token);
return new UsernamePasswordAuthenticationToken(
jwt.getClaim("user_name").asString(),
null,
jwt.getClaim("authorities")
.asList(String.class)
.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList()));
}
}
我的/api 调用全部返回 401,因为它们正在由BearerTokenAuthenticationFilter(由我的OktaWebSecurityConfigurerAdapter 使用)而不是我的JwtFilter 处理。自然,两个令牌之间的签名不匹配。我很困惑为什么我的 /api 调用甚至被该过滤器处理,因为我只为我的 Okta 处理程序应用 .oauth2ResourceServer().jwt(); 配置
我的日志如下所示:
SecurityContextHolder now cleared, as request processing completed
Checking match of request : '/api/entities'; against '/token/exchange'
Checking match of request : '/api/entities'; against '/api/**'
/api/entities at position 1 of 13 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
/api/entities at position 2 of 13 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
/api/entities at position 3 of 13 in additional filter chain; firing Filter: 'HeaderWriterFilter'
/api/entities at position 4 of 13 in additional filter chain; firing Filter: 'CorsFilter'
/api/entities at position 5 of 13 in additional filter chain; firing Filter: 'LogoutFilter'
Trying to match using Ant [pattern='/logout', GET]
Checking match of request : '/api/entities'; against '/logout'
Trying to match using Ant [pattern='/logout', POST]
Request 'GET /api/entities' doesn't match 'POST /logout'
Trying to match using Ant [pattern='/logout', PUT]
Request 'GET /api/entities' doesn't match 'PUT /logout'
Trying to match using Ant [pattern='/logout', DELETE]
Request 'GET /api/entities' doesn't match 'DELETE /logout'
No matches found
/api/entities at position 6 of 13 in additional filter chain; firing Filter: 'BearerTokenAuthenticationFilter'
Authentication attempt using org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider
No event was found for the exception org.springframework.security.oauth2.core.OAuth2AuthenticationException
Authentication request for failed: org.springframework.security.oauth2.core.OAuth2AuthenticationException: An error occurred while attempting to decode the Jwt: Signed JWT rejected: Another algorithm expected, or no matching key(s) found
Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@13691de5
SecurityContextHolder now cleared, as request processing completed
我整日整夜都在用这个敲打我的头......谢谢你的帮助!
【问题讨论】:
标签: spring-boot spring-security jwt spring-security-oauth2