[英]Customize keycloak error page with spring boot
我使用keycloak-spring-boot-starter
來保護我的休息服務免受未經授權的訪問。 身份驗證按預期工作,但如果身份驗證失敗,則返回空響應。
但是,我想返回類似於我所有其他錯誤處理程序的 json 錯誤響應。 我已經嘗試定義@ExceptionHandler(Throwable.class)
、 ErrorController
、 ErrorViewResolver
或通過WebServerCustomizer
配置ErrorPage
,但這根本不起作用。
我很好,如果我可以為它定義一個 static 響應。
似乎有一個名為delegateBearerErrorResponseSending
的屬性,但我找不到在哪里設置它。 它不存在於 spring-boot 的屬性中。 我什至不確定電話將被委派到哪里。
有一個名為policy-enforcer-config.on-deny-redirect-to
的屬性,但重定向不是 rest 服務的預期行為。
TLDR:如何配置/自定義 keycloak 的錯誤頁面。
我在當前的 spring-security-web (5.4+) 版本中找到了一種在一定程度上做到這一點的方法。
/**
* A {@link RequestRejectedHandler} for spring security web's application firewall.
*/
@Component
public class FirewallRequestRejectedHandler implements RequestRejectedHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(FirewallRequestRejectedHandler.class);
@Override
public void handle(
final HttpServletRequest request,
final HttpServletResponse response,
final RequestRejectedException requestRejectedException) throws IOException {
// Optionally write a warning to the logs
LOGGER.warn("Application firewall: {}", requestRejectedException.getMessage(),
LOGGER.isDebugEnabled() ? requestRejectedException : null);
// Make the exception accessible to the ErrorController
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, requestRejectedException );
// Call error controller
response.sendError(401, "Access denied");
}
}
這將調用包含我的后備錯誤處理邏輯的ErrorController
,從而發送正確的響應。
朋友你好!覆蓋 class 方法challengeResponse()
得到解決
org.keycloak.adapters.BearerTokenRequestAuthenticator.challengeResponse( HttpFacade facade, final OIDCAuthenticationError.Reason reason, final String error, final String description )
這就是我增強keycloak-json-response-spring-boot-starter
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-json-response-spring-boot-starter</artifactId>
<version>18.0.0</version>
<name>keycloak-json-response-spring-boot-starter</name>
<description>keycloak-json-response-spring-boot-starter</description>
<!-- Omit some configuration ... -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.keycloak.bom</groupId>
<artifactId>keycloak-adapter-bom</artifactId>
<version>${keycloak.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<!-- Omit some configuration ... -->
創建相同的 package 和 class 然后在方法中嵌入代碼
package org.keycloak.adapters;
@SuppressWarnings( "unused" )
public class BearerTokenRequestAuthenticator {
protected AuthChallenge challengeResponse( HttpFacade facade, final OIDCAuthenticationError.Reason reason, final String error, final String description ) {
StringBuilder header = new StringBuilder( "Bearer realm=\"" );
header.append( deployment.getRealm( ) ).append( "\"" );
// addon begin
Map< String, String > responseBody = new LinkedHashMap<>( );
HttpFacade.Response response = facade.getResponse( );
// addon end
// addon begin
if ( reason == OIDCAuthenticationError.Reason.NO_BEARER_TOKEN ) {
response.setStatus( 401 );
responseBody.put( "error", "unauthorized" );
responseBody.put( "error_description", "Not Token! To login." );
}
// addon end
if ( error != null ) {
header.append( ", error=\"" ).append( error ).append( "\"" );
// addon begin
responseBody.put( "error", error );
response.setStatus( 403 );
// addon end
}
if ( description != null ) {
header.append( ", error_description=\"" ).append( description ).append( "\"" );
// addon begin
responseBody.put( "error_description", description );
response.setStatus( 403 );
// addon end
}
final String challenge = header.toString( );
// addon begin
try {
if ( !responseBody.isEmpty( ) ) {
response.setHeader( "Content-Type", "application/json" );
OutputStream responseOutputStream = response.getOutputStream( );
responseOutputStream.write( new ObjectMapper( ).writeValueAsBytes( responseBody ) );
responseOutputStream.flush( );
}
} catch ( IOException e ) {
e.printStackTrace( );
}
// addon end
return new AuthChallenge( ) {
@Override
public int getResponseCode( ) {
return 401;
}
@Override
public boolean challenge( HttpFacade facade ) {
if ( deployment.getPolicyEnforcer( ) != null ) {
deployment.getPolicyEnforcer( ).enforce( ( OIDCHttpFacade ) facade );
return true;
}
OIDCAuthenticationError error = new OIDCAuthenticationError( reason, description );
facade.getRequest( ).setError( error );
facade.getResponse( ).addHeader( "WWW-Authenticate", challenge );
if ( deployment.isDelegateBearerErrorResponseSending( ) ) {
facade.getResponse( ).setStatus( 401 );
} else {
facade.getResponse( ).sendError( 401 );
}
return true;
}
};
}
}
不要忘記復制 class 中代碼的 rest 並將您的keycloak-spring-boot-starter
替換為keycloak-json-response-spring-boot-starter
我不知道最近的 keycloak/spring-boot 版本是否發生了變化,但現在我必須在org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter
中配置以下內容:
@Override
protected KeycloakAuthenticationProcessingFilter keycloakAuthenticationProcessingFilter() throws Exception {
final KeycloakAuthenticationProcessingFilter filter = super.keycloakAuthenticationProcessingFilter();
// Don't run for error handling
filter.setRequiresAuthenticationRequestMatcher(new AndRequestMatcher(
KeycloakAuthenticationProcessingFilter.DEFAULT_REQUEST_MATCHER,
new NegatedRequestMatcher(new AntPathRequestMatcher(ERROR_HANDLING_PATH))));
return filter;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.