Spring Boot Security OAuth2配置:从HttpOnly Cookie获取Access Token
Alright, let's get your Spring Boot resource server pulling JWT tokens from an HttpOnly Cookie instead of the Authorization header. I'll walk you through the key configurations using spring-security-oauth2 and spring-security-jwt, including custom token extraction, resource server setup, and a bonus on setting the cookie from your authorization server.
First, you need a custom TokenExtractor to pull the JWT from the request's cookies instead of the header. This component tells Spring Security where to look for the token.
import org.springframework.security.oauth2.provider.token.TokenExtractor; import org.springframework.stereotype.Component; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; @Component public class CookieTokenExtractor implements TokenExtractor { // Name of your HttpOnly cookie holding the JWT private static final String ACCESS_TOKEN_COOKIE = "ACCESS_TOKEN"; private static final String BEARER_PREFIX = "Bearer "; @Override public String extract(HttpServletRequest request) { Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if (ACCESS_TOKEN_COOKIE.equals(cookie.getName())) { String token = cookie.getValue(); // Add Bearer prefix if your decoder expects it (most do) if (!token.startsWith(BEARER_PREFIX)) { token = BEARER_PREFIX + token; } return token; } } } // Optional: Fall back to Authorization header if cookie isn't present return request.getHeader("Authorization"); } }
Next, update your resource server configuration to use the custom token extractor. This tells Spring Security to use your cookie-based logic instead of the default header extraction.
import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; import org.springframework.security.oauth2.provider.token.TokenExtractor; @Configuration @EnableResourceServer public class ResourceServerConfig extends ResourceServerConfigurerAdapter { private final TokenExtractor cookieTokenExtractor; // Inject the custom token extractor via constructor public ResourceServerConfig(TokenExtractor cookieTokenExtractor) { this.cookieTokenExtractor = cookieTokenExtractor; } @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { // Set the custom token extractor resources.tokenExtractor(cookieTokenExtractor); // Add your resource ID (if your authorization server uses them) resources.resourceId("your-resource-identifier"); } @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() // Configure CSRF protection: adjust based on your app's needs // If you're using a SPA with cross-domain requests, you may need to tweak this .csrf().ignoringAntMatchers("/api/**"); } }
Make sure your JWT decoder is set up to validate the tokens from the cookie. This depends on whether you're using symmetric or asymmetric signing.
Example with JWKS (Asymmetric Signing):
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; @Configuration public class JwtConfig { @Bean public JwtDecoder jwtDecoder() { // Replace with your authorization server's JWKS endpoint return NimbusJwtDecoder.withJwkSetUri("https://your-auth-server/.well-known/jwks.json").build(); } }
Example with Symmetric Key:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; @Configuration public class JwtConfig { @Bean public JwtDecoder jwtDecoder() { String secretKey = "your-strong-symmetric-secret-key"; SecretKey key = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); return NimbusJwtDecoder.withSecretKey(key).build(); } }
If you control the authorization server, here's how to set the JWT as an HttpOnly cookie when issuing tokens. This uses Spring Security's OAuth2 Authorization Server features.
import org.springframework.http.ResponseCookie; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext; import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletResponse; @Component public class CookieTokenCustomizer implements OAuth2TokenCustomizer<OAuth2TokenContext> { @Override public void customize(OAuth2TokenContext context) { if (OAuth2AccessToken.TOKEN_TYPE_VALUE.equals(context.getTokenType().getValue())) { OAuth2AccessToken accessToken = context.getToken(); HttpServletResponse response = context.get(HttpServletResponse.class); ResponseCookie accessTokenCookie = ResponseCookie.from("ACCESS_TOKEN", accessToken.getTokenValue()) .httpOnly(true) // Critical: Prevents XSS attacks by blocking JS access .secure(true) // Enable in production: Only send cookie over HTTPS .sameSite("Strict") // Mitigates CSRF; use "Lax" if needed for cross-site flows .path("/") // Adjust to your app's base path .maxAge(accessToken.getExpiresAt().getEpochSecond() - System.currentTimeMillis() / 1000) .build(); response.addHeader("Set-Cookie", accessTokenCookie.toString()); } } }
- CSRF Protection: When using HttpOnly cookies, ensure your CSRF configuration aligns with your app's architecture. For SPAs, you may need to expose the CSRF token to the frontend and include it in requests.
- CORS Configuration: If your frontend is on a different domain, enable credential support in CORS to allow cookies to be sent cross-domain:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import java.util.List; @Configuration public class CorsConfig { @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); config.setAllowedOrigins(List.of("https://your-frontend-domain.com")); config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE")); config.setAllowedHeaders(List.of("*")); config.setAllowCredentials(true); // Required for cookie-based auth UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return source; } } - Secure Cookie: Always set
secure(true)in production to ensure cookies are only transmitted over HTTPS.
内容的提问来源于stack exchange,提问作者ionutt93

