You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Keycloak Spring适配器:每次HTTP请求校验AuthToken有效性需求

Got it, let's get this token validation and redirection working in your Grails 3.2.9 + Keycloak 3.4.3 setup. Since you already started with a TokenAuthenticationFilter, here's how to complete and integrate it properly:

Step 1: Add Keycloak Spring Security Dependencies

First, ensure your build.gradle includes the necessary Keycloak adapters to handle JWT validation:

dependencies {
    // Existing dependencies...
    compile 'org.keycloak:keycloak-spring-security-adapter:3.4.3'
    compile 'org.keycloak:keycloak-core:3.4.3'
}

Step 2: Complete the TokenAuthenticationFilter

Extend Spring's OncePerRequestFilter to extract, validate tokens, and handle redirection. This filter checks for a Bearer token in the Authorization header, validates it against Keycloak, and redirects to login if the token is missing or invalid:

import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.keycloak.common.VerificationException;
import org.keycloak.representations.AccessToken;
import org.keycloak.adapters.springsecurity.token.KeycloakTokenVerifier;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class TokenAuthenticationFilter extends OncePerRequestFilter {

    private static final String AUTHORIZATION_HEADER = "Authorization";
    private static final String BEARER_PREFIX = "Bearer ";
    private final String keycloakAuthServerUrl;
    private final String realm;
    private final String clientId;

    // Inject Keycloak config values via constructor
    public TokenAuthenticationFilter(String keycloakAuthServerUrl, String realm, String clientId) {
        this.keycloakAuthServerUrl = keycloakAuthServerUrl;
        this.realm = realm;
        this.clientId = clientId;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token = extractTokenFromRequest(request);

        if (token != null) {
            try {
                // Validate token using Keycloak's built-in verifier
                AccessToken accessToken = KeycloakTokenVerifier.verifyToken(token, keycloakAuthServerUrl, realm, clientId);
                
                // Set authenticated user in security context
                Authentication auth = new KeycloakAuthenticationToken(accessToken, false);
                SecurityContextHolder.getContext().setAuthentication(auth);
            } catch (VerificationException e) {
                // Invalid token - redirect to login
                response.sendRedirect("/login");
                return;
            }
        } else {
            // No token present - redirect to login
            response.sendRedirect("/login");
            return;
        }

        filterChain.doFilter(request, response);
    }

    private String extractTokenFromRequest(HttpServletRequest request) {
        String authHeader = request.getHeader(AUTHORIZATION_HEADER);
        if (authHeader != null && authHeader.startsWith(BEARER_PREFIX)) {
            return authHeader.substring(BEARER_PREFIX.length());
        }
        return null;
    }
}

Step 3: Register the Filter in Grails Security Configuration

Integrate the filter into Grails' Spring Security setup. Choose one of these options based on your config style:

Option 1: Java Configuration Class

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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // Add our token filter before the default username/password filter
            .addFilterBefore(authenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class)
            // Define access rules (adjust as needed)
            .authorizeRequests()
                .anyRequest().authenticated();
    }

    @Bean
    public TokenAuthenticationFilter authenticationTokenFilter() {
        // Replace with your actual Keycloak server and client values
        return new TokenAuthenticationFilter(
            "http://your-keycloak-server/auth",
            "your-realm-name",
            "your-client-id"
        );
    }
}

Option 2: Groovy resources.groovy

If you use Groovy-based bean configuration:

import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter

beans = {
    tokenAuthenticationFilter(TokenAuthenticationFilter, 
        "http://your-keycloak-server/auth",
        "your-realm-name",
        "your-client-id"
    )

    springSecurityFilterChain(org.springframework.security.web.FilterChainProxy) {
        filterChains = [
            new org.springframework.security.web.DefaultSecurityFilterChain(
                new AntPathRequestMatcher("/**"),
                [ref('tokenAuthenticationFilter'), ref('usernamePasswordAuthenticationFilter')] // Add other required filters here
            )
        ]
    }
}

Step 4: Fine-Tune Edge Cases

  • Public Endpoints: To skip validation for certain paths (e.g., /public/**), add a check at the start of doFilterInternal:
    if (new AntPathRequestMatcher("/public/**").matches(request)) {
        filterChain.doFilter(request, response);
        return;
    }
    
  • Direct Keycloak Login Redirect: Instead of your app's login page, redirect users directly to Keycloak's login form using this URL pattern:
    String keycloakLoginUrl = String.format(
        "%s/realms/%s/protocol/openid-connect/auth?client_id=%s&redirect_uri=%s&response_type=code",
        keycloakAuthServerUrl, realm, clientId, "http://your-app-url/callback"
    );
    response.sendRedirect(keycloakLoginUrl);
    
  • Token Expiry: Keycloak's KeycloakTokenVerifier automatically checks token expiration, so expired tokens will trigger the redirect.

内容的提问来源于stack exchange,提问作者JoeyHolloway

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:44:29