我正在使用 Spring Boot 2 遵循 auth0 的快速入门,但我的项目使用的是 Spring Boot 3,在更改已弃用的方法后,注销不再起作用。
安全配置:
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@EnableWebSecurity
public class SecurityConfig {
private final LogoutHandler logoutHandler;
public SecurityConfig(LogoutHandler logoutHandler) {
this.logoutHandler = logoutHandler;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
.requestMatchers("/images/**").permitAll()
.anyRequest().authenticated()
.and().oauth2Login()
.and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.addLogoutHandler(logoutHandler);
return http.build();
}
}
注销处理程序:
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import org.springframework.web.util.UriComponentsBuilder;
import java.io.IOException;
@Controller
public class LogoutHandler extends SecurityContextLogoutHandler {
private final Logger log = LoggerFactory.getLogger(this.getClass());
private final ClientRegistrationRepository clientRegistrationRepository;
@Autowired
public LogoutHandler(ClientRegistrationRepository clientRegistrationRepository) {
this.clientRegistrationRepository = clientRegistrationRepository;
}
@Override
public void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
Authentication authentication) {
super.logout(httpServletRequest, httpServletResponse, authentication);
String issuer = (String) getClientRegistration().getProviderDetails().getConfigurationMetadata().get("issuer");
String clientId = getClientRegistration().getClientId();
String returnTo = ServletUriComponentsBuilder.fromCurrentContextPath().build().toString();
String logoutUrl = UriComponentsBuilder
.fromHttpUrl(issuer + "v2/logout?client_id={clientId}&returnTo={returnTo}")
.encode()
.buildAndExpand(clientId, returnTo)
.toUriString();
log.info("Will attempt to redirect to logout URL: {}", logoutUrl);
try {
httpServletResponse.sendRedirect(logoutUrl);
} catch (IOException ioe) {
log.error("Error redirecting to logout URL", ioe);
}
}
private ClientRegistration getClientRegistration() {
return this.clientRegistrationRepository.findByRegistrationId("auth0");
}
}
当我注销时,我不会被重定向,并且显示: 但我仍然可以访问受保护的资源,如果我转到 /profile 端点,我可以看到我的个人资料,就像我登录一样。当我使用 Spring Boot 2 运行 QuickStart 时,不会发生这种情况,一切正常,我的所有配置( client-secret、client-id、issuer-uri、回调和注销 url 配置正确,并且与 Spring Boot 3 应用程序相同,我仔细检查过)。任何帮助将不胜感激,我找不到 Spring Boot 3 和 Auth0 的教程。
我能够使用 Spring Boot 3.0.4 来实现类似的应用程序。我正在使用最新的 okta-spring-boot 启动器,但我认为这并不重要。您可以尝试以下方法来处理注销吗?
@Configuration
@EnableMethodSecurity(securedEnabled = true)
public class SecurityConfiguration {
@Value("${spring.security.oauth2.client.provider.auth0.issuer-uri}")
private String issuer;
@Value("${spring.security.oauth2.client.registration.auth0.client-id}")
private String clientId;
LogoutHandler oidcLogoutHandler() {
return (request, response, authentication) -> {
try {
response.sendRedirect( issuer + "v2/logout?client_id=" + clientId + "&returnTo=http://localhost:8080/");
} catch (IOException e) {
throw new RuntimeException(e);
}
};
}
@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((requests) -> requests
// allow anonymous access to the root page
.requestMatchers("/").permitAll()
// authenticate all other requests
.anyRequest().authenticated());
// configure logout handler
http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).addLogoutHandler(oidcLogoutHandler());
// enable OAuth2/OIDC
http.oauth2Login();
// enable Resource Server for API access
http.oauth2ResourceServer().jwt();
return http.build();
}
}
当会话在依赖方(RP 或 OAuth2 客户端)上失效时,要使 OIDC 提供者(OP 或 OAuth2 授权服务器)上的会话也失效,您应该使用 RP 发起的注销。
Spring 有 注销成功处理程序:
OidcClientInitiatedLogoutSuccessHandler
(反应式应用程序的等效处理程序记录在那里)。
问题是,Auth0 开箱即用,在两点上不遵循 OIDC 标准(但如果您联系支持人员,也许会遵循 OIDC 标准):
end_session_endpoint
。注销端点是 {issuer-uri}v2/logout
(在我的例子中:https://dev-ch4mpy.eu.auth0.com/v2/logout
)。returnTo
而不是 post_logout_redirect_uri
,因为它应该是因此,除了像上面链接的文档中那样进行配置之外,您还必须编写自己的
ServerLogoutSuccessHandler
。您可能会从其他答案中获得灵感来编写此处理程序,只需记住更改实现的接口(或扩展SimpleUrlLogoutSuccessHandler
)。
我在spring-boot-starter-oauth2-client
周围写了薄薄的包装纸。它公开了这样一个注销成功处理程序,您可以从属性中进行配置:
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-addons-webmvc-client</artifactId>
<version>${project.version}</version>
</dependency>
@Configuration
@EnableMethodSecurity
public class WebSecurityConfig {
// Yes, that's all, logout is configured in YAML
// with the rest of the security filter-chain
}
api-host: ${scheme}://localhost:${server.port}
ui-host: ${api-host}
scheme: http
auth0-issuer: https://dev-ch4mpy.eu.auth0.com/
autho-secret: change-me
server:
port: 8080
ssl:
enabled: false
spring:
lifecycle:
timeout-per-shutdown-phase: 30s
security:
oauth2:
client:
provider:
auth0:
issuer-uri: ${auth0-issuer}
registration:
auth0-confidential-user:
authorization-grant-type: authorization_code
client-name: Auth0
client-id: TyY0H7xkRMRe6lDf9F8EiNqCo8PdhICy
client-secret: ${autho-secret}
provider: auth0
scope: openid,profile,email,offline_access
com:
c4-soft:
springaddons:
security:
issuers:
- location: ${auth0-issuer}
username-claim: $['https://c4-soft.com/spring-addons']['name']
authorities:
- path: $.roles
- path: $.permissions
client:
security-matchers:
- /**
permit-all:
- /login/**
- /oauth2/**
- /
- /ui/**
- /swagger-ui.html
- /swagger-ui/**
client-uri: ${ui-host}
post-login-redirect-path: /ui/greet
post-logout-redirect-path: /ui/greet
back-channel-logout-enabled: true
oauth2-logout:
- client-registration-id: auth0-confidential-user
uri: ${auth0-issuer}v2/logout
client-id-request-param: client_id
post-logout-uri-request-param: returnTo
---
scheme: https
server:
ssl:
enabled: true
spring:
config:
activate:
on-profile: ssl
您可以使用 lambda 表达式:
.logout((logout)
-> logout.logoutUrl("/api/v1/auth/logout")
.addLogoutHandler(logoutHandler)
.logoutSuccessHandler((request, response, authentication) -> SecurityContextHolder.clearContext()))
.build();