我还没有找到一个合适的解决方案。我使用Spring Security Oauth2资源服务器来验证我的请求。这很好。但是当测试不同的场景时,发现如果没有Authorization, spring security返回403而不是401。报头存在,或者如果有授权报头存在,但值不是以承载.
Spring Boot Starter - 2.6.7
Spring Boot Starter Security - 2.6.7
Spring Security Config & Web - 5.6.3
Spring Security Core - 5.3.19
Spring Boot Starter OAuth2 Resource Server - 2.6.7
Spring OAuth2 Resource Server - 5.6.3
我指的是这个答案,并在下面添加了BearerTokenAuthenticationEntryPoint的代码。不同之处在于我用的是自省url而不是jwt。但这没有帮助,这部分也没有执行。如果存在承载令牌,则只执行它。
我在这里错过了什么?
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class CustomResourceServerSecurityConfiguration {
@Value("${spring.security.oauth2.resourceserver.opaque-token.introspection-uri}")
String introspectionUri;
@Value("${spring.security.oauth2.resourceserver.opaque-token.client-id}")
String clientId;
@Value("${spring.security.oauth2.resourceserver.opaque-token.client-secret}")
String clientSecret;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2
.opaqueToken(opaque -> opaque.introspectionUri(this.introspectionUri)
.introspectionClientCredentials(this.clientId, this.clientSecret))
.authenticationEntryPoint((request, response, exception) -> {
System.out.println("Authentication failed");
BearerTokenAuthenticationEntryPoint delegate = new BearerTokenAuthenticationEntryPoint();
delegate.commence(request, response, exception);
}))
.exceptionHandling(
(exceptions) -> exceptions.authenticationEntryPoint((request, response, exception) -> {
System.out.println("Authentication is required");
BearerTokenAuthenticationEntryPoint delegate = new BearerTokenAuthenticationEntryPoint();
delegate.commence(request, response, exception);
}));
return http.build();
}
}
如果您的场景是您获得403的POST和401的get,如果不存在承载令牌,它与csrf有关。
44.2.14我得到一个403禁止当执行POST
如果HTTP POST返回HTTP 403 Forbidden,但HTTP GET工作,那么这个问题很可能与CSRF有关。要么提供CSRF令牌,要么禁用CSRF保护(不推荐)。
这是来源
如果你使用JWT令牌,那么它没有任何额外的要求,那么你可以禁用它。
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated())
.csrf().disable()
return http.build();
}
4。无状态Spring API
如果我们的无状态API使用基于令牌的身份验证,比如JWT,我们不需要CSRF保护,我们必须像前面看到的那样禁用它
来源使用默认配置,当授权头丢失或无效(格式错误,过期,错误的颁发者,…)时,您应该有一个302(重定向到登录)。如果您有403,那么您将面临另一个例外(CSRF, CORS或其他)。设置logging.level.org.sprngframework.security=DEBUG
,仔细检查日志
要更改此默认行为(401而不是302),请按照教程中所做的操作:
http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm="Restricted Content"");
response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
});
在示例中,如servlet和令牌自省的示例,它符合您的确切用例,您甚至可以找到断言http状态是您期望的单元测试:未授权时为401,拒绝时为403:
@Test
// security-context not set for this test => anonymous
void greetWitoutAuthentication() throws Exception {
api.get("/greet").andExpect(status().isUnauthorized());
}
@Test
@WithMockBearerTokenAuthentication(authorities = "ROLE_AUTHORIZED_PERSONNEL", attributes = @OpenIdClaims(sub = "Ch4mpy"))
void securedRouteWithAuthorizedPersonnelIsOk() throws Exception {
api.get("/secured-route").andExpect(status().isOk());
}
@Test
@WithMockBearerTokenAuthentication(authorities = "NOT_A_REQUIRED_ROLE")
void securedMethodWithoutAuthorizedPersonnelIsForbidden() throws Exception {
api.get("/secured-method").andExpect(status().isForbidden());
}
当然,这些测试通过了…
我也有同样的问题,我只是不明白为什么安全团队在默认的AuthenticationEntryPoint中返回403,当请求头中没有令牌时。
我的解决方案是自定义AuthenticationEntryPoint:
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
private static final Log logger = LogFactory.getLog(CustomAuthenticationEntryPoint.class);
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
logger.debug("Pre-authenticated entry point called. Rejecting access");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access Denied");
}
}