Spring 引导安全性 - 注销重定向问题



应用程序的目的

所以我正在开发一个非常基本的应用程序,使用 Kotlin 语言的 Spring Boot,我想集成一个基于Keycloak身份验证管理服务的身份验证系统。

我的应用程序现在包含一个许多页面,当您想首次访问您的个人资料时,该页面要求您通过将您发送到Keycloak登录表单来登录。然后,在身份验证成功后,Keycloak 会自动管理对受保护内容的所有请求。

最后,当您在个人资料中时,您可以选择注销,一旦您这样做,您将被重定向到一个注销页面,该页面宣布您成功注销并返回主页。

我的应用程序配置

首先,以下是我设置应用程序的方法。

这是我的控制器:

@Controller
class MainController {
@GetMapping("/profile")
fun getProfile() = "profile"
@GetMapping("/logout")
@Throws(Exception::class)
fun logout(request: HttpServletRequest): String? {
request.logout()
return "logout"
}
}

这是我的安全配置:

@Configuration
@EnableWebSecurity
class SecurityConfig(keycloakLogoutHandler: KeycloakLogoutHandler?) {
private final val keycloakLogoutHandler: KeycloakLogoutHandler = keycloakLogoutHandler!!
@Bean
@Throws(Exception::class)
fun filterChain(http: HttpSecurity): SecurityFilterChain {
http
.authorizeHttpRequests()
.requestMatchers("/profile").authenticated()
.anyRequest().permitAll()
.and()
.oauth2Login()
.and()
.logout()
.addLogoutHandler(keycloakLogoutHandler)
.logoutSuccessUrl("/logout")
.and()
.csrf().disable()
return http.build()
}
}

这是我自定义的Keycloak配置的注销处理程序:

@Component
class KeycloakLogoutHandler : LogoutHandler {
var logger: Logger = LoggerFactory.getLogger(KeycloakLogoutHandler::class.java)
private final val restTemplate: RestTemplate = RestTemplate()
override fun logout(
request: HttpServletRequest?,
response: HttpServletResponse?,
auth: Authentication?
) {
val user = auth?.principal as OidcUser
val endSessionEndpoint = user.issuer.toString() + "/protocol/openid-connect/logout"
val builder = UriComponentsBuilder
.fromUriString(endSessionEndpoint)
.queryParam("id_token_hint", user.idToken?.tokenValue)
val logoutResponse = restTemplate.getForEntity(
builder.toUriString(), String::class.java
)
if (logoutResponse.statusCode.is2xxSuccessful) {
logger.info("Successfully logged out from Keycloak")
} else {
logger.error("Could not propagate logout to Keycloak")
}
}
}

这是我的应用程序.yml :

server:
port: 8081
keycloak:
realm: http://localhost:8082/realms/StroffosRealm
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: ${keycloak.realm}
client:
registration:
keycloak:
client-id: authenticator-app
authorization-grant-type: authorization_code
scope: openid
provider:
keycloak:
issuer-uri: ${keycloak.realm}
user-name-attribute: preferred_username

问题所在

该应用程序在大多数情况下都能完美运行,直到您尝试在此配置文件页面中注销:

<html lang="en">
<head>
<meta charset="UTF-8">
<title>Your Profile</title>
</head>
<body>
<h1>Hello, User!</h1>
<p>This is your profile</p>
<a href="/logout"><button>Logout</button></a>
</body>
</html>

实际的注销请求从正确注销当前会话的KeycloakLogoutHandler正确发送,并且在我再次重新登录之前不允许我访问配置文件页面。

但是我要显示的注销确认页面显示内部服务器错误 500,因为KeycloakLogoutHandlerlogout()函数中的auth参数给了我错误:

java.lang.NullPointerException: null cannot be cast to non-null type org.springframework.security.oauth2.core.oidc.user.OidcUser

但即使我试图通过auth简单地通过放置

if (auth == null) return

控制作为logout()函数的第一条指令,但每当 I 或 T注销按钮尝试重定向到注销 HTML 页面时,结果是重定向过多错误

我认为是主要问题

每当我单击Logout按钮时,都会将我发送到localhost:8081/logoutURL,并且@Controller中的映射首次执行request.logout()指令,使我的当前会话无效并清空身份验证信息。 一旦这样做,它应该返回注销HTML页面,但它没有,因为一旦注销成功,就像它在SecurityConfig中编写的那样,重定向URL也会被localost:8081/logout,因此它将第二次执行request.logout()指令,但这次找到一个空的auth参数。

我尝试过什么

SecurityConfig中删除.logoutSuccessUrl("/logout")是我的第一个想法,但是一旦执行注销,它就会将我重定向到默认的Spring安全注销页面

我显然不希望它发生。

更新

我已经清理了我的@Controller和我的安全配置:

@Controller
class MainController {
@GetMapping("/profile")
fun getProfile() = "profile"
@GetMapping("/logout")
fun logoutPage() = "logout"
}
@Configuration
@EnableWebSecurity
class SecurityConfig(keycloakLogoutHandler: KeycloakLogoutHandler?) {
private final val keycloakLogoutHandler: KeycloakLogoutHandler = keycloakLogoutHandler!!
@Bean
@Throws(Exception::class)
fun filterChain(http: HttpSecurity): SecurityFilterChain {
http.authorizeHttpRequests()
.requestMatchers("/profile").authenticated()
.anyRequest().permitAll()
.and()
.oauth2Login()
.and()
.logout()
.logoutRequestMatcher(AntPathRequestMatcher("/logoutRequest"))
.addLogoutHandler(keycloakLogoutHandler)
.logoutSuccessUrl("/logout")
.and()
.oauth2ResourceServer(OAuth2ResourceServerConfigurer<HttpSecurity>::jwt)
http.csrf().disable()
return http.build()
}
}

现在,每当在/logoutRequestURL发送请求时,它都会被威胁为注销请求,并会正确地将我重定向到注销页面。

不再发生ERR_TOO_MANY_REDIRECTS,但现在它显示的东西而不是我的自定义注销确认页面是 Spring 安全性提供的默认页面,我不想要

不确定,但尝试在.logout() 之后的链中添加 .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))

编辑:

引用文档:

使用 HttpServletRequest.logout() 选项时,适配器执行 针对 Keycloak 服务器的反向通道 POST 呼叫通过 刷新令牌。如果该方法从未受保护的页面 (a 不检查有效令牌的页面)刷新令牌可以是 不可用,在这种情况下,适配器将跳过调用。为此 原因,使用受保护的页面执行 HttpServletRequest.logout() 建议始终考虑当前令牌 如果需要,可以执行与Keycloak服务器的交互。

似乎您必须将控制器的映射更改为 POST,并使 HTML 页面中的按钮看起来有点

<form th:action="@{/logout}" method="post">
<input type="submit" th:value="LogOut" class="btn btn-warning"/>
* 在这个例子中使用了 Bootstrap CSS 和 Thymeleaf

您可以尝试将处理注销逻辑的端点和显示成功注销的端点分开。

例如,您可以有一个/logout-success端点,该端点返回所需的 html,告诉用户注销成功,并在一段时间后将用户重定向到主页。

您需要将.logoutSuccessUrl("/logout-success")配置为在成功注销后重定向用户。

相关内容

  • 没有找到相关文章

最新更新