Backend Development 7 min read

Secure Spring Boot 2 with OAuth2 Resource Server and Redis

This guide demonstrates how to integrate Spring Boot 2 with OAuth2 for unified authentication, configure a Redis-backed token store, implement a resource server with custom security settings, and test protected endpoints, providing complete code snippets and configuration details.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Secure Spring Boot 2 with OAuth2 Resource Server and Redis

Continuing from the previous article on integrating SpringBoot2 with OAuth2 for unified authentication, this tutorial shows how to use the issued token to protect resources.

Environment

Spring Boot 2.4.12, OAuth2, Redis

pom.xml Dependencies

<code>&lt;dependency&gt;
  &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
  &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
  &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
  &lt;artifactId&gt;spring-boot-starter-data-redis&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
  &lt;groupId&gt;org.apache.commons&lt;/groupId&gt;
  &lt;artifactId&gt;commons-pool2&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
  &lt;groupId&gt;org.springframework.security.oauth.boot&lt;/groupId&gt;
  &lt;artifactId&gt;spring-security-oauth2-autoconfigure&lt;/artifactId&gt;
  &lt;version&gt;2.2.11.RELEASE&lt;/version&gt;
&lt;/dependency&gt;</code>

application.yml

<code>server:
  port: 8088
---
spring:
  application:
    name: oauth-resource
---
spring:
  redis:
    host: localhost
    port: 6379
    password: 
    database: 1
    lettuce:
      pool:
        maxActive: 8
        maxIdle: 100
        minIdle: 10
        maxWait: -1</code>

Domain Object

<code>public class Users implements UserDetails, Serializable {
    private static final long serialVersionUID = 1L;
    private String id;
    private String username;
    private String password;
}</code>

Core Configuration Class

<code>@Configuration
@EnableResourceServer
public class OAuthConfig extends ResourceServerConfigurerAdapter {
    private static final Logger logger = LoggerFactory.getLogger(OAuthConfig.class);
    public static final String RESOURCE_ID = "gx_resource_id";

    @Resource
    private RedisConnectionFactory redisConnectionFactory;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId(RESOURCE_ID);
        OAuth2AuthenticationEntryPoint entryPoint = new OAuth2AuthenticationEntryPoint();
        entryPoint.setExceptionTranslator(webResponseExceptionTranslator());
        resources.authenticationEntryPoint(entryPoint);
        resources.tokenExtractor(request -> {
            String token = extractToken(request);
            if (token != null) {
                return new PreAuthenticatedAuthenticationToken(token, "");
            }
            return null;
        });
    }

    private String extractToken(HttpServletRequest request) {
        // first check the header... Authorization: Bearer xxx
        String token = extractHeaderToken(request);
        // second check the header... access_token: xxx
        if (token == null) {
            token = request.getHeader("access_token");
        }
        if (token == null) {
            logger.debug("Token not found in headers. Trying request parameters.");
            token = request.getParameter(OAuth2AccessToken.ACCESS_TOKEN);
            if (token == null) {
                logger.debug("Token not found in request parameters. Not an OAuth2 request.");
            } else {
                request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, OAuth2AccessToken.BEARER_TYPE);
            }
        }
        return token;
    }

    private String extractHeaderToken(HttpServletRequest request) {
        Enumeration<String> headers = request.getHeaders("Authorization");
        while (headers.hasMoreElements()) {
            String value = headers.nextElement();
            if (value.toLowerCase().startsWith(OAuth2AccessToken.BEARER_TYPE.toLowerCase())) {
                String authHeaderValue = value.substring(OAuth2AccessToken.BEARER_TYPE.length()).trim();
                request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE,
                    value.substring(0, OAuth2AccessToken.BEARER_TYPE.length()).trim());
                int commaIndex = authHeaderValue.indexOf(',');
                if (commaIndex > 0) {
                    authHeaderValue = authHeaderValue.substring(0, commaIndex);
                }
                return authHeaderValue;
            }
        }
        return null;
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http.requestMatcher(request -> {
            String path = request.getServletPath();
            if (path != null && path.startsWith("/demo")) {
                return true;
            }
            return false;
        }).authorizeRequests().anyRequest().authenticated();
    }

    @Bean
    public TokenStore tokenStore() {
        TokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);
        return tokenStore;
    }

    @Bean
    public WebResponseExceptionTranslator<?> webResponseExceptionTranslator() {
        return new DefaultWebResponseExceptionTranslator() {
            @SuppressWarnings({"unchecked", "rawtypes"})
            @Override
            public ResponseEntity translate(Exception e) throws Exception {
                ResponseEntity<OAuth2Exception> responseEntity = super.translate(e);
                return exceptionProcess(responseEntity);
            }
        };
    }

    private static ResponseEntity<Map<String, Object>> exceptionProcess(ResponseEntity<OAuth2Exception> responseEntity) {
        Map<String, Object> body = new HashMap<>();
        body.put("code", -1);
        OAuth2Exception ex = responseEntity.getBody();
        String errorMessage = ex.getMessage();
        if (errorMessage != null) {
            errorMessage = "认证失败,非法用户";
            body.put("message", errorMessage);
        } else {
            String error = ex.getOAuth2ErrorCode();
            if (error != null) {
                body.put("message", error);
            } else {
                body.put("message", "认证服务异常,未知错误");
            }
        }
        body.put("data", null);
        return new ResponseEntity<>(body, responseEntity.getHeaders(), responseEntity.getStatusCode());
    }
}
</code>

Test Controller

<code>@RestController
@RequestMapping("/demo")
public class DemoController {
    @GetMapping("/res")
    public Object res() {
        return "success";
    }
}</code>

Testing Steps

First, try accessing the resource with no token or an invalid token.

Then obtain a valid token.

Successful response indicates the resource is protected correctly.

End of tutorial.

JavaRedisSpring BootsecurityOAuth2Resource Server
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.